Commit ea3f7f51 authored by David Reid's avatar David Reid

Add ma_audio_buffer API.

The ma_audio_buffer object is used for storing raw audio data in memory
and reading from it like any other data source. It supports flexible
memory management, reading, seeking, memory mapping and looping.

See documentation under "Audio Buffers" for a detailed description.
parent d836c0b5
This diff is collapsed.
......@@ -19,6 +19,7 @@ ma_noise g_noise;
ma_waveform g_waveform;
ma_decoder g_decoder;
ma_bool32 g_hasDecoder = MA_FALSE;
ma_audio_buffer* g_pAudioBuffer;
void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{
......@@ -46,6 +47,9 @@ void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn,
if (g_hasDecoder) {
ma_mixer_mix_decoder(&g_mixerEffects, &g_decoder, submixFrameCountIn, MA_TRUE);
}
if (g_pAudioBuffer != NULL) {
ma_mixer_mix_audio_buffer(&g_mixerEffects, g_pAudioBuffer, submixFrameCountIn, MA_TRUE);
}
}
ma_mixer_end(&g_mixerEffects, &g_mixer, NULL);
}
......@@ -65,12 +69,19 @@ int main(int argc, char** argv)
ma_noise_config noiseConfig;
ma_waveform_config waveformConfig;
ma_decoder_config decoderConfig;
const char* pInputFilePath = NULL;
ma_audio_buffer_config audioBufferConfig;
void* pInputFileData2;
ma_uint64 inputFileDataSize2;
const char* pInputFilePath1 = NULL;
const char* pInputFilePath2 = NULL;
ma_effect_config effectConfig;
ma_effect effect;
if (argc > 1) {
pInputFilePath = argv[1];
pInputFilePath1 = argv[1];
}
if (argc > 2) {
pInputFilePath2 = argv[2];
}
deviceConfig = ma_device_config_init(ma_device_type_playback);
......@@ -125,20 +136,34 @@ int main(int argc, char** argv)
return result;
}
waveformConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.2, 220);
waveformConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.5, 220);
result = ma_waveform_init(&waveformConfig, &g_waveform);
if (result != MA_SUCCESS) {
return result;
}
if (pInputFilePath != NULL) {
if (pInputFilePath1 != NULL) {
decoderConfig = ma_decoder_config_init(device.playback.format, device.playback.channels, device.sampleRate);
result = ma_decoder_init_file(pInputFilePath, &decoderConfig, &g_decoder);
result = ma_decoder_init_file(pInputFilePath1, &decoderConfig, &g_decoder);
if (result == MA_SUCCESS) {
g_hasDecoder = MA_TRUE;
}
}
if (pInputFilePath2 != NULL) {
ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 0);
result = ma_decode_file(pInputFilePath2, &config, &inputFileDataSize2, &pInputFileData2);
if (result == MA_SUCCESS) {
audioBufferConfig = ma_audio_buffer_config_init(config.format, config.channels, inputFileDataSize2, pInputFileData2, NULL);
result = ma_audio_buffer_alloc_and_init(&audioBufferConfig, &g_pAudioBuffer);
ma_free(pInputFileData2, NULL);
pInputFileData2 = NULL;
}
}
/* Everything is setup. We can now start the device. */
result = ma_device_start(&device);
......
......@@ -801,6 +801,7 @@ ma_mixer_end()
MA_API ma_result ma_mixer_mix_decoder(ma_mixer* pMixer, ma_decoder* pDecoder, ma_uint64 frameCountIn, ma_bool32 loop);
#endif
MA_API ma_result ma_mixer_mix_audio_buffer(ma_mixer* pMixer, ma_audio_buffer* pAudioBuffer, ma_uint64 frameCountIn, ma_bool32 loop);
#ifndef MA_NO_GENERATION
MA_API ma_result ma_mixer_mix_waveform(ma_mixer* pMixer, ma_waveform* pWaveform, ma_uint64 frameCountIn);
MA_API ma_result ma_mixer_mix_noise(ma_mixer* pMixer, ma_noise* pNoise, ma_uint64 frameCountIn);
......@@ -808,6 +809,7 @@ MA_API ma_result ma_mixer_mix_noise(ma_mixer* pMixer, ma_noise* pNoise, ma_uint6
MA_API ma_result ma_mixer_mix_pcm_rb(ma_mixer* pMixer, ma_pcm_rb* pRB, ma_uint64 frameCountIn); /* Caller is the consumer. */
MA_API ma_result ma_mixer_mix_rb(ma_mixer* pMixer, ma_rb* pRB, ma_uint64 frameCountIn); /* Caller is the consumer. Assumes data is in the same format as the mixer. */
MA_API ma_result ma_mixer_mix_rb_ex(ma_mixer* pMixer, ma_rb* pRB, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn); /* Caller is the consumer. */
MA_API ma_result ma_mixer_mix_data_source(ma_mixer* pMixer, ma_data_source* pDataSource, ma_uint64 frameCountIn, ma_bool32 loop);
MA_API ma_result ma_mixer_set_volume(ma_mixer* pMixer, float volume);
MA_API ma_result ma_mixer_get_volume(ma_mixer* pMixer, float* pVolume);
MA_API ma_result ma_mixer_set_gain_db(ma_mixer* pMixer, float gainDB);
......@@ -2810,66 +2812,68 @@ MA_API ma_result ma_mixer_mix_callback(ma_mixer* pMixer, ma_mixer_mix_callback_p
}
#ifndef MA_NO_DECODING
typedef struct
MA_API ma_result ma_mixer_mix_decoder(ma_mixer* pMixer, ma_decoder* pDecoder, ma_uint64 frameCountIn, ma_bool32 loop)
{
ma_decoder* pDecoder;
ma_bool32 loop;
} ma_mixer_mix_decoder_data;
return ma_mixer_mix_data_source(pMixer, pDecoder, frameCountIn, loop);
}
#endif /* MA_NO_DECODING */
static ma_uint32 ma_mixer_mix_decoder__callback(void* pUserData, void* pFramesOut, ma_uint32 frameCount)
MA_API ma_result ma_mixer_mix_audio_buffer(ma_mixer* pMixer, ma_audio_buffer* pAudioBuffer, ma_uint64 frameCountIn, ma_bool32 loop)
{
ma_mixer_mix_decoder_data* pData = (ma_mixer_mix_decoder_data*)pUserData;
ma_uint32 totalFramesRead = 0;
void* pRunningFramesOut = pFramesOut;
/*
The ma_audio_buffer object is a data source, but we can do a specialized implementation to optimize data movement by utilizing memory mapping, kind
of like what we do with `ma_mixer_mix_pcm_rb()`.
*/
ma_result result;
ma_uint64 totalFramesProcessed = 0;
void* pRunningAccumulationBuffer = pMixer->pAccumulationBuffer;
while (totalFramesRead < frameCount) {
ma_uint32 framesToRead = frameCount - totalFramesRead;
ma_uint32 framesRead = (ma_uint32)ma_decoder_read_pcm_frames(pData->pDecoder, pRunningFramesOut, framesToRead); /* Safe cast because frameCount is 32-bit. */
if (framesRead < framesToRead) {
if (pData->loop) {
ma_decoder_seek_to_pcm_frame(pData->pDecoder, 0);
} else {
break;
if (pMixer == NULL || pAudioBuffer == NULL) {
return MA_INVALID_ARGS;
}
if (frameCountIn > pMixer->mixingState.frameCountIn) {
return MA_INVALID_ARGS; /* Passing in too many input frames. */
}
totalFramesRead += framesRead;
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesRead * ma_get_bytes_per_frame(pData->pDecoder->outputFormat, pData->pDecoder->outputChannels));
while (totalFramesProcessed < frameCountIn) {
void* pMappedBuffer;
ma_uint64 framesToProcess = frameCountIn - totalFramesProcessed;
result = ma_audio_buffer_map(pAudioBuffer, &pMappedBuffer, &framesToProcess);
if (framesToProcess == 0) {
break; /* Wasn't able to map any data. Abort. */
}
return totalFramesRead;
}
ma_mix_pcm_frames_ex(pRunningAccumulationBuffer, pMixer->format, pMixer->channels, pMappedBuffer, pAudioBuffer->format, pAudioBuffer->channels, framesToProcess);
MA_API ma_result ma_mixer_mix_decoder(ma_mixer* pMixer, ma_decoder* pDecoder, ma_uint64 frameCountIn, ma_bool32 loop)
{
ma_mixer_mix_decoder_data data;
data.pDecoder = pDecoder;
data.loop = loop;
ma_audio_buffer_unmap(pAudioBuffer, framesToProcess);
return ma_mixer_mix_callback(pMixer, ma_mixer_mix_decoder__callback, &data, frameCountIn, pDecoder->outputFormat, pDecoder->outputChannels);
}
#endif /* MA_NO_DECODING */
/* If after mapping we're at the end we'll need to decide if we want to loop. */
if (ma_audio_buffer_at_end(pAudioBuffer)) {
if (loop) {
ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0);
} else {
break; /* We've reached the end and we're not looping. */
}
}
#ifndef MA_NO_GENERATION
static ma_uint32 ma_mixer_mix_waveform__callback(void* pUserData, void* pFramesOut, ma_uint32 frameCount)
{
return (ma_uint32)ma_waveform_read_pcm_frames((ma_waveform*)pUserData, pFramesOut, frameCount); /* Safe case to ma_uint32 because frameCount is 32-bit. */
}
totalFramesProcessed += framesToProcess;
pRunningAccumulationBuffer = ma_offset_ptr(pRunningAccumulationBuffer, framesToProcess * ma_get_accumulation_bytes_per_frame(pMixer->format, pMixer->channels));
}
MA_API ma_result ma_mixer_mix_waveform(ma_mixer* pMixer, ma_waveform* pWaveform, ma_uint64 frameCountIn)
{
return ma_mixer_mix_callback(pMixer, ma_mixer_mix_waveform__callback, pWaveform, frameCountIn, pWaveform->config.format, pWaveform->config.channels);
return MA_SUCCESS;
}
static ma_uint32 ma_mixer_mix_noise__callback(void* pUserData, void* pFramesOut, ma_uint32 frameCount)
#ifndef MA_NO_GENERATION
MA_API ma_result ma_mixer_mix_waveform(ma_mixer* pMixer, ma_waveform* pWaveform, ma_uint64 frameCountIn)
{
return (ma_uint32)ma_noise_read_pcm_frames((ma_noise*)pUserData, pFramesOut, frameCount); /* Safe case to ma_uint32 because frameCount is 32-bit. */
return ma_mixer_mix_data_source(pMixer, pWaveform, frameCountIn, MA_FALSE);
}
MA_API ma_result ma_mixer_mix_noise(ma_mixer* pMixer, ma_noise* pNoise, ma_uint64 frameCountIn)
{
return ma_mixer_mix_callback(pMixer, ma_mixer_mix_noise__callback, pNoise, frameCountIn, pNoise->config.format, pNoise->config.channels);
return ma_mixer_mix_data_source(pMixer, pNoise, frameCountIn, MA_FALSE);
}
#endif /* MA_NO_GENERATION */
......@@ -2967,6 +2971,54 @@ MA_API ma_result ma_mixer_mix_rb_ex(ma_mixer* pMixer, ma_rb* pRB, ma_uint64 fram
return MA_SUCCESS;
}
typedef struct
{
ma_data_source* pDataSource;
ma_format format;
ma_uint32 channels;
ma_bool32 loop;
} ma_mixer_mix_data_source_data;
static ma_uint32 ma_mixer_mix_data_source__callback(void* pUserData, void* pFramesOut, ma_uint32 frameCount)
{
ma_mixer_mix_data_source_data* pData = (ma_mixer_mix_data_source_data*)pUserData;
ma_uint32 totalFramesRead = 0;
void* pRunningFramesOut = pFramesOut;
while (totalFramesRead < frameCount) {
ma_uint32 framesToRead = frameCount - totalFramesRead;
ma_uint32 framesRead = (ma_uint32)ma_data_source_read_pcm_frames(pData->pDataSource, pRunningFramesOut, framesToRead); /* Safe cast because frameCount is 32-bit. */
if (framesRead < framesToRead) {
if (pData->loop) {
ma_data_source_seek_to_pcm_frame(pData->pDataSource, 0);
} else {
break;
}
}
totalFramesRead += framesRead;
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesRead * ma_get_bytes_per_frame(pData->format, pData->channels));
}
return totalFramesRead;
}
MA_API ma_result ma_mixer_mix_data_source(ma_mixer* pMixer, ma_data_source* pDataSource, ma_uint64 frameCountIn, ma_bool32 loop)
{
ma_result result;
ma_mixer_mix_data_source_data data;
result = ma_data_source_get_data_format(pDataSource, &data.format, &data.channels);
if (result != MA_SUCCESS) {
return result;
}
data.pDataSource = pDataSource;
data.loop = loop;
return ma_mixer_mix_callback(pMixer, ma_mixer_mix_data_source__callback, &data, frameCountIn, data.format, data.channels);
}
MA_API ma_result ma_mixer_set_volume(ma_mixer* pMixer, float volume)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment