{"id":13603622,"url":"https://github.com/HEnquist/rubato","last_synced_at":"2025-04-11T22:31:45.836Z","repository":{"id":38191538,"uuid":"256027773","full_name":"HEnquist/rubato","owner":"HEnquist","description":"An asyncronous resampling library written in Rust","archived":false,"fork":false,"pushed_at":"2024-10-21T20:16:31.000Z","size":651,"stargazers_count":186,"open_issues_count":6,"forks_count":21,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-10-22T14:01:00.121Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HEnquist.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-15T20:22:19.000Z","updated_at":"2024-10-22T10:33:50.000Z","dependencies_parsed_at":"2023-01-27T13:01:28.760Z","dependency_job_id":"3995139b-5615-4e53-b2aa-e2d36fb53057","html_url":"https://github.com/HEnquist/rubato","commit_stats":{"total_commits":232,"total_committers":8,"mean_commits":29.0,"dds":"0.22844827586206895","last_synced_commit":"f3694e7db39a241ea44872cd1f6712e8d668f235"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HEnquist%2Frubato","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HEnquist%2Frubato/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HEnquist%2Frubato/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HEnquist%2Frubato/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HEnquist","download_url":"https://codeload.github.com/HEnquist/rubato/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248489699,"owners_count":21112621,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T19:00:27.467Z","updated_at":"2025-04-11T22:31:45.824Z","avatar_url":"https://github.com/HEnquist.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# Rubato\n\nAn audio sample rate conversion library for Rust.\n\nThis library provides resamplers to process audio in chunks.\n\nThe ratio between input and output sample rates is completely free.\nImplementations are available that accept a fixed length input\nwhile returning a variable length output, and vice versa.\n\nRubato can be used in realtime applications without any allocation during\nprocessing by preallocating a [Resampler] and using its\n[input_buffer_allocate](Resampler::input_buffer_allocate) and\n[output_buffer_allocate](Resampler::output_buffer_allocate) methods before\nbeginning processing. The [log feature](#log-enable-logging) feature should be disabled\nfor realtime use (it is disabled by default).\n\n## Input and output data format\n\nInput and output data are stored in a non-interleaved format.\n\nInput and output data are stored as slices of references, `\u0026[AsRef\u003c[f32]\u003e]` or `\u0026[AsRef\u003c[f64]\u003e]`.\nThe inner references (`AsRef\u003c[f32]\u003e` or `AsRef\u003c[f64]\u003e`) hold the sample values for one channel each.\n\nSince normal vectors implement the `AsRef` trait,\n`Vec\u003cVec\u003cf32\u003e\u003e` and `Vec\u003cVec\u003cf64\u003e\u003e` can be used for both input and output.\n\n## Asynchronous resampling\n\nThe asynchronous resamplers are available with and without anti-aliasing filters.\n\nResampling with anti-aliasing is based on band-limited interpolation using sinc\ninterpolation filters. The sinc interpolation upsamples by an adjustable factor,\nand then the new sample points are calculated by interpolating between these points.\nThe resampling ratio can be updated at any time.\n\nResampling without anti-aliasing omits the cpu-heavy sinc interpolation.\nThis runs much faster but produces a lower quality result.\n\n## Synchronous resampling\n\nSynchronous resampling is implemented via FFT. The data is FFT:ed, the spectrum modified,\nand then inverse FFT:ed to get the resampled data.\nThis type of resampler is considerably faster but doesn't support changing the resampling ratio.\n\n## Usage\nThe resamplers provided by this library are intended to process audio in chunks.\nThe optimal chunk size is determined by the application,\nbut will likely end up somwhere between a few hundred to a few thousand frames.\nThis gives a good compromize between efficiency and memory usage.\n\n### Real time considerations\nRubato is suitable for real-time applications when using the `Resampler::process_into_buffer()` method.\nThis stores the output in a pre-allocated output buffer, and performs no allocations or other\noperations that may block the thread.\n\n### Resampling a given audio clip\nA suggested simple process for resampling an audio clip of known length to a new sample rate is as follows.\nHere it is assumed that the source data is stored in a vec,\nor some other structure that supports reading arbitrary number of frames at a time.\nFor simplicity, the output is stored in a temporary buffer during resampling,\nand copied to the destination afterwards.\n\nPreparations:\n1. Create a resampler of suitable type, for example FFTFixedIn which is quite fast and gives good quality.\n   Since neither input or output has any restrictions for the number of frames that can be read or written at a time,\n   the chunk size can be chosen arbitrarily. Start with a chunk size of for example 1024.\n2. Create an input buffer.\n3. Create a temporary buffer for collecting the resampled output data.\n4. Call `Resampler::output_delay()` to know how many frames of delay the resampler gives.\n   Store the number as `delay`.\n5. Calculate the new clip length as `new_length = original_length * new_rate / original_rate`.\n\nNow it's time to process the bulk of the clip by repeated procesing calls. Loop:\n1. Call `Resampler::input_frames_next()` to learn how many frames the resampler needs.\n2. Check the number of available frames in the source. If it is less than the needed input size, break the loop.\n3. Read the required number of frames from the source, convert the sample values to float, and copy them to the input buffer.\n4. Call `Resampler::process()` or `Resampler::process_into_buffer()`.\n5. Append the output frames to the temporary output buffer.\n\nThe next step is to process the last remaining frames.\n1. Read the available frames fom the source, convert the sample values to float, and copy them to the input buffer.\n2. Call `Resampler::process_partial()` or `Resampler::process_partial_into_buffer()`.\n3. Append the output frames to the temporary buffer.\n\nAt this point, all frames have been sent to the resampler,\nbut because of the delay through the resampler,\nit may still have some frames in its internal buffers.\nWhen all wanted frames have been generated, the length of the temporary\noutput buffer should be at least `new_length + delay`.\nIf this is not the case, call `Resampler::process_partial()`\nor `Resampler::process_partial_into_buffer()` with `None` as input,\nand append the output to the temporary output buffer.\nIf needed, repeat until the length is sufficient.\n\nFinally, copy the data from the temporary output buffer to the desired destination.\nSkip the first `delay` frames, and copy `new_length` frames.\n\nIf there is more than one clip to resample from and to the same sample rates,\nthe same resampler should be reused.\nCreating a new resampler is an expensive task and should be avoided if possible.\nStart the procedire from the start, but instead of creating a new resampler,\ncall `Resampler::reset()` on the existing one to prepare it for a new job.\n\n### Resampling a stream\nWhen resamping a stream, the process is normally performed in real time,\nand either the input of output is some API that provides or consumes frames at a given rate.\n\n#### Example, record to file from an audio API\nAudio APIs such as [CoreAudio](https://crates.io/crates/coreaudio-rs) on MacOS,\nor the cross platform [cpal](https://crates.io/crates/cpal) crate,\noften use callback functions for data exchange.\n\nA complete\n\nWhen capturing audio from these, the application passes a function to the audio API.\nThe API then calls this function periodically, with a pointer to a data buffer containing new audio frames.\nThe data buffer size is usually the same on every call, but that varies between APIs.\nIt is important that the function does not block,\nsince this would block some internal loop of the API and cause loss of some audio data.\nIt is recommended to keep the callback function light.\nIdeally it should read the provided audio data from the buffer provided by the API,\nand optionally perform some light processing such as sample format conversion.\nNo heavy processing such as resampling should be performed here.\nIt should then store the audio data to a shared buffer.\nThe buffer may be a `Arc\u003cMutex\u003cVecDeque\u003cT\u003e\u003e\u003e`,\nor something more advanced such as [ringbuf](https://crates.io/crates/ringbuf).\n\nA separate loop, running either in the main or a separate thread,\nshould then read from that buffer, resample, and save to file.\nIf the Audio API provides a fixed buffer size,\nthen this number of frames is a good choice for the resampler chunk size.\nIf the size varies, the shared buffer can be used to adapt the chunk sizes of the audio API and the resampler.\nA good starting point for the resampler chunk size is to use an \"easy\" value\nnear the average chunk size of the audio API.\nMake sure that the shared buffer is large enough to not get full\nin case for the loop gets blocked waiting for example for disk access.\n\nThe loop should follow a process similar to [resampling a clip](#resampling-a-given-audio-clip),\nbut the input is now the shared buffer.\nThe loop needs to wait for the needed number of frames to become available in the buffer,\nbefore reading and passing them to the resampler.\n\nIt would also be appropriate to omit the temporary output buffer,\nand write the output directly to the destination.\nThe [hound](https://crates.io/crates/hound) crate is a popular choice\nfor reading and writing uncompressed audio formats.\n\n\n## SIMD acceleration\n\n### Asynchronous resampling with anti-aliasing\n\nThe asynchronous resampler supports SIMD on x86_64 and on aarch64.\nThe SIMD capabilities of the CPU are determined at runtime.\nIf no supported SIMD instruction set is available, it falls back to a scalar implementation.\n\nOn x86_64, it will try to use AVX. If AVX isn't available, it will instead try SSE3.\n\nOn aarch64 (64-bit Arm), it will use Neon if available.\n\n### Synchronous resampling\n\nThe synchronous resamplers benefit from the SIMD support of the RustFFT library.\n\n## Cargo features\n\n### `fft_resampler`: Enable the FFT based synchronous resamplers\n\nThis feature is enabled by default. Disable it if the FFT resamplers are not needed,\nto save compile time and reduce the resulting binary size.\n\n### `log`: Enable logging\n\nThis feature enables logging via the `log` crate. This is intended for debugging purposes.\nNote that outputting logs allocates a [std::string::String] and most logging implementations involve various other system calls.\nThese calls may take some (unpredictable) time to return, during which the application is blocked.\nThis means that logging should be avoided if using this library in a realtime application.\n\nThe `log` feature can be enabled when running tests, which can be very useful when debugging.\nThe logging level can be set via the `RUST_LOG` environment variable.\n\nExample:\n```sh\nRUST_LOG=trace cargo test --features log\n```\n\n## Example\n\nResample a single chunk of a dummy audio file from 44100 to 48000 Hz.\nSee also the \"process_f64\" example that can be used to process a file from disk.\n```rust\nuse rubato::{Resampler, SincFixedIn, SincInterpolationType, SincInterpolationParameters, WindowFunction};\nlet params = SincInterpolationParameters {\n    sinc_len: 256,\n    f_cutoff: 0.95,\n    interpolation: SincInterpolationType::Linear,\n    oversampling_factor: 256,\n    window: WindowFunction::BlackmanHarris2,\n};\nlet mut resampler = SincFixedIn::\u003cf64\u003e::new(\n    48000 as f64 / 44100 as f64,\n    2.0,\n    params,\n    1024,\n    2,\n).unwrap();\n\nlet waves_in = vec![vec![0.0f64; 1024];2];\nlet waves_out = resampler.process(\u0026waves_in, None).unwrap();\n```\n\n## Included examples\n\nThe `examples` directory contains a few sample applications for testing the resamplers.\nThere are also Python scripts for generating simple test signals as well as analyzing the resampled results.\n\nThe examples read and write raw audio data in 64-bit float format.\nThey can be used to process .wav files if the files are first converted to the right format.\nUse `sox` to convert a .wav to raw samples:\n```sh\nsox some_file.wav -e floating-point -b 64 some_file_f64.raw\n```\nAfter processing, the result can be converted back to new .wav. This examples converts to 16-bits at 44.1 kHz:\n```sh\nsox -e floating-point -b 64 -r 44100 -c 2 resampler_output.raw -e signed-integer -b 16 some_file_resampled.wav\n```\n\nMany audio editors, for example Audacity, are also able to directly import and export the raw samples.\n\n## Compatibility\n\nThe `rubato` crate requires rustc version 1.61 or newer.\n\n## Changelog\n- v0.16.2\n  - Fix issues when using on 32-bit systems.\n- v0.16.1\n  - Fix issue in test suite when building without FFT resamplers.\n- v0.16.0\n  - Add support for changing the fixed input or output size of the asynchronous resamplers.\n- v0.15.0\n  - Make FFT resamplers optional via `fft_resampler` feature.\n  - Fix calculation of input and output sizes when creating FftFixedInOut resampler.\n  - Fix panic when using very small chunksizes (less than 5).\n- v0.14.1\n  - More bugfixes for buffer allocation and max output length calculation.\n  - Fix building with `log` feature.\n- v0.14.0\n  - Add argument to let `input/output_buffer_allocate()` optionally pre-fill buffers with zeros.\n  - Add convenience methods for managing buffers.\n  - Bugfixes for buffer allocation and max output length calculation.\n- v0.13.0\n  - Switch to slices of references for input and output data.\n  - Add faster (lower quality) asynchronous resamplers.\n  - Add a macro to help implement custom object safe resamplers.\n  - Optional smooth ramping of ratio changes to avoid audible steps.\n  - Add convenience methods for handling last frames in a stream.\n  - Add resampler reset method.\n  - Refactoring for a more logical structure.\n  - Add helper function for calculating cutoff frequency.\n  - Add quadratic interpolation for sinc resampler.\n  - Add method to get the delay through a resampler as a number of output frames.\n- v0.12.0\n  - Always enable all simd acceleration (and remove the simd Cargo features).\n- v0.11.0\n  - New api to allow use in realtime applications.\n  - Configurable adjust range of asynchronous resamplers.\n- v0.10.1\n  - Fix compiling with neon feature after changes in latest nightly.\n- v0.10.0\n  - Add an object-safe wrapper trait for Resampler.\n- v0.9.0\n  - Accept any AsRef\u003c\\[T\\]\u003e as input.\n\n\nLicense: MIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHEnquist%2Frubato","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FHEnquist%2Frubato","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHEnquist%2Frubato/lists"}