Commit e1eb387a authored by David Reid's avatar David Reid

Add simple_mixing example.

parent 212e750d
/*
Usage: simple_mixing [input file 0] [input file 1] ... [input file n]
Example: simple_mixing file1.wav file2.flac
*/
#define DR_FLAC_IMPLEMENTATION
#include "../extras/dr_flac.h" // Enables FLAC decoding.
#define DR_MP3_IMPLEMENTATION
#include "../extras/dr_mp3.h" // Enables MP3 decoding.
#define DR_WAV_IMPLEMENTATION
#include "../extras/dr_wav.h" // Enables WAV decoding.
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
#include <stdio.h>
/*
For simplicity, this example requires the device use floating point samples.
*/
#define SAMPLE_FORMAT ma_format_f32
#define CHANNEL_COUNT 2
#define SAMPLE_RATE 48000
ma_uint32 g_decoderCount;
ma_decoder* g_pDecoders;
ma_bool32* g_pDecodersAtEnd;
ma_event g_stopEvent; /* <-- Signaled by the audio thread, waited on by the main thread. */
ma_bool32 are_all_decoders_at_end()
{
for (ma_uint32 iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
if (g_pDecodersAtEnd[iDecoder] == MA_FALSE) {
return MA_FALSE;
}
}
return MA_TRUE;
}
ma_uint32 read_and_mix_pcm_frames_f32(ma_decoder* pDecoder, float* pOutputF32, ma_uint32 frameCount)
{
/*
The way mixing works is that we just read into a temporary buffer, then take the contents of that buffer and mix it with the
contents of the output buffer by simply adding the sample together. You could also clip the samples to -1..+1, but I'm not
doing that in this example.
*/
float temp[4096];
ma_uint32 tempCapInFrames = ma_countof(temp) / CHANNEL_COUNT;
ma_uint32 totalFramesRead = 0;
while (totalFramesRead < frameCount) {
ma_uint32 framesReadThisIteration;
ma_uint32 totalFramesRemaining = frameCount - totalFramesRead;
ma_uint32 framesToReadThisIteration = tempCapInFrames;
if (framesToReadThisIteration > totalFramesRemaining) {
framesToReadThisIteration = totalFramesRemaining;
}
framesReadThisIteration = (ma_uint32)ma_decoder_read_pcm_frames(pDecoder, temp, framesToReadThisIteration);
if (framesReadThisIteration == 0) {
break;
}
/* Mix the frames together. */
for (ma_uint32 iSample = 0; iSample < framesReadThisIteration*CHANNEL_COUNT; ++iSample) {
pOutputF32[totalFramesRead*CHANNEL_COUNT + iSample] += temp[iSample];
}
totalFramesRead += framesReadThisIteration;
if (framesReadThisIteration < framesToReadThisIteration) {
break; /* Reached EOF. */
}
}
return totalFramesRead;
}
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
float* pOutputF32 = (float*)pOutput;
ma_assert(pDevice->playback.format == SAMPLE_FORMAT); /* <-- Important for this example. */
for (ma_uint32 iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
if (!g_pDecodersAtEnd[iDecoder]) {
ma_uint32 framesRead = read_and_mix_pcm_frames_f32(&g_pDecoders[iDecoder], pOutputF32, frameCount);
if (framesRead < frameCount) {
g_pDecodersAtEnd[iDecoder] = MA_TRUE;
}
}
}
/*
If at the end all of our decoders are at the end we need to stop. We cannot stop the device in the callback. Instead we need to
signal an event to indicate that it's stopped. The main thread will be waiting on the event, after which it will stop the event.
*/
if (are_all_decoders_at_end()) {
ma_event_signal(&g_stopEvent);
}
(void)pInput;
}
int main(int argc, char** argv)
{
ma_result result;
ma_decoder_config decoderConfig;
if (argc < 2) {
printf("No input files.\n");
return -1;
}
g_decoderCount = argc-1;
g_pDecoders = (ma_decoder*)malloc(sizeof(*g_pDecoders) * g_decoderCount);
g_pDecodersAtEnd = (ma_bool32*) malloc(sizeof(*g_pDecodersAtEnd) * g_decoderCount);
/* In this example, all decoders need to have the same output format. */
decoderConfig = ma_decoder_config_init(SAMPLE_FORMAT, CHANNEL_COUNT, SAMPLE_RATE);
for (ma_uint32 iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
result = ma_decoder_init_file(argv[1+iDecoder], &decoderConfig, &g_pDecoders[iDecoder]);
if (result != MA_SUCCESS) {
for (ma_uint32 iDecoder2 = 0; iDecoder2 < iDecoder; ++iDecoder2) {
ma_decoder_uninit(&g_pDecoders[iDecoder2]);
}
free(g_pDecoders);
free(g_pDecodersAtEnd);
printf("Failed to load %s.\n", argv[1+iDecoder]);
return -3;
}
g_pDecodersAtEnd[iDecoder] = MA_FALSE;
}
/* Create only a single device. The decoders will be mixed together in the callback. In this example the data format needs to be the same as the decoders. */
ma_device_config config = ma_device_config_init(ma_device_type_playback);
config.playback.format = SAMPLE_FORMAT;
config.playback.channels = CHANNEL_COUNT;
config.sampleRate = SAMPLE_RATE;
config.dataCallback = data_callback;
config.pUserData = NULL;
ma_device device;
if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
for (ma_uint32 iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
ma_decoder_uninit(&g_pDecoders[iDecoder]);
}
free(g_pDecoders);
free(g_pDecodersAtEnd);
printf("Failed to open playback device.\n");
return -3;
}
/*
We can't stop in the audio thread so we instead need to use an event. We wait on this thread in the main thread, and signal it in the audio thread. This
needs to be done before starting the device. We need a context to initialize the event, which we can get from the device. Alternatively you can initialize
a context separately, but we don't need to do that for this example.
*/
ma_event_init(device.pContext, &g_stopEvent);
/* Now we start playback and wait for the audio thread to tell us to stop. */
if (ma_device_start(&device) != MA_SUCCESS) {
ma_device_uninit(&device);
for (ma_uint32 iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
ma_decoder_uninit(&g_pDecoders[iDecoder]);
}
free(g_pDecoders);
free(g_pDecodersAtEnd);
printf("Failed to start playback device.\n");
return -4;
}
printf("Waiting for playback to complete...\n");
ma_event_wait(&g_stopEvent);
/* Getting here means the audio thread has signaled that the device should be stopped. */
ma_device_uninit(&device);
for (ma_uint32 iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
ma_decoder_uninit(&g_pDecoders[iDecoder]);
}
free(g_pDecoders);
free(g_pDecodersAtEnd);
return 0;
}
...@@ -294,13 +294,14 @@ ...@@ -294,13 +294,14 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\examples\simple_mixing.c" />
<ClCompile Include="..\examples\simple_playback.c"> <ClCompile Include="..\examples\simple_playback.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\research\tests\ma_resampler_test_0.c"> <ClCompile Include="..\research\tests\ma_resampler_test_0.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
......
...@@ -57,6 +57,9 @@ ...@@ -57,6 +57,9 @@
<ClCompile Include="..\examples\advanced_config.c"> <ClCompile Include="..\examples\advanced_config.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\examples\simple_mixing.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\miniaudio.h"> <ClInclude Include="..\miniaudio.h">
......
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