Channel conversion is used for channel rearrangement and conversion from one channel count to another. The `ma_channel_converter` API is used for channel
conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo.
result = ma_channel_converter_init(&config, &converter);
if (result != MA_SUCCESS) {
// Error.
}
```
To process perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so:
```c
ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);
if (result != MA_SUCCESS) {
// Error.
}
```
It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM frames.
The only formats supported are ma_format_s16 and ma_format_f32. If you need another format you need to convert your data manually which you can do with
ma_pcm_convert(), etc.
Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
Channel Mapping
---------------
In addition to converting from one channel count to another, like the example above, The channel converter can also be used to rearrange channels. When
initializing the channel converter, you can optionally pass in channel maps for both the input and output frames. If the channel counts are the same, and each
channel map contains the same channel positions with the exception that they're in a different order, a simple shuffling of the channels with be performed. If,
however, there is not a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed based on a mixing
mode which is specified when initializing the `ma_channel_converter_config` object.
When converting from mono to multi-channel, the mono channel is simply copied to each output channel. When going the other way around, the audio of each output
channel is simply averaged and copied to the mono channel.
In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess channels and silence extra channels. For example, converting
from 4 to 2 channels, the 3rd and 4th channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and 4th channels.
The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a simple distribution between input and output. Imagine sitting
in the middle of a room, with speakers on the walls representing channel positions. The MA_CHANNEL_FRONT_LEFT position can be thought of as being in the corner
of the front and left walls.
Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined weights. Custom weights can be passed in as the last parameter of
config.allowDynamicSampleRate=MA_FALSE;/* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */
/* We can't read more frames than can fit in the output buffer. */
if(pConverter->hasPostFormatConversion){
if(frameCountOutThisIteration>tempBufferOutCap){
frameCountOutThisIteration=tempBufferOutCap;
}
}
/* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */
The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do
We can't convert more frames than will fit in the output buffer. We shouldn't actually need to do this check because the channel count is always reduced
in this case which means we should always have capacity, but I'm leaving it here just for safety for future maintenance.
*/
if(frameCountInThisIteration>tempBufferMidCap){
frameCountInThisIteration=tempBufferMidCap;
}
/*
Make sure we don't read any more input frames than we need to fill the output frame count. If we do this we will end up in a situation where we lose some
Here is where the real work is done. Getting here means we're not using a passthrough and we need to move the data through each of the relevant stages. The order
of our stages depends on the input and output channel count. If the input channels is less than the output channels we want to do sample rate conversion first so
that it has less work (resampling is the most expensive part of format conversion).
Biquad filtering is implemented using transposed direct form 2. The denominator coefficients are a0, a1 and a2, and the numerator coefficients are b0, b1 and
b2. The a0 coefficient is required and coefficients must not be pre-normalized.
Supported formats are ma_format_s16 and ma_format_f32. If you need to use a different format you need to convert it yourself beforehand. When using
ma_format_s16 the biquad filter will use fixed point arithmetic. When using ma_format_f32, floating point arithmetic will be used.
Input and output frames are always interleaved.
Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
If you need to change the values of the coefficients, but maintain the values in the registers you can do so with `ma_biquad_reinit()`. This is useful if you
need to change the properties of the filter while keeping the values of registers valid to avoid glitching or whatnot. Do not use `ma_biquad_init()` for this
as it will do a full initialization which involves clearing the registers to 0. Note that changing the format or channel count after initialization is invalid
Supported formats are ma_format_s16 and ma_format_f32. If you need to use a different format you need to convert it yourself beforehand. Input and output
frames are always interleaved.
Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so:
If you need to change the configuration of the filter, but need to maintain the state of internal registers you can do so with `ma_lpf_reinit()`. This may be
useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel
count after initialization is invalid and will result in an error.
ma_result result = ma_resampler_init(&config, &resampler);
if (result != MA_SUCCESS) {
// An error occurred...
}
```
Do the following to uninitialize the resampler:
```c
ma_resampler_uninit(&resampler);
```
The following example shows how data can be processed
```c
ma_uint64 frameCountIn = 1000;
ma_uint64 frameCountOut = 2000;
ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
if (result != MA_SUCCESS) {
// An error occurred...
}
// At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number of output frames written.
```
To initialize the resampler you first need to set up a config (`ma_resampler_config`) with `ma_resampler_config_init()`. You need to specify the sample format
you want to use, the number of channels, the input and output sample rate, and the algorithm.
The sample format can be one of the following:
- ma_format_s16
- ma_format_f32
If you need a different format you will need to perform pre- and post-conversions yourself where necessary. Note that the format is the same for both input
and output. The format cannot be changed after initialization.
The resampler supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization.
The sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the
only configuration property that can be changed after initialization.
The miniaudio resampler supports multiple algorithms:
- Linear: ma_resample_algorithm_linear
- Speex: ma_resample_algorithm_speex
Because Speex is not public domain it is strictly opt-in and the code is stored in separate files. if you opt-in to the Speex backend you will need to consider
it's license, the text of which can be found in it's source files in "extras/speex_resampler". Details on how to opt-in to the Speex resampler is explained in
the Speex Resampler section below.
The algorithm cannot be changed after initialization.
Processing always happens on a per PCM frame basis and always assumed interleaved input and output. De-interleaved processing is not supported. To process
frames, use `ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number of
input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the
number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large
buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek.
The sample rate can be changed dynamically on the fly. You can change this with explicit sample rates with `ma_resampler_set_rate()` and also with a decimal
ratio with `ma_resampler_set_rate_ratio()`. The ratio is in/out.
Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with
`ma_resampler_get_required_input_frame_count()`. Likewise, it's sometimes useful to know exactly how many frames would be output given a certain number of
input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`.
Due to the nature of how resampling works, the resampler introduces some latency. This can be retrieved in terms of both the input rate and the output rate
with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
Resampling Algorithms
---------------------
The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency,
but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap applications
internally for memory management.
Linear Resampling
-----------------
The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, some control over the quality of the linear resampler which
may make it a suitable option depending on your requirements.
The linear resampler performs low-pass filtering before or after downsampling or upsampling, depending on the sample rates you're converting between. When
decreasing the sample rate, the low-pass filter will be applied before downsampling. When increasing the rate it will be performed after upsampling. By default
a second order low-pass filter will be applied. To improve quality you can chain low-pass filters together, up to a maximum of `MA_MAX_RESAMPLER_LPF_FILTERS`.
This comes at the expense of increased computational cost and latency. You can also disable filtering altogether by setting the filter count to 0. The filter
count is controlled with the `lpfCount` config variable.
The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of the input and output sample rates (Nyquist Frequency). This
can be controlled with the `lpfNyquistFactor` config variable. This defaults to 1, and should be in the range of 0..1, although a value of 0 does not make
sense and should be avoided. A value of 1 will use the Nyquist Frequency as the cutoff. A value of 0.5 will use half the Nyquist Frequency as the cutoff, etc.
Values less than 1 will result in more washed out sound due to more of the higher frequencies being removed. This config variable has no impact on performance
and is a purely perceptual configuration.
The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`.
Speex Resampling
----------------
The Speex resampler is made up of third party code which is released under the BSD license. Because it is licensed differently to miniaudio, which is public
domain, it is strictly opt-in and all of it's code is stored in separate files. If you opt-in to the Speex resampler you must consider the license text in it's
source files. To opt-in, you must first #include the following file before the implementation of miniaudio.h:
Note that even if you opt-in to the Speex backend, miniaudio won't use it unless you explicitly ask for it in the respective config of the object you are
initializing. If you try to use the Speex resampler without opting in, initialization of the `ma_resampler` object will fail with `MA_NO_BACKEND`.
The only configuration option to consider with the Speex resampler is the `speex.quality` config variable. This is a value between 0 and 10, with 0 being
the worst/fastest and 10 being the best/slowest. The default value is 3.
ma_uint32lpfCount;/* How many low-pass filters to chain together. A single low-pass filter is second order. Setting this to 0 will disable low-pass filtering. */
doublelpfNyquistFactor;/* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */
/* Fill the input buffer with sequential numbers so we can get an idea on the state of things. Useful for inspecting the linear backend in particular. */
/* We retrieve the required number of input frames for the specified number of output frames, and then compare with what we actually get when reading. */
/* Fill the input buffer with sequential numbers so we can get an idea on the state of things. Useful for inspecting the linear backend in particular. */
/* We retrieve the required number of input frames for the specified number of output frames, and then compare with what we actually get when reading. */