From 32c64703dd79538323f0a14ca68901582dd76c4c Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 9 Feb 2019 17:39:49 +1000 Subject: [PATCH] Initial working implementation of full-duplex on WASAPI. --- examples/simple_capture.c | 124 ++++++--------- mini_al.h | 252 +++++++++++++++++++++++++------ tests/mal_duplex.c | 50 ++++-- tests/mal_test_0.vcxproj | 25 +-- tests/mal_test_0.vcxproj.filters | 3 + 5 files changed, 303 insertions(+), 151 deletions(-) diff --git a/examples/simple_capture.c b/examples/simple_capture.c index 5d0ad577..8554353a 100644 --- a/examples/simple_capture.c +++ b/examples/simple_capture.c @@ -1,106 +1,72 @@ -// This example simply captures data from your default microphone until you press Enter, after -// which it plays back the captured audio. +// 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. #define MINI_AL_IMPLEMENTATION #include "../mini_al.h" +#define DR_WAV_IMPLEMENTATION +#include "../extras/dr_wav.h" + #include #include - -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) +void data_callback(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; + + 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 (samplesToRead > capturedSampleCount-playbackSample) { - samplesToRead = capturedSampleCount-playbackSample; - } - - if (samplesToRead == 0) { - return; - } - - memcpy(pOutput, pCapturedSamples + playbackSample, samplesToRead * sizeof(mal_int16)); - playbackSample += samplesToRead; - - (void)pInput; -} - -int main() -{ - mal_device_config config; - - mal_context context; - if (mal_context_init(NULL, 0, NULL, &context) != MAL_SUCCESS) { - printf("Failed to initialize context."); + if (argc < 2) { + printf("No input file.\n"); return -1; } - printf("Recording...\n"); - config = mal_device_config_init(mal_format_s16, 2, 48000, on_recv_frames, NULL); - mal_device captureDevice; - if (mal_device_init(&context, mal_device_type_capture, NULL, &config, &captureDevice) != MAL_SUCCESS) { - mal_context_uninit(&context); + mal_result result; + + drwav_data_format wavFormat; + 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, argv[1], &wavFormat) == DRWAV_FALSE) { + printf("Failed to initialize output file.\n"); + return -1; + } + + mal_device_config config = mal_device_config_init(mal_device_type_capture); + config.capture.format = mal_format_f32; + config.capture.channels = wavFormat.channels; + config.sampleRate = wavFormat.sampleRate; + 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"); return -2; } - if (mal_device_start(&captureDevice) != MAL_SUCCESS) { - mal_device_uninit(&captureDevice); - mal_context_uninit(&context); - printf("Failed to start capture device.\n"); + result = mal_device_start(&device); + if (result != MAL_SUCCESS) { + mal_device_uninit(&device); + printf("Failed to start device.\n"); return -3; } printf("Press Enter to stop recording...\n"); 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; } \ No newline at end of file diff --git a/mini_al.h b/mini_al.h index 9be821eb..b3286ab9 100644 --- a/mini_al.h +++ b/mini_al.h @@ -2052,6 +2052,8 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device mal_uint32 internalBufferSizeInFrames; mal_uint32 internalPeriods; mal_pcm_converter converter; + mal_uint32 _dspFrameCount; // Internal use only. Used as the data source when reading from the device. + const mal_uint8* _dspFrames; // ^^^ AS ABOVE ^^^ } playback; struct { @@ -2072,6 +2074,8 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device mal_uint32 internalBufferSizeInFrames; mal_uint32 internalPeriods; mal_pcm_converter converter; + mal_uint32 _dspFrameCount; // Internal use only. Used as the data source when reading from the device. + const mal_uint8* _dspFrames; // ^^^ AS ABOVE ^^^ } capture; union @@ -4623,6 +4627,53 @@ mal_uint32 mal_device__on_read_from_device(mal_pcm_converter* pDSP, mal_uint32 f return framesToRead; } +/* The PCM converter callback for reading from a buffer. */ +mal_uint32 mal_device__pcm_converter__on_read_from_buffer_capture(mal_pcm_converter* pConverter, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + if (pDevice->capture._dspFrameCount == 0) { + return 0; // Nothing left. + } + + mal_uint32 framesToRead = frameCount; + if (framesToRead > pDevice->capture._dspFrameCount) { + framesToRead = pDevice->capture._dspFrameCount; + } + + mal_uint32 bytesToRead = framesToRead * mal_get_bytes_per_frame(pConverter->formatConverterIn.config.formatIn, pConverter->channelRouter.config.channelsIn); + mal_copy_memory(pFramesOut, pDevice->capture._dspFrames, bytesToRead); + pDevice->capture._dspFrameCount -= framesToRead; + pDevice->capture._dspFrames += bytesToRead; + + return framesToRead; +} + +mal_uint32 mal_device__pcm_converter__on_read_from_buffer_playback(mal_pcm_converter* pConverter, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + if (pDevice->playback._dspFrameCount == 0) { + return 0; // Nothing left. + } + + mal_uint32 framesToRead = frameCount; + if (framesToRead > pDevice->playback._dspFrameCount) { + framesToRead = pDevice->playback._dspFrameCount; + } + + mal_uint32 bytesToRead = framesToRead * mal_get_bytes_per_frame(pConverter->formatConverterIn.config.formatIn, pConverter->channelRouter.config.channelsIn); + mal_copy_memory(pFramesOut, pDevice->playback._dspFrames, bytesToRead); + pDevice->playback._dspFrameCount -= framesToRead; + pDevice->playback._dspFrames += bytesToRead; + + return framesToRead; +} + + + // A helper function for reading sample data from the client. Returns the number of samples read from the client. Remaining samples // are filled with silence. static MAL_INLINE mal_uint32 mal_device__read_frames_from_client(mal_device* pDevice, mal_uint32 frameCount, void* pSamples) @@ -7156,10 +7207,10 @@ mal_result mal_device_init__wasapi(mal_context* pContext, const mal_device_confi if (pConfig->deviceType == mal_device_type_capture || pConfig->deviceType == mal_device_type_duplex) { mal_device_init_internal_data__wasapi data; - data.formatIn = pConfig->format; - data.channelsIn = pConfig->channels; + data.formatIn = pConfig->capture.format; + data.channelsIn = pConfig->capture.channels; data.sampleRateIn = pConfig->sampleRate; - mal_copy_memory(data.channelMapIn, pConfig->channelMap, sizeof(pConfig->channelMap)); + mal_copy_memory(data.channelMapIn, pConfig->capture.channelMap, sizeof(pConfig->capture.channelMap)); data.usingDefaultFormat = pDevice->usingDefaultFormat; data.usingDefaultChannels = pDevice->usingDefaultChannels; data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; @@ -7207,25 +7258,18 @@ mal_result mal_device_init__wasapi(mal_context* pContext, const mal_device_confi if (pConfig->deviceType == mal_device_type_playback || pConfig->deviceType == mal_device_type_duplex) { mal_device_init_internal_data__wasapi data; - data.formatIn = pConfig->format; - data.channelsIn = pConfig->channels; - data.sampleRateIn = pConfig->sampleRate; + data.formatIn = pConfig->playback.format; + data.channelsIn = pConfig->playback.channels; + data.sampleRateIn = pConfig->sampleRate; mal_copy_memory(data.channelMapIn, pConfig->channelMap, sizeof(pConfig->channelMap)); - data.usingDefaultFormat = pDevice->usingDefaultFormat; - data.usingDefaultChannels = pDevice->usingDefaultChannels; - data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; - data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; - data.shareMode = pConfig->playback.shareMode; - - // In duplex mode we want the playback device to have the same buffer size and period count as the capture device. - if (pConfig->deviceType == mal_device_type_duplex) { - data.bufferSizeInFramesIn = pDevice->capture.internalBufferSizeInFrames; - data.periodsIn = pDevice->capture.internalPeriods; - } else { - data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames; - data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds; - data.periodsIn = pConfig->periods; - } + data.usingDefaultFormat = pDevice->usingDefaultFormat; + data.usingDefaultChannels = pDevice->usingDefaultChannels; + data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; + data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; + data.shareMode = pConfig->playback.shareMode; + data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames; + data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds; + data.periodsIn = pConfig->periods; result = mal_device_init_internal__wasapi(pDevice->pContext, mal_device_type_playback, pConfig->playback.pDeviceID, &data); if (result != MAL_SUCCESS) { @@ -7446,7 +7490,7 @@ mal_result mal_device__get_available_frames__wasapi(mal_device* pDevice, mal_IAu *pFrameCount = paddingFramesCount; } else { if ((mal_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->bufferSizeInFrames - paddingFramesCount; + *pFrameCount = (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods) - paddingFramesCount; } else { *pFrameCount = paddingFramesCount; } @@ -8616,11 +8660,6 @@ mal_result mal_device_init__dsound(mal_context* pContext, const mal_device_confi mal_assert(pDevice != NULL); mal_zero_object(&pDevice->dsound); - /* Full-duplex is not yet implemented. */ - if (pConfig->deviceType == mal_device_type_duplex) { - return MAL_INVALID_ARGS; - } - /* DirectSound should use a latency of about 20ms per period for low latency mode. */ if (pDevice->usingDefaultBufferSize) { if (pConfig->performanceProfile == mal_performance_profile_low_latency) { @@ -9717,11 +9756,6 @@ mal_result mal_device_init__winmm(mal_context* pContext, const mal_device_config mal_assert(pDevice != NULL); mal_zero_object(&pDevice->winmm); - /* Full-duplex is not yet implemented. */ - if (pConfig->deviceType == mal_device_type_duplex) { - return MAL_INVALID_ARGS; - } - /* No exlusive mode with WinMM. */ if (((pConfig->deviceType == mal_device_type_playback || pConfig->deviceType == mal_device_type_duplex) && pConfig->playback.shareMode == mal_share_mode_exclusive) || ((pConfig->deviceType == mal_device_type_capture || pConfig->deviceType == mal_device_type_duplex) && pConfig->capture.shareMode == mal_share_mode_exclusive)) { @@ -20657,6 +20691,50 @@ void mal_device__post_init_setup(mal_device* pDevice, mal_device_type deviceType dspConfig.onRead = mal_device__on_read_from_device; mal_pcm_converter_init(&dspConfig, &pDevice->dsp); } + + + /* PCM converters. */ + if (deviceType == mal_device_type_capture || deviceType == mal_device_type_duplex) { + /* Converting from internal device format to public format. */ + mal_pcm_converter_config converterConfig = mal_pcm_converter_config_init_new(); + converterConfig.neverConsumeEndOfInput = MAL_TRUE; + converterConfig.pUserData = pDevice; + converterConfig.formatIn = pDevice->capture.internalFormat; + converterConfig.channelsIn = pDevice->capture.internalChannels; + converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; + mal_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, pDevice->capture.internalChannels); + converterConfig.formatOut = pDevice->capture.format; + converterConfig.channelsOut = pDevice->capture.channels; + converterConfig.sampleRateOut = pDevice->sampleRate; + mal_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, pDevice->capture.channels); + if (deviceType == mal_device_type_capture) { + converterConfig.onRead = mal_device__on_read_from_device; + } else { + converterConfig.onRead = mal_device__pcm_converter__on_read_from_buffer_capture; + } + mal_pcm_converter_init(&converterConfig, &pDevice->capture.converter); + } + + if (deviceType == mal_device_type_playback || deviceType == mal_device_type_duplex) { + /* Converting from public format to device format. */ + mal_pcm_converter_config converterConfig = mal_pcm_converter_config_init_new(); + converterConfig.neverConsumeEndOfInput = MAL_TRUE; + converterConfig.pUserData = pDevice; + converterConfig.formatIn = pDevice->playback.format; + converterConfig.channelsIn = pDevice->playback.channels; + converterConfig.sampleRateIn = pDevice->sampleRate; + mal_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, pDevice->playback.channels); + converterConfig.formatOut = pDevice->playback.internalFormat; + converterConfig.channelsOut = pDevice->playback.internalChannels; + converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; + mal_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, pDevice->playback.internalChannels); + if (deviceType == mal_device_type_playback) { + converterConfig.onRead = mal_device__on_read_from_client; + } else { + converterConfig.onRead = mal_device__pcm_converter__on_read_from_buffer_playback; + } + mal_pcm_converter_init(&converterConfig, &pDevice->playback.converter); + } } @@ -20700,7 +20778,13 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) (pDevice->type == mal_device_type_duplex && pDevice->pContext->onDeviceWrite != NULL && pDevice->pContext->onDeviceRead != NULL) ); - mal_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; + mal_uint32 periodSizeInFrames; + if (pDevice->type == mal_device_type_capture || pDevice->type == mal_device_type_duplex) { + periodSizeInFrames = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods; + } else { + periodSizeInFrames = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods; + } + /* With the blocking API, the device is started automatically in read()/write(). All we need to do is enter the loop and just keep reading @@ -20718,25 +20802,97 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) while (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { mal_result result = MAL_SUCCESS; mal_uint32 totalFramesProcessed = 0; - mal_uint8 buffer[4096]; - mal_uint32 bufferSizeInFrames = sizeof(buffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); - while (totalFramesProcessed < periodSizeInFrames) { - mal_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed; - mal_uint32 framesToProcess = framesRemaining; - if (framesToProcess > bufferSizeInFrames) { - framesToProcess = bufferSizeInFrames; + if (pDevice->type == mal_device_type_duplex) { + /* The process is device_read -> convert -> callback -> convert -> device_write. */ + mal_uint8 captureDeviceData[4096]; + mal_uint32 captureDeviceDataCapInFrames = sizeof(captureDeviceData) / mal_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + + while (totalFramesProcessed < periodSizeInFrames) { + mal_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed; + mal_uint32 framesToProcess = framesRemaining; + if (framesToProcess > captureDeviceDataCapInFrames) { + framesToProcess = captureDeviceDataCapInFrames; + } + + result = pDevice->pContext->onDeviceRead(pDevice, captureDeviceData, framesToProcess); + if (result != MAL_SUCCESS) { + break; + } + + mal_device_callback_proc onData = pDevice->onData; + if (onData != NULL) { + pDevice->capture._dspFrameCount = framesToProcess; + pDevice->capture._dspFrames = captureDeviceData; + + /* We need to process every input frame. */ + for (;;) { + mal_uint8 capturedData[4096]; // In capture.format/channels format + mal_uint8 playbackData[4096]; // In playback.format/channels format + + mal_uint32 capturedDataCapInFrames = sizeof(capturedData) / mal_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + mal_uint32 playbackDataCapInFrames = sizeof(playbackData) / mal_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + + mal_uint32 capturedFramesToTryProcessing = mal_min(capturedDataCapInFrames, playbackDataCapInFrames); + mal_uint32 capturedFramesToProcess = (mal_uint32)mal_pcm_converter_read(&pDevice->capture.converter, capturedFramesToTryProcessing, capturedData, pDevice->capture.converter.pUserData); + if (capturedFramesToProcess == 0) { + break; /* Don't fire the data callback with zero frames. */ + } + + onData(pDevice, playbackData, capturedData, capturedFramesToProcess); + + /* At this point the playbackData buffer should be holding data that needs to be written to the device. */ + pDevice->playback._dspFrameCount = capturedFramesToProcess; + pDevice->playback._dspFrames = playbackData; + for (;;) { + mal_uint8 playbackDeviceData[4096]; + + mal_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / mal_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + mal_uint32 playbackDeviceFramesCount = (mal_uint32)mal_pcm_converter_read(&pDevice->playback.converter, playbackDeviceDataCapInFrames, playbackDeviceData, pDevice->playback.converter.pUserData); + if (playbackDeviceFramesCount == 0) { + break; + } + + result = pDevice->pContext->onDeviceWrite(pDevice, playbackDeviceData, playbackDeviceFramesCount); + if (result != MAL_SUCCESS) { + break; + } + } + + if (capturedFramesToProcess < capturedFramesToTryProcessing) { + break; + } + + /* In case an error happened from onDeviceWrite()... */ + if (result != MAL_SUCCESS) { + break; + } + } + } + + totalFramesProcessed += framesToProcess; } + } else { + mal_uint8 buffer[4096]; + mal_uint32 bufferSizeInFrames = sizeof(buffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); - if (pDevice->type == mal_device_type_playback) { - mal_device__read_frames_from_client(pDevice, framesToProcess, buffer); - result = pDevice->pContext->onDeviceWrite(pDevice, buffer, framesToProcess); - } else { - result = pDevice->pContext->onDeviceRead(pDevice, buffer, framesToProcess); - mal_device__send_frames_to_client(pDevice, framesToProcess, buffer); + while (totalFramesProcessed < periodSizeInFrames) { + mal_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed; + mal_uint32 framesToProcess = framesRemaining; + if (framesToProcess > bufferSizeInFrames) { + framesToProcess = bufferSizeInFrames; + } + + if (pDevice->type == mal_device_type_playback) { + mal_device__read_frames_from_client(pDevice, framesToProcess, buffer); + result = pDevice->pContext->onDeviceWrite(pDevice, buffer, framesToProcess); + } else { + result = pDevice->pContext->onDeviceRead(pDevice, buffer, framesToProcess); + mal_device__send_frames_to_client(pDevice, framesToProcess, buffer); + } + + totalFramesProcessed += framesToProcess; } - - totalFramesProcessed += framesToProcess; } /* Get out of the loop if read()/write() returned an error. It probably means the device has been stopped. */ diff --git a/tests/mal_duplex.c b/tests/mal_duplex.c index e6cf1d7a..05ded139 100644 --- a/tests/mal_duplex.c +++ b/tests/mal_duplex.c @@ -3,6 +3,9 @@ #define MINI_AL_IMPLEMENTATION #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)pContext; @@ -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(). */ 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) { mal_result result; - (void)argc; - (void)argv; + drwav_data_format wavFormat; + 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; @@ -43,16 +63,18 @@ int main(int argc, char** argv) } mal_device_config deviceConfig = mal_device_config_init(mal_device_type_duplex); - deviceConfig.pPlaybackDeviceID = NULL; - deviceConfig.format = mal_format_f32; - deviceConfig.channels = 2; - deviceConfig.sampleRate = 44100; - deviceConfig.bufferSizeInMilliseconds = 50; - deviceConfig.periods = 3; - deviceConfig.shareMode = mal_share_mode_shared; - deviceConfig.dataCallback = data_callback; - deviceConfig.stopCallback = stop_callback; - deviceConfig.pUserData = NULL; + deviceConfig.capture.pDeviceID = NULL; + deviceConfig.capture.format = mal_format_f32; + deviceConfig.capture.channels = 2; + deviceConfig.playback.pDeviceID = NULL; + deviceConfig.playback.format = mal_format_f32; + deviceConfig.playback.channels = 2; + deviceConfig.sampleRate = 44100; + deviceConfig.bufferSizeInMilliseconds = 1000; + deviceConfig.periods = 3; + deviceConfig.dataCallback = data_callback; + deviceConfig.stopCallback = stop_callback; + deviceConfig.pUserData = &wav; mal_device device; result = mal_device_init(&context, &deviceConfig, &device); @@ -66,5 +88,9 @@ int main(int argc, char** argv) getchar(); mal_device_uninit(&device); + drwav_uninit(&wav); + + (void)argc; + (void)argv; return 0; } \ No newline at end of file diff --git a/tests/mal_test_0.vcxproj b/tests/mal_test_0.vcxproj index 891c4f9d..ffe67adb 100644 --- a/tests/mal_test_0.vcxproj +++ b/tests/mal_test_0.vcxproj @@ -272,11 +272,11 @@ true - true - true - true - true - true + false + false + false + false + false true @@ -319,7 +319,7 @@ true - true + false true true true @@ -359,12 +359,12 @@ true - false - false - false - false - false - false + true + true + true + true + true + true true @@ -386,6 +386,7 @@ + diff --git a/tests/mal_test_0.vcxproj.filters b/tests/mal_test_0.vcxproj.filters index f955da89..20c33555 100644 --- a/tests/mal_test_0.vcxproj.filters +++ b/tests/mal_test_0.vcxproj.filters @@ -68,5 +68,8 @@ Source Files + + Source Files + \ No newline at end of file