Commit 67db0635 authored by David Reid's avatar David Reid

API CHANGE: Change the data callback in preparation for full-duplex.

This removes the two separate callbacks for sending and receiving data
to/from the device to a unified callback that's used for both input and
output. The new callback takes a pointer to both an input and output
buffer. When the device is opened in playback mode the input pointer
will be set to null. Likewise the output pointer will be set to null
for capture devices. Both input and output pointers will be non-null
for full-duplex devices.
parent 3badd55b
...@@ -12,7 +12,7 @@ mal_uint32 capturedSampleCount = 0; ...@@ -12,7 +12,7 @@ mal_uint32 capturedSampleCount = 0;
mal_int16* pCapturedSamples = NULL; mal_int16* pCapturedSamples = NULL;
mal_uint32 playbackSample = 0; mal_uint32 playbackSample = 0;
void on_recv_frames(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples) void on_recv_frames(mal_device* pDevice, const void* pInput, void* pOutput, mal_uint32 frameCount)
{ {
mal_uint32 sampleCount = frameCount * pDevice->channels; mal_uint32 sampleCount = frameCount * pDevice->channels;
...@@ -22,13 +22,15 @@ void on_recv_frames(mal_device* pDevice, mal_uint32 frameCount, const void* pSam ...@@ -22,13 +22,15 @@ void on_recv_frames(mal_device* pDevice, mal_uint32 frameCount, const void* pSam
return; return;
} }
memcpy(pNewCapturedSamples + capturedSampleCount, pSamples, sampleCount * sizeof(mal_int16)); memcpy(pNewCapturedSamples + capturedSampleCount, pInput, sampleCount * sizeof(mal_int16));
pCapturedSamples = pNewCapturedSamples; pCapturedSamples = pNewCapturedSamples;
capturedSampleCount = newCapturedSampleCount; capturedSampleCount = newCapturedSampleCount;
(void)pOutput;
} }
mal_uint32 on_send_frames(mal_device* pDevice, mal_uint32 frameCount, void* pSamples) void on_send_frames(mal_device* pDevice, const void* pInput, void* pOutput, mal_uint32 frameCount)
{ {
mal_uint32 samplesToRead = frameCount * pDevice->channels; mal_uint32 samplesToRead = frameCount * pDevice->channels;
if (samplesToRead > capturedSampleCount-playbackSample) { if (samplesToRead > capturedSampleCount-playbackSample) {
...@@ -36,26 +38,27 @@ mal_uint32 on_send_frames(mal_device* pDevice, mal_uint32 frameCount, void* pSam ...@@ -36,26 +38,27 @@ mal_uint32 on_send_frames(mal_device* pDevice, mal_uint32 frameCount, void* pSam
} }
if (samplesToRead == 0) { if (samplesToRead == 0) {
return 0; return;
} }
memcpy(pSamples, pCapturedSamples + playbackSample, samplesToRead * sizeof(mal_int16)); memcpy(pOutput, pCapturedSamples + playbackSample, samplesToRead * sizeof(mal_int16));
playbackSample += samplesToRead; playbackSample += samplesToRead;
return samplesToRead / pDevice->channels; (void)pInput;
} }
int main() int main()
{ {
mal_device_config config;
mal_context context; mal_context context;
if (mal_context_init(NULL, 0, NULL, &context) != MAL_SUCCESS) { if (mal_context_init(NULL, 0, NULL, &context) != MAL_SUCCESS) {
printf("Failed to initialize context."); printf("Failed to initialize context.");
return -1; return -1;
} }
mal_device_config config = mal_device_config_init(mal_format_s16, 2, 48000, on_recv_frames, on_send_frames, NULL);
printf("Recording...\n"); printf("Recording...\n");
config = mal_device_config_init(mal_format_s16, 2, 48000, on_recv_frames, NULL);
mal_device captureDevice; mal_device captureDevice;
if (mal_device_init(&context, mal_device_type_capture, NULL, &config, &captureDevice) != MAL_SUCCESS) { if (mal_device_init(&context, mal_device_type_capture, NULL, &config, &captureDevice) != MAL_SUCCESS) {
mal_context_uninit(&context); mal_context_uninit(&context);
...@@ -78,6 +81,7 @@ int main() ...@@ -78,6 +81,7 @@ int main()
printf("Playing...\n"); printf("Playing...\n");
config = mal_device_config_init(mal_format_s16, 2, 48000, on_send_frames, NULL);
mal_device playbackDevice; mal_device playbackDevice;
if (mal_device_init(&context, mal_device_type_playback, NULL, &config, &playbackDevice) != MAL_SUCCESS) { if (mal_device_init(&context, mal_device_type_playback, NULL, &config, &playbackDevice) != MAL_SUCCESS) {
mal_context_uninit(&context); mal_context_uninit(&context);
......
...@@ -11,14 +11,16 @@ ...@@ -11,14 +11,16 @@
#include <stdio.h> #include <stdio.h>
// This is the function that's used for sending more data to the device for playback. // This is the function that's used for sending more data to the device for playback.
mal_uint32 on_send_frames_to_device(mal_device* pDevice, mal_uint32 frameCount, void* pSamples) void on_send_frames_to_device(mal_device* pDevice, const void* pInput, void* pOutput, mal_uint32 frameCount)
{ {
mal_decoder* pDecoder = (mal_decoder*)pDevice->pUserData; mal_decoder* pDecoder = (mal_decoder*)pDevice->pUserData;
if (pDecoder == NULL) { if (pDecoder == NULL) {
return 0; return;
} }
return (mal_uint32)mal_decoder_read_pcm_frames(pDecoder, frameCount, pSamples); mal_decoder_read_pcm_frames(pDecoder, frameCount, pOutput);
(void)pInput;
} }
int main(int argc, char** argv) int main(int argc, char** argv)
...@@ -34,7 +36,7 @@ int main(int argc, char** argv) ...@@ -34,7 +36,7 @@ int main(int argc, char** argv)
return -2; return -2;
} }
mal_device_config config = mal_device_config_init_playback( mal_device_config config = mal_device_config_init(
decoder.outputFormat, decoder.outputFormat,
decoder.outputChannels, decoder.outputChannels,
decoder.outputSampleRate, decoder.outputSampleRate,
......
This diff is collapsed.
...@@ -30,16 +30,18 @@ mal_uint32 on_convert_samples_out(mal_format_converter* pConverter, mal_uint32 f ...@@ -30,16 +30,18 @@ mal_uint32 on_convert_samples_out(mal_format_converter* pConverter, mal_uint32 f
return (mal_uint32)mal_format_converter_read(pConverterIn, frameCount, pFrames, NULL); return (mal_uint32)mal_format_converter_read(pConverterIn, frameCount, pFrames, NULL);
} }
mal_uint32 on_send_to_device__original(mal_device* pDevice, mal_uint32 frameCount, void* pFrames) void on_send_to_device__original(mal_device* pDevice, const void* pInput, void* pOutput, mal_uint32 frameCount)
{ {
(void)pDevice;
mal_assert(pDevice->format == mal_format_f32); mal_assert(pDevice->format == mal_format_f32);
mal_assert(pDevice->channels == 1); mal_assert(pDevice->channels == 1);
return (mal_uint32)mal_sine_wave_read_f32(&sineWave, frameCount, (float*)pFrames); mal_sine_wave_read_f32(&sineWave, frameCount, (float*)pOutput);
(void)pDevice;
(void)pInput;
} }
mal_uint32 on_send_to_device__dithered(mal_device* pDevice, mal_uint32 frameCount, void* pFrames) void on_send_to_device__dithered(mal_device* pDevice, const void* pInput, void* pOutput, mal_uint32 frameCount)
{ {
mal_assert(pDevice->channels == 1); mal_assert(pDevice->channels == 1);
...@@ -47,12 +49,14 @@ mal_uint32 on_send_to_device__dithered(mal_device* pDevice, mal_uint32 frameCoun ...@@ -47,12 +49,14 @@ mal_uint32 on_send_to_device__dithered(mal_device* pDevice, mal_uint32 frameCoun
mal_assert(pConverter != NULL); mal_assert(pConverter != NULL);
mal_assert(pDevice->format == pConverter->config.formatOut); mal_assert(pDevice->format == pConverter->config.formatOut);
return (mal_uint32)mal_format_converter_read(pConverter, frameCount, pFrames, NULL); mal_format_converter_read(pConverter, frameCount, pOutput, NULL);
(void)pInput;
} }
int do_dithering_test() int do_dithering_test()
{ {
mal_device_config config = mal_device_config_init_playback(mal_format_f32, 1, 0, on_send_to_device__original, NULL); mal_device_config config = mal_device_config_init(mal_format_f32, 1, 0, on_send_to_device__original, NULL);
mal_device device; mal_device device;
mal_result result; mal_result result;
...@@ -102,7 +106,7 @@ int do_dithering_test() ...@@ -102,7 +106,7 @@ int do_dithering_test()
return -3; return -3;
} }
config = mal_device_config_init_playback(converterOutConfig.formatOut, 1, 0, on_send_to_device__dithered, &converterOut); config = mal_device_config_init(converterOutConfig.formatOut, 1, 0, on_send_to_device__dithered, &converterOut);
result = mal_device_init(NULL, mal_device_type_playback, NULL, &config, &device); result = mal_device_init(NULL, mal_device_type_playback, NULL, &config, &device);
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
......
#include <stdio.h>
#define MINI_AL_IMPLEMENTATION
#include "../mini_al.h"
mal_sine_wave sineWave;
mal_uint32 framesWritten;
mal_event stopEvent;
mal_bool32 isInitialRun = MAL_TRUE;
void on_stop(mal_device* pDevice)
{
(void)pDevice;
printf("STOPPED\n");
}
void data_callback(mal_device* pDevice, const void* pInput, void* pOutput, mal_uint32 frameCount)
{
(void)pInput; /* Not used yet. */
/* It's important to initialize the buffer with silence to ensure exactly a second worth of data is actually output to the speakers. */
mal_zero_memory(pOutput, frameCount * mal_get_bytes_per_frame(pDevice->format, pDevice->channels));
/* Output exactly one second of data. Pad the end with silence. */
mal_uint32 framesRemaining = pDevice->sampleRate - framesWritten;
mal_uint32 framesToProcess = frameCount;
if (framesToProcess > framesRemaining && isInitialRun) {
framesToProcess = framesRemaining;
}
mal_sine_wave_read_f32_ex(&sineWave, framesToProcess, pDevice->channels, mal_stream_layout_interleaved, (float**)&pOutput);
if (isInitialRun) {
framesWritten += framesToProcess;
}
mal_assert(framesWritten <= pDevice->sampleRate);
if (framesWritten >= pDevice->sampleRate) {
if (isInitialRun) {
printf("STOPPING [AUDIO THREAD]...\n");
mal_event_signal(&stopEvent);
isInitialRun = MAL_FALSE;
}
}
}
int main(int argc, char** argv)
{
mal_result result;
(void)argc;
(void)argv;
mal_backend backend = mal_backend_wasapi;
mal_sine_wave_init(0.25, 400, 44100, &sineWave);
mal_device_config config = mal_device_config_init_default(data_callback, NULL);
config.format = mal_format_f32;
config.channels = 2;
config.sampleRate = 44100;
config.onStopCallback = on_stop;
config.bufferSizeInFrames = 16384*4;
mal_device device;
result = mal_device_init_ex(&backend, 1, NULL, mal_device_type_playback, NULL, &config, &device);
if (result != MAL_SUCCESS) {
printf("Failed to initialize device.\n");
return result;
}
result = mal_event_init(device.pContext, &stopEvent);
if (result != MAL_SUCCESS) {
printf("Failed to initialize stop event.\n");
return result;
}
mal_device_start(&device);
/* We wait for the stop event, stop the device, then ask the user to press any key to restart. This checks that the device can restart after stopping. */
mal_event_wait(&stopEvent);
printf("STOPPING [MAIN THREAD]...\n");
mal_device_stop(&device);
printf("Press Enter to restart...\n");
getchar();
result = mal_device_start(&device);
if (result != MAL_SUCCESS) {
printf("Failed to restart the device.\n");
mal_device_uninit(&device);
return -1;
}
printf("Press Enter to quit...\n");
getchar();
mal_device_uninit(&device);
return 0;
}
\ No newline at end of file
...@@ -2232,18 +2232,16 @@ typedef struct ...@@ -2232,18 +2232,16 @@ typedef struct
mal_event endOfPlaybackEvent; mal_event endOfPlaybackEvent;
} playback_test_callback_data; } playback_test_callback_data;
mal_uint32 on_send__playback_test(mal_device* pDevice, mal_uint32 frameCount, void* pFrames) void on_send__playback_test(mal_device* pDevice, const void* pInput, void* pOutput, mal_uint32 frameCount)
{ {
playback_test_callback_data* pData = (playback_test_callback_data*)pDevice->pUserData; playback_test_callback_data* pData = (playback_test_callback_data*)pDevice->pUserData;
mal_assert(pData != NULL); mal_assert(pData != NULL);
#if !defined(__EMSCRIPTEN__) #if !defined(__EMSCRIPTEN__)
mal_uint64 framesRead = mal_decoder_read_pcm_frames(pData->pDecoder, frameCount, pFrames); mal_uint64 framesRead = mal_decoder_read_pcm_frames(pData->pDecoder, frameCount, pOutput);
if (framesRead == 0) { if (framesRead == 0) {
mal_event_signal(&pData->endOfPlaybackEvent); mal_event_signal(&pData->endOfPlaybackEvent);
} }
return (mal_uint32)framesRead;
#else #else
if (pDevice->format == mal_format_f32) { if (pDevice->format == mal_format_f32) {
for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) {
...@@ -2254,10 +2252,6 @@ mal_uint32 on_send__playback_test(mal_device* pDevice, mal_uint32 frameCount, vo ...@@ -2254,10 +2252,6 @@ mal_uint32 on_send__playback_test(mal_device* pDevice, mal_uint32 frameCount, vo
((float*)pFrames)[iFrame*pDevice->channels + iChannel] = sample; ((float*)pFrames)[iFrame*pDevice->channels + iChannel] = sample;
} }
} }
return frameCount;
} else {
return 0;
} }
#endif #endif
} }
...@@ -2290,7 +2284,7 @@ int do_playback_test(mal_backend backend) ...@@ -2290,7 +2284,7 @@ int do_playback_test(mal_backend backend)
printf(" Opening Device... "); printf(" Opening Device... ");
{ {
mal_context_config contextConfig = mal_context_config_init(on_log); mal_context_config contextConfig = mal_context_config_init(on_log);
mal_device_config deviceConfig = mal_device_config_init_default_playback(on_send__playback_test, &callbackData); mal_device_config deviceConfig = mal_device_config_init_default(on_send__playback_test, &callbackData);
deviceConfig.onStopCallback = on_stop__playback_test; deviceConfig.onStopCallback = on_stop__playback_test;
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)
......
...@@ -270,7 +270,22 @@ ...@@ -270,7 +270,22 @@
<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_enumeration.c" /> <ClCompile Include="..\examples\simple_capture.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\examples\simple_enumeration.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\examples\simple_playback.c"> <ClCompile Include="..\examples\simple_playback.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
...@@ -303,6 +318,14 @@ ...@@ -303,6 +318,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="mal_duplex.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="mal_no_device_io.c"> <ClCompile Include="mal_no_device_io.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
...@@ -327,6 +350,7 @@ ...@@ -327,6 +350,7 @@
<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="mal_stop.c" />
<ClCompile Include="mal_test_0.c"> <ClCompile Include="mal_test_0.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
......
...@@ -51,6 +51,15 @@ ...@@ -51,6 +51,15 @@
<ClCompile Include="..\examples\simple_enumeration.c"> <ClCompile Include="..\examples\simple_enumeration.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="mal_duplex.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mal_stop.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\examples\simple_capture.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\mini_al.h"> <ClInclude Include="..\mini_al.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