Commit 32c64703 authored by David Reid's avatar David Reid

Initial working implementation of full-duplex on WASAPI.

parent 934c7d5e
// This example simply captures data from your default microphone until you press Enter, after // This example simply captures data from your default microphone until you press Enter. The output is saved to the file specified on the command line.
// which it plays back the captured audio.
#define MINI_AL_IMPLEMENTATION #define MINI_AL_IMPLEMENTATION
#include "../mini_al.h" #include "../mini_al.h"
#define DR_WAV_IMPLEMENTATION
#include "../extras/dr_wav.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
void data_callback(mal_device* pDevice, void* pOutput, const void* pInput, mal_uint32 frameCount)
mal_uint32 capturedSampleCount = 0;
mal_int16* pCapturedSamples = NULL;
mal_uint32 playbackSample = 0;
void on_recv_frames(mal_device* pDevice, void* pOutput, const void* pInput, mal_uint32 frameCount)
{ {
mal_uint32 sampleCount = frameCount * pDevice->channels;
mal_uint32 newCapturedSampleCount = capturedSampleCount + sampleCount;
mal_int16* pNewCapturedSamples = (mal_int16*)realloc(pCapturedSamples, newCapturedSampleCount * sizeof(mal_int16));
if (pNewCapturedSamples == NULL) {
return;
}
memcpy(pNewCapturedSamples + capturedSampleCount, pInput, sampleCount * sizeof(mal_int16));
pCapturedSamples = pNewCapturedSamples;
capturedSampleCount = newCapturedSampleCount;
(void)pOutput; (void)pOutput;
drwav* pWav = (drwav*)pDevice->pUserData;
mal_assert(pWav != NULL);
drwav_write_pcm_frames(pWav, frameCount, pInput);
} }
void on_send_frames(mal_device* pDevice, void* pOutput, const void* pInput, mal_uint32 frameCount) int main(int argc, char** argv)
{ {
mal_uint32 samplesToRead = frameCount * pDevice->channels; if (argc < 2) {
if (samplesToRead > capturedSampleCount-playbackSample) { printf("No input file.\n");
samplesToRead = capturedSampleCount-playbackSample; return -1;
}
if (samplesToRead == 0) {
return;
} }
memcpy(pOutput, pCapturedSamples + playbackSample, samplesToRead * sizeof(mal_int16)); mal_result result;
playbackSample += samplesToRead;
(void)pInput;
}
int main() drwav_data_format wavFormat;
{ wavFormat.container = drwav_container_riff;
mal_device_config config; wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT;
wavFormat.channels = 2;
wavFormat.sampleRate = 44100;
wavFormat.bitsPerSample = 32;
mal_context context; drwav wav;
if (mal_context_init(NULL, 0, NULL, &context) != MAL_SUCCESS) { if (drwav_init_file_write(&wav, argv[1], &wavFormat) == DRWAV_FALSE) {
printf("Failed to initialize context."); printf("Failed to initialize output file.\n");
return -1; return -1;
} }
printf("Recording...\n"); mal_device_config config = mal_device_config_init(mal_device_type_capture);
config = mal_device_config_init(mal_format_s16, 2, 48000, on_recv_frames, NULL); config.capture.format = mal_format_f32;
mal_device captureDevice; config.capture.channels = wavFormat.channels;
if (mal_device_init(&context, mal_device_type_capture, NULL, &config, &captureDevice) != MAL_SUCCESS) { config.sampleRate = wavFormat.sampleRate;
mal_context_uninit(&context); config.dataCallback = data_callback;
config.pUserData = &wav;
mal_device device;
result = mal_device_init(NULL, &config, &device);
if (result != MAL_SUCCESS) {
printf("Failed to initialize capture device.\n"); printf("Failed to initialize capture device.\n");
return -2; return -2;
} }
if (mal_device_start(&captureDevice) != MAL_SUCCESS) { result = mal_device_start(&device);
mal_device_uninit(&captureDevice); if (result != MAL_SUCCESS) {
mal_context_uninit(&context); mal_device_uninit(&device);
printf("Failed to start capture device.\n"); printf("Failed to start device.\n");
return -3; return -3;
} }
printf("Press Enter to stop recording...\n"); printf("Press Enter to stop recording...\n");
getchar(); getchar();
mal_device_uninit(&device);
drwav_uninit(&wav);
mal_device_uninit(&captureDevice);
printf("Playing...\n");
config = mal_device_config_init(mal_format_s16, 2, 48000, on_send_frames, NULL);
mal_device playbackDevice;
if (mal_device_init(&context, mal_device_type_playback, NULL, &config, &playbackDevice) != MAL_SUCCESS) {
mal_context_uninit(&context);
printf("Failed to initialize playback device.\n");
return -4;
}
if (mal_device_start(&playbackDevice) != MAL_SUCCESS) {
mal_device_uninit(&playbackDevice);
mal_context_uninit(&context);
printf("Failed to start playback device.\n");
return -5;
}
printf("Press Enter to quit...\n");
getchar();
mal_device_uninit(&playbackDevice);
mal_context_uninit(&context);
return 0; return 0;
} }
\ No newline at end of file
This diff is collapsed.
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
#define MINI_AL_IMPLEMENTATION #define MINI_AL_IMPLEMENTATION
#include "../mini_al.h" #include "../mini_al.h"
#define DR_WAV_IMPLEMENTATION
#include "../extras/dr_wav.h"
void log_callback(mal_context* pContext, mal_device* pDevice, mal_uint32 logLevel, const char* message) void log_callback(mal_context* pContext, mal_device* pDevice, mal_uint32 logLevel, const char* message)
{ {
(void)pContext; (void)pContext;
...@@ -21,14 +24,31 @@ void data_callback(mal_device* pDevice, void* pOutput, const void* pInput, mal_u ...@@ -21,14 +24,31 @@ void data_callback(mal_device* pDevice, void* pOutput, const void* pInput, mal_u
{ {
/* In this test the format and channel count are the same for both input and output which means we can just memcpy(). */ /* In this test the format and channel count are the same for both input and output which means we can just memcpy(). */
mal_copy_memory(pOutput, pInput, frameCount * mal_get_bytes_per_frame(pDevice->format, pDevice->channels)); mal_copy_memory(pOutput, pInput, frameCount * mal_get_bytes_per_frame(pDevice->format, pDevice->channels));
/* Also write to a wav file for debugging. */
drwav* pWav = (drwav*)pDevice->pUserData;
mal_assert(pWav != NULL);
drwav_write_pcm_frames(pWav, frameCount, pInput);
} }
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
mal_result result; mal_result result;
(void)argc; drwav_data_format wavFormat;
(void)argv; wavFormat.container = drwav_container_riff;
wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT;
wavFormat.channels = 2;
wavFormat.sampleRate = 44100;
wavFormat.bitsPerSample = 32;
drwav wav;
if (drwav_init_file_write(&wav, "output.wav", &wavFormat) == DRWAV_FALSE) {
printf("Failed to initialize output file.\n");
return -1;
}
mal_backend backend = mal_backend_wasapi; mal_backend backend = mal_backend_wasapi;
...@@ -43,16 +63,18 @@ int main(int argc, char** argv) ...@@ -43,16 +63,18 @@ int main(int argc, char** argv)
} }
mal_device_config deviceConfig = mal_device_config_init(mal_device_type_duplex); mal_device_config deviceConfig = mal_device_config_init(mal_device_type_duplex);
deviceConfig.pPlaybackDeviceID = NULL; deviceConfig.capture.pDeviceID = NULL;
deviceConfig.format = mal_format_f32; deviceConfig.capture.format = mal_format_f32;
deviceConfig.channels = 2; deviceConfig.capture.channels = 2;
deviceConfig.sampleRate = 44100; deviceConfig.playback.pDeviceID = NULL;
deviceConfig.bufferSizeInMilliseconds = 50; deviceConfig.playback.format = mal_format_f32;
deviceConfig.periods = 3; deviceConfig.playback.channels = 2;
deviceConfig.shareMode = mal_share_mode_shared; deviceConfig.sampleRate = 44100;
deviceConfig.dataCallback = data_callback; deviceConfig.bufferSizeInMilliseconds = 1000;
deviceConfig.stopCallback = stop_callback; deviceConfig.periods = 3;
deviceConfig.pUserData = NULL; deviceConfig.dataCallback = data_callback;
deviceConfig.stopCallback = stop_callback;
deviceConfig.pUserData = &wav;
mal_device device; mal_device device;
result = mal_device_init(&context, &deviceConfig, &device); result = mal_device_init(&context, &deviceConfig, &device);
...@@ -66,5 +88,9 @@ int main(int argc, char** argv) ...@@ -66,5 +88,9 @@ int main(int argc, char** argv)
getchar(); getchar();
mal_device_uninit(&device); mal_device_uninit(&device);
drwav_uninit(&wav);
(void)argc;
(void)argv;
return 0; return 0;
} }
\ No newline at end of file
...@@ -272,11 +272,11 @@ ...@@ -272,11 +272,11 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\examples\simple_capture.c"> <ClCompile Include="..\examples\simple_capture.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'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\examples\simple_enumeration.c"> <ClCompile Include="..\examples\simple_enumeration.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
...@@ -319,7 +319,7 @@ ...@@ -319,7 +319,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="mal_duplex.c"> <ClCompile Include="mal_duplex.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
...@@ -359,12 +359,12 @@ ...@@ -359,12 +359,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="mal_test_0.c"> <ClCompile Include="mal_test_0.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">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="mal_test_0.cpp"> <ClCompile Include="mal_test_0.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
...@@ -386,6 +386,7 @@ ...@@ -386,6 +386,7 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="..\mini_al.h" /> <ClInclude Include="..\mini_al.h" />
<ClInclude Include="..\research\mal_resampler.h" /> <ClInclude Include="..\research\mal_resampler.h" />
<ClInclude Include="..\research\mal_ring_buffer.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
......
...@@ -68,5 +68,8 @@ ...@@ -68,5 +68,8 @@
<ClInclude Include="..\research\mal_resampler.h"> <ClInclude Include="..\research\mal_resampler.h">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\research\mal_ring_buffer.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>
\ No newline at end of file
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