Created
May 6, 2016 20:05
-
-
Save roxlu/9d2ed4d16908144394c43cd4dd054b25 to your computer and use it in GitHub Desktop.
Example implementation of using the amazing b8brain audio samplerate converter, https://github.com/avaneev/r8brain-free-src This was used for a real time audio stream converter.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <poly/AudioConverterBrain.h> | |
#include <poly/AudioDataConverter.h> | |
namespace poly { | |
AudioConverterBrain::AudioConverterBrain() | |
:listener(NULL) | |
{ | |
} | |
AudioConverterBrain::~AudioConverterBrain() { | |
shutdown(); | |
} | |
/* | |
Initialize. | |
We allocate our destination buffers here. The size that we use | |
(expected_nframes) is based on the default frame sizes that we get | |
on Mac and Windows when capturing audio; actually it's 512 frames, | |
but we allocate a bit more just to be safe. I'm not sure how this | |
class could know the number of frames; maybe I can pass them into | |
this function as a member of `from`. | |
My current understanding is that r8brain works with doubles | |
(and ints) but not with floats, and only with deinterleaved | |
samples. Therefore I use the `in_deinterleaved` member that we | |
use to store deinterleaved data. | |
For now we only work with F32, interleaved data. We create | |
`CDSPResampler24` objects on the heap; similar to the exmaple.cpp | |
file from the b8brain repository. The `CDSPResampler24` is the | |
type that can be used for float conversions as stated in the | |
documentatin, see the doxygen documentation. | |
*/ | |
int AudioConverterBrain::init(AudioSettings from, | |
AudioSettings to, | |
AudioConverterListener* lis) | |
{ | |
if (NULL == lis) { | |
SX_ERROR("Given listener is NULL."); | |
return -1; | |
} | |
if (from.samplerate == to.samplerate) { | |
SX_ERROR("Input samplerate is same as output."); | |
return -1; | |
} | |
if (from.mode != to.mode) { | |
SX_ERROR("We don't support audio mode conversion."); | |
return -2; | |
} | |
if (AUDIO_BITSIZE_F32 != from.bitsize | |
|| AUDIO_BITSIZE_F32 != to.bitsize) | |
{ | |
SX_ERROR("Currently we only support AUDIO_BITSIZE_F32"); | |
return -3; | |
} | |
if (AUDIO_MODE_STEREO != from.mode | |
|| AUDIO_MODE_STEREO != to.mode) | |
{ | |
SX_ERROR("Currently we only support stereo interleaved."); | |
return -3; | |
} | |
if (false == from.is_interleaved | |
|| false == to.is_interleaved) | |
{ | |
SX_ERROR("We only support interleaved."); | |
return -4; | |
} | |
listener = lis; | |
convert_to.samplerate = to.samplerate; | |
convert_to.bitsize = to.bitsize; | |
convert_to.nchannels = to.mode; | |
convert_from.samplerate = from.samplerate; | |
convert_from.bitsize = from.bitsize; | |
convert_from.nchannels = from.mode; | |
in_deinterleaved.resize((size_t)to.mode); | |
/* Prepare our buffer. We allocate some size that we think is usable. */ | |
size_t expected_nframes = 1024; | |
for (int i = 0; i < convert_from.nchannels; ++i) { | |
in_deinterleaved[i].resize(expected_nframes, 0.0); | |
} | |
out_resampled.resize(expected_nframes * convert_from.nchannels); | |
/* Create the resampler objects. */ | |
CDSPResampler24* resampler = NULL; | |
for (int i = 0; i < convert_from.nchannels; ++i) { | |
resampler = new CDSPResampler24((double)from.samplerate, | |
(double)to.samplerate, | |
expected_nframes | |
); | |
resamplers.push_back(resampler); | |
} | |
out_pointers.resize(resamplers.size(), NULL); | |
return 0; | |
} | |
int AudioConverterBrain::shutdown() { | |
listener = NULL; | |
in_deinterleaved.clear(); | |
out_resampled.clear(); | |
out_pointers.clear(); | |
for (size_t i = 0; i < resamplers.size(); ++i) { | |
delete resamplers[i]; | |
resamplers[i] = NULL; | |
} | |
return 0; | |
} | |
/* | |
Convert audio data. Converting 512, 2 channels float, 32Khz | |
into 44.1Khz takes about 0.1-0.4ms on a intel i5 3.2Ghz | |
*/ | |
int AudioConverterBrain::convert(void* data, size_t nbytes, size_t nframes) { | |
if (NULL == data) { | |
SX_ERROR("Given data is NULL."); | |
return -1; | |
} | |
if (0 == nbytes) { | |
SX_ERROR("nbytes is 0."); | |
return -2; | |
} | |
if (0 == nframes) { | |
SX_ERROR("nframes is 0."); | |
return -3; | |
} | |
if (AUDIO_BITSIZE_F32 != convert_from.bitsize) { | |
SX_ERROR("Currently we only support AUDIO_BITSIZE_F32"); | |
return -4; | |
} | |
if (2 != convert_from.nchannels) { | |
SX_ERROR("Currently we only support interleaved 2 channel input data."); | |
return -5; | |
} | |
if (NULL == listener) { | |
SX_ERROR("Cannot convert audio data; listener is not set."); | |
return -6; | |
} | |
/* Make sure our deinterleaved buffers are big enough. */ | |
for (size_t i = 0; i < convert_from.nchannels; ++i) { | |
if (in_deinterleaved[i].size() < nframes) { | |
in_deinterleaved[i].resize(nframes, 0.0); | |
} | |
} | |
/* We assume input data to be interleaved; here we deinterleave. */ | |
float* src_ptr = (float*)data; | |
double* channel_ptrs[] = { &in_deinterleaved[0][0], &in_deinterleaved[1][0] }; | |
poly_deinterleave<float, double>(src_ptr, channel_ptrs, nframes, resamplers.size()); | |
/* Perform the resampling. */ | |
int frames_generated = 0; | |
for (size_t k = 0; k < resamplers.size(); ++k) { | |
frames_generated = resamplers[k]->process(&in_deinterleaved[k][0], nframes, out_pointers[k]); | |
} | |
size_t out_elements_needed = resamplers.size() * frames_generated; | |
if (out_resampled.size() < out_elements_needed) { | |
out_resampled.resize(out_elements_needed, 0.0); | |
} | |
if (2 == convert_from.nchannels) { | |
poly_interleave<double, float>(&out_pointers[0], &out_resampled[0], frames_generated, 2); | |
} | |
listener->onAudioConverterData((void*)&out_resampled[0], | |
frames_generated * sizeof(float) * convert_from.nchannels, | |
frames_generated | |
); | |
return 0; | |
} | |
} /* namespace poly */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Audio Converter using b8brain | |
============================== | |
GENERAL INFO: | |
This sample rate converter uses the awesome [r8brain-free-rc][0] | |
library to convert samplerates. At the time of writing we can | |
convert between any samplerate that is supported by r8brain but | |
we're limited to 2-channel, interleaved input data. | |
@todo When converting data, the first couple of output samples result | |
in a glitch; this is not a bug; it's described in the | |
documentation. There is also a way to work around this, see | |
the documentation. | |
LICENSE: | |
The r8brain repository is MIT licensed. | |
REFERENCES: | |
[0]: https://github.com/avaneev/r8brain-free-src "r8brain" | |
*/ | |
#ifndef POLY_AUDIO_CONVERTER_BRAIN_H | |
#define POLY_AUDIO_CONVERTER_BRAIN_H | |
#include <poly/AudioTypes.h> | |
#include <poly/AudioSettings.h> | |
#include <poly/AudioConverterListener.h> | |
#include <CDSPResampler.h> | |
using namespace r8b; | |
namespace poly { | |
/* --------------------------------------------------------------------------- */ | |
class AudioConverterBrain { | |
public: | |
AudioConverterBrain(); /* Sets default members. */ | |
~AudioConverterBrain(); /* Calls shutdown to cleanup. */ | |
int init(AudioSettings convertFrom, AudioSettings convertTo, AudioConverterListener* lis); /* Initialize. */ | |
int shutdown(); /* Cleans up; resets state as it was before calling init(). */ | |
int convert(void* data, size_t nbytes, size_t nframes); /* Convert the given data; make sure that it has the same format as the `convertFrom` parameter that you used when initializing. */ | |
private: | |
AudioConverterListener* listener; /* The listener that we call when we have (samplerate) converted data. */ | |
std::vector<std::vector<double> > in_deinterleaved; /* The b8brain library that we use, needs deinterleaved data; we've only worked with interleaved data so far so we expect that we need to deinterleave (which we do). */ | |
std::vector<float> out_resampled; /* After resampling using the b8brain library we have deinterleaved, resampled data, we use this array to write the interleaved data (with converted samplerate) into. */ | |
std::vector<double*> out_pointers; /* The b8brain samplerate converter outputs pointers to the converted audio data when we call it's process() function; we use these to interleave the result again. */ | |
std::vector<CDSPResampler24*> resamplers; /* The instances, for each input channel of the resamplers. */ | |
AudioConvertData convert_from; /* We convert from this format into `convert_to`. */ | |
AudioConvertData convert_to; /* We convert into this format. */ | |
}; | |
/* --------------------------------------------------------------------------- */ | |
} /* namespace poly */ | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment