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,
......
...@@ -1388,15 +1388,52 @@ typedef struct ...@@ -1388,15 +1388,52 @@ typedef struct
} mal_event; } mal_event;
typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, mal_uint32 logLevel, const char* message); /*
typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples); The callback for processing audio data from the device.
typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples);
typedef void (* mal_stop_proc)(mal_device* pDevice); pInput is a pointer to a buffer containing input data from the device. This will be non-null for a capture or full-duplex device, and
null for a playback device.
pOutput is a pointer to a buffer that will receive audio data that will later be played back through the speakers. This will be non-null
for a playback or full-duplex device and null for a capture device.
frameCount is the number of PCM frames to process. If an output buffer is provided (pOutput is not null), applications should write out
to the entire output buffer.
Do _not_ call any mini_al APIs from the callback. Attempting the stop the device can result in a deadlock. The proper way to stop the
device is to call mal_device_stop() from a different thread, normally the main application thread.
*/
typedef void (* mal_device_callback_proc)(mal_device* pDevice, const void* pInput, void* pOutput, mal_uint32 frameCount);
/*
The callback for when the device has been stopped.
This will be called when the device is stopped explicitly with mal_device_stop() and also called implicitly when the device is stopped
through external forces such as being unplugged or an internal error occuring.
Do not restart the device from the callback.
*/
typedef void (* mal_stop_proc)(mal_device* pDevice);
/*
The callback for handling log messages.
It is possible for pDevice to be null in which case the log originated from the context. If it is non-null you can assume the message
came from the device.
logLevel is one of the following:
MAL_LOG_LEVEL_VERBOSE
MAL_LOG_LEVEL_INFO
MAL_LOG_LEVEL_WARNING
MAL_LOG_LEVEL_ERROR
*/
typedef void (* mal_log_proc)(mal_context* pContext, mal_device* pDevice, mal_uint32 logLevel, const char* message);
typedef enum typedef enum
{ {
mal_device_type_playback, mal_device_type_playback = 1,
mal_device_type_capture mal_device_type_capture = 2,
mal_device_type_duplex = mal_device_type_playback | mal_device_type_capture,
} mal_device_type; } mal_device_type;
typedef enum typedef enum
...@@ -1491,8 +1528,7 @@ typedef struct ...@@ -1491,8 +1528,7 @@ typedef struct
mal_uint32 periods; mal_uint32 periods;
mal_share_mode shareMode; mal_share_mode shareMode;
mal_performance_profile performanceProfile; mal_performance_profile performanceProfile;
mal_recv_proc onRecvCallback; mal_device_callback_proc onDataCallback;
mal_send_proc onSendCallback;
mal_stop_proc onStopCallback; mal_stop_proc onStopCallback;
void* pUserData; void* pUserData;
struct struct
...@@ -1901,8 +1937,7 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device ...@@ -1901,8 +1937,7 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
mal_uint32 bufferSizeInMilliseconds; mal_uint32 bufferSizeInMilliseconds;
mal_uint32 periods; mal_uint32 periods;
mal_uint32 state; mal_uint32 state;
mal_recv_proc onRecv; mal_device_callback_proc onData;
mal_send_proc onSend;
mal_stop_proc onStop; mal_stop_proc onStop;
void* pUserData; // Application defined data. void* pUserData; // Application defined data.
char name[256]; char name[256];
...@@ -2372,9 +2407,7 @@ mal_context_config mal_context_config_init(mal_log_proc onLog); ...@@ -2372,9 +2407,7 @@ mal_context_config mal_context_config_init(mal_log_proc onLog);
// //
// mal_device_config_init(), mal_device_config_init_playback(), etc. will allow you to explicitly set the sample format, // mal_device_config_init(), mal_device_config_init_playback(), etc. will allow you to explicitly set the sample format,
// channel count, etc. // channel count, etc.
mal_device_config mal_device_config_init_default(void* pUserData); mal_device_config mal_device_config_init_default(mal_device_callback_proc onDataCallback, void* pUserData);
mal_device_config mal_device_config_init_default_capture(mal_recv_proc onRecvCallback, void* pUserData);
mal_device_config mal_device_config_init_default_playback(mal_send_proc onSendCallback, void* pUserData);
// Helper function for initializing a mal_device_config object. // Helper function for initializing a mal_device_config object.
// //
...@@ -2439,19 +2472,10 @@ mal_device_config mal_device_config_init_default_playback(mal_send_proc onSendCa ...@@ -2439,19 +2472,10 @@ mal_device_config mal_device_config_init_default_playback(mal_send_proc onSendCa
// //
// Efficiency: HIGH // Efficiency: HIGH
// This just returns a stack allocated object and consists of just a few assignments. // This just returns a stack allocated object and consists of just a few assignments.
mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_recv_proc onRecvCallback, mal_send_proc onSendCallback, void* pUserData); mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_device_callback_proc onDataCallback, void* pUserData);
// A simplified version of mal_device_config_init_ex(). // A simplified version of mal_device_config_init_ex().
static MAL_INLINE mal_device_config mal_device_config_init(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback, mal_send_proc onSendCallback, void* pUserData) { return mal_device_config_init_ex(format, channels, sampleRate, NULL, onRecvCallback, onSendCallback, pUserData); } static MAL_INLINE mal_device_config mal_device_config_init(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_device_callback_proc onDataCallback, void* pUserData) { return mal_device_config_init_ex(format, channels, sampleRate, NULL, onDataCallback, pUserData); }
// A simplified version of mal_device_config_init() for capture devices.
static MAL_INLINE mal_device_config mal_device_config_init_capture_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_recv_proc onRecvCallback, void* pUserData) { return mal_device_config_init_ex(format, channels, sampleRate, channelMap, onRecvCallback, NULL, pUserData); }
static MAL_INLINE mal_device_config mal_device_config_init_capture(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback, void* pUserData) { return mal_device_config_init_capture_ex(format, channels, sampleRate, NULL, onRecvCallback, pUserData); }
// A simplified version of mal_device_config_init() for playback devices.
static MAL_INLINE mal_device_config mal_device_config_init_playback_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_send_proc onSendCallback, void* pUserData) { return mal_device_config_init_ex(format, channels, sampleRate, channelMap, NULL, onSendCallback, pUserData); }
static MAL_INLINE mal_device_config mal_device_config_init_playback(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_send_proc onSendCallback, void* pUserData) { return mal_device_config_init_playback_ex(format, channels, sampleRate, NULL, onSendCallback, pUserData); }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
...@@ -4479,9 +4503,10 @@ mal_uint32 mal_device__on_read_from_client(mal_pcm_converter* pDSP, mal_uint32 f ...@@ -4479,9 +4503,10 @@ mal_uint32 mal_device__on_read_from_client(mal_pcm_converter* pDSP, mal_uint32 f
mal_device* pDevice = (mal_device*)pUserData; mal_device* pDevice = (mal_device*)pUserData;
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
mal_send_proc onSend = pDevice->onSend; mal_device_callback_proc onData = pDevice->onData;
if (onSend) { if (onData) {
return onSend(pDevice, frameCount, pFramesOut); onData(pDevice, NULL, pFramesOut, frameCount);
return frameCount;
} }
return 0; return 0;
...@@ -4504,7 +4529,7 @@ mal_uint32 mal_device__on_read_from_device(mal_pcm_converter* pDSP, mal_uint32 f ...@@ -4504,7 +4529,7 @@ mal_uint32 mal_device__on_read_from_device(mal_pcm_converter* pDSP, mal_uint32 f
framesToRead = pDevice->_dspFrameCount; framesToRead = pDevice->_dspFrameCount;
} }
mal_uint32 bytesToRead = framesToRead * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); mal_uint32 bytesToRead = framesToRead * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
mal_copy_memory(pFramesOut, pDevice->_dspFrames, bytesToRead); mal_copy_memory(pFramesOut, pDevice->_dspFrames, bytesToRead);
pDevice->_dspFrameCount -= framesToRead; pDevice->_dspFrameCount -= framesToRead;
pDevice->_dspFrames += bytesToRead; pDevice->_dspFrames += bytesToRead;
...@@ -4537,8 +4562,8 @@ static MAL_INLINE void mal_device__send_frames_to_client(mal_device* pDevice, ma ...@@ -4537,8 +4562,8 @@ static MAL_INLINE void mal_device__send_frames_to_client(mal_device* pDevice, ma
mal_assert(frameCount > 0); mal_assert(frameCount > 0);
mal_assert(pSamples != NULL); mal_assert(pSamples != NULL);
mal_recv_proc onRecv = pDevice->onRecv; mal_device_callback_proc onData = pDevice->onData;
if (onRecv) { if (onData) {
pDevice->_dspFrameCount = frameCount; pDevice->_dspFrameCount = frameCount;
pDevice->_dspFrames = (const mal_uint8*)pSamples; pDevice->_dspFrames = (const mal_uint8*)pSamples;
...@@ -4551,7 +4576,7 @@ static MAL_INLINE void mal_device__send_frames_to_client(mal_device* pDevice, ma ...@@ -4551,7 +4576,7 @@ static MAL_INLINE void mal_device__send_frames_to_client(mal_device* pDevice, ma
break; break;
} }
onRecv(pDevice, framesJustRead, chunkBuffer); onData(pDevice, chunkBuffer, NULL, framesJustRead);
if (framesJustRead < chunkFrameCount) { if (framesJustRead < chunkFrameCount) {
break; break;
...@@ -4575,7 +4600,7 @@ static MAL_INLINE mal_uint32 mal_device__get_state(mal_device* pDevice) ...@@ -4575,7 +4600,7 @@ static MAL_INLINE mal_uint32 mal_device__get_state(mal_device* pDevice)
/* A helper for determining whether or not the device is running in async mode. */ /* A helper for determining whether or not the device is running in async mode. */
static MAL_INLINE mal_bool32 mal_device__is_async(mal_device* pDevice) static MAL_INLINE mal_bool32 mal_device__is_async(mal_device* pDevice)
{ {
return pDevice->onRecv != NULL || pDevice->onSend != NULL; return pDevice->onData != NULL;
} }
...@@ -7075,11 +7100,19 @@ mal_result mal_device_stop__wasapi(mal_device* pDevice) ...@@ -7075,11 +7100,19 @@ mal_result mal_device_stop__wasapi(mal_device* pDevice)
return MAL_DEVICE_NOT_INITIALIZED; return MAL_DEVICE_NOT_INITIALIZED;
} }
/* TODO: Wait until every sample that was written by the callback has been processed. */
HRESULT hr = mal_IAudioClient_Stop((mal_IAudioClient*)pDevice->wasapi.pAudioClient); HRESULT hr = mal_IAudioClient_Stop((mal_IAudioClient*)pDevice->wasapi.pAudioClient);
if (FAILED(hr)) { if (FAILED(hr)) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
} }
/* The audio client needs to be reset otherwise restarting will fail. */
hr = mal_IAudioClient_Reset((mal_IAudioClient*)pDevice->wasapi.pAudioClient);
if (FAILED(hr)) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
}
mal_atomic_exchange_32(&pDevice->wasapi.isStarted, MAL_FALSE); mal_atomic_exchange_32(&pDevice->wasapi.isStarted, MAL_FALSE);
return MAL_SUCCESS; return MAL_SUCCESS;
} }
...@@ -8470,6 +8503,10 @@ mal_result mal_device_stop__dsound(mal_device* pDevice) ...@@ -8470,6 +8503,10 @@ mal_result mal_device_stop__dsound(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
if (!pDevice->dsound.isStarted) {
return MAL_DEVICE_NOT_STARTED;
}
if (pDevice->type == mal_device_type_playback) { if (pDevice->type == mal_device_type_playback) {
if (FAILED(mal_IDirectSoundBuffer_Stop((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer))) { if (FAILED(mal_IDirectSoundBuffer_Stop((mal_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer))) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
...@@ -12358,14 +12395,13 @@ mal_pa_channel_position_t mal_channel_position_to_pulse(mal_channel position) ...@@ -12358,14 +12395,13 @@ mal_pa_channel_position_t mal_channel_position_to_pulse(mal_channel position)
} }
#endif #endif
mal_result mal_wait_for_operation__pulse(mal_context* pContext, mal_pa_mainloop* pMainLoop, mal_pa_operation* pOP) mal_result mal_wait_for_operation__pulse(mal_context* pContext, mal_pa_mainloop* pMainLoop, mal_pa_operation* pOP)
{ {
mal_assert(pContext != NULL); mal_assert(pContext != NULL);
mal_assert(pMainLoop != NULL); mal_assert(pMainLoop != NULL);
mal_assert(pOP != NULL); mal_assert(pOP != NULL);
while (((mal_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP) != MAL_PA_OPERATION_DONE) { while (((mal_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP) == MAL_PA_OPERATION_RUNNING) {
int error = ((mal_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL); int error = ((mal_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
if (error < 0) { if (error < 0) {
return mal_result_from_pulse(error); return mal_result_from_pulse(error);
...@@ -12698,6 +12734,11 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes, ...@@ -12698,6 +12734,11 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
printf("[PulseAudio] write_callback: sizeInBytes=%d\n", (int)sizeInBytes); printf("[PulseAudio] write_callback: sizeInBytes=%d\n", (int)sizeInBytes);
#endif #endif
/* Don't do anything if the device is stopping. Not doing this will result in draining never completing. */
if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) {
return;
}
size_t bytesRemaining = sizeInBytes; size_t bytesRemaining = sizeInBytes;
while (bytesRemaining > 0) { while (bytesRemaining > 0) {
size_t bytesToReadFromClient = bytesRemaining; size_t bytesToReadFromClient = bytesRemaining;
...@@ -12755,6 +12796,11 @@ void mal_pulse_device_read_callback(mal_pa_stream* pStream, size_t sizeInBytes, ...@@ -12755,6 +12796,11 @@ void mal_pulse_device_read_callback(mal_pa_stream* pStream, size_t sizeInBytes,
mal_context* pContext = pDevice->pContext; mal_context* pContext = pDevice->pContext;
mal_assert(pContext != NULL); mal_assert(pContext != NULL);
/* Don't do anything if the device is stopping. */
if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) {
return;
}
size_t bytesRemaining = sizeInBytes; size_t bytesRemaining = sizeInBytes;
while (bytesRemaining > 0) { while (bytesRemaining > 0) {
size_t bytesToSendToClient = bytesRemaining; size_t bytesToSendToClient = bytesRemaining;
...@@ -13190,13 +13236,20 @@ mal_result mal_device_start__pulse(mal_device* pDevice) ...@@ -13190,13 +13236,20 @@ mal_result mal_device_start__pulse(mal_device* pDevice)
mal_result mal_device_stop__pulse(mal_device* pDevice) mal_result mal_device_stop__pulse(mal_device* pDevice)
{ {
mal_result result; mal_result result;
mal_bool32 wasSuccessful;
mal_pa_operation* pOP;
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
/* /* The stream needs to be drained if it's a playback device. */
Corking the device without flushing or draining should satisfy the requirements of mal_device_stop() which is that it must not if (pDevice->type == mal_device_type_playback) {
drop unprocessed samples. pOP = ((mal_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((mal_pa_stream*)pDevice->pulse.pStream, mal_pulse_operation_complete_callback, &wasSuccessful);
*/ if (pOP != NULL) {
mal_device__wait_for_operation__pulse(pDevice, pOP);
((mal_pa_operation_unref_proc)pDevice->pContext->pulse.pa_operation_unref)(pOP);
}
}
result = mal_device__cork_stream__pulse(pDevice, 1); result = mal_device__cork_stream__pulse(pDevice, 1);
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
return result; return result;
...@@ -13869,7 +13922,6 @@ mal_result mal_device_stop__jack(mal_device* pDevice) ...@@ -13869,7 +13922,6 @@ mal_result mal_device_stop__jack(mal_device* pDevice)
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MAL_ERROR); return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MAL_ERROR);
} }
mal_device__set_state(pDevice, MAL_STATE_STOPPED);
mal_stop_proc onStop = pDevice->onStop; mal_stop_proc onStop = pDevice->onStop;
if (onStop) { if (onStop) {
onStop(pDevice); onStop(pDevice);
...@@ -15328,7 +15380,7 @@ OSStatus mal_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* p ...@@ -15328,7 +15380,7 @@ OSStatus mal_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* p
mal_device* pDevice = (mal_device*)pUserData; mal_device* pDevice = (mal_device*)pUserData;
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
#if defined(MAL_DEBUG_OUTPUT) #if defined(MAL_DEBUG_OUTPUT)
printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers); printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
#endif #endif
...@@ -18842,18 +18894,16 @@ void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueueItf pBu ...@@ -18842,18 +18894,16 @@ void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueueItf pBu
// For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like // For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
// OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, // OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
// but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( // but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
/* Don't do anything if the device is not started. */
if (pDevice->state != MAL_STATE_STARTED) { if (pDevice->state != MAL_STATE_STARTED) {
return; return;
} }
size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
mal_uint8* pBuffer = pDevice->opensl.pBuffer + (pDevice->opensl.currentBufferIndex * periodSizeInBytes); mal_uint8* pBuffer = pDevice->opensl.pBuffer + (pDevice->opensl.currentBufferIndex * periodSizeInBytes);
if (pDevice->type == mal_device_type_playback) { if (pDevice->type == mal_device_type_playback) {
if (pDevice->state != MAL_STATE_STARTED) {
return;
}
mal_device__read_frames_from_client(pDevice, pDevice->opensl.periodSizeInFrames, pBuffer); mal_device__read_frames_from_client(pDevice, pDevice->opensl.periodSizeInFrames, pBuffer);
SLresult resultSL = MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, pBuffer, periodSizeInBytes); SLresult resultSL = MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, pBuffer, periodSizeInBytes);
...@@ -19227,6 +19277,8 @@ mal_result mal_device_stop__opensl(mal_device* pDevice) ...@@ -19227,6 +19277,8 @@ mal_result mal_device_stop__opensl(mal_device* pDevice)
return MAL_INVALID_OPERATION; return MAL_INVALID_OPERATION;
} }
/* TODO: Wait until all buffers have been processed. Hint: Maybe SLAndroidSimpleBufferQueue::GetState() could be used in a loop? */
if (pDevice->type == mal_device_type_playback) { if (pDevice->type == mal_device_type_playback) {
SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
if (resultSL != SL_RESULT_SUCCESS) { if (resultSL != SL_RESULT_SUCCESS) {
...@@ -20520,7 +20572,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi ...@@ -20520,7 +20572,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
// The config is allowed to be NULL, in which case we default to mal_device_config_init_default(). // The config is allowed to be NULL, in which case we default to mal_device_config_init_default().
mal_device_config config; mal_device_config config;
if (pConfig == NULL) { if (pConfig == NULL) {
config = mal_device_config_init_default(NULL); config = mal_device_config_init_default(NULL, NULL);
} else { } else {
config = *pConfig; config = *pConfig;
} }
...@@ -20540,9 +20592,8 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi ...@@ -20540,9 +20592,8 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
// Set the user data and log callback ASAP to ensure it is available for the entire initialization process. // Set the user data and log callback ASAP to ensure it is available for the entire initialization process.
pDevice->pUserData = config.pUserData; pDevice->pUserData = config.pUserData;
pDevice->onData = config.onDataCallback;
pDevice->onStop = config.onStopCallback; pDevice->onStop = config.onStopCallback;
pDevice->onSend = config.onSendCallback;
pDevice->onRecv = config.onRecvCallback;
if (((size_t)pDevice % sizeof(pDevice)) != 0) { if (((size_t)pDevice % sizeof(pDevice)) != 0) {
if (pContext->config.onLog) { if (pContext->config.onLog) {
...@@ -20780,11 +20831,6 @@ mal_result mal_device_write(mal_device* pDevice, const void* pPCMFrames, mal_uin ...@@ -20780,11 +20831,6 @@ mal_result mal_device_write(mal_device* pDevice, const void* pPCMFrames, mal_uin
return MAL_INVALID_ARGS; return MAL_INVALID_ARGS;
} }
/* Not allowed to call this in asynchronous mode. */
if (pDevice->onRecv != NULL || pDevice->onSend != NULL) {
return MAL_INVALID_OPERATION;
}
/* Backend must supporting synchronous writes. */ /* Backend must supporting synchronous writes. */
if (pDevice->pContext->onDeviceWrite == NULL) { if (pDevice->pContext->onDeviceWrite == NULL) {
return MAL_INVALID_OPERATION; return MAL_INVALID_OPERATION;
...@@ -21012,41 +21058,22 @@ mal_context_config mal_context_config_init(mal_log_proc onLog) ...@@ -21012,41 +21058,22 @@ mal_context_config mal_context_config_init(mal_log_proc onLog)
} }
mal_device_config mal_device_config_init_default(void* pUserData) mal_device_config mal_device_config_init_default(mal_device_callback_proc onDataCallback, void* pUserData)
{ {
mal_device_config config; mal_device_config config;
mal_zero_object(&config); mal_zero_object(&config);
config.onDataCallback = onDataCallback;
config.pUserData = pUserData; config.pUserData = pUserData;
return config; return config;
} }
mal_device_config mal_device_config_init_default_capture(mal_recv_proc onRecvCallback, void* pUserData) mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_device_callback_proc onDataCallback, void* pUserData)
{ {
mal_device_config config = mal_device_config_init_default(pUserData); mal_device_config config = mal_device_config_init_default(onDataCallback, pUserData);
config.onRecvCallback = onRecvCallback;
return config;
}
mal_device_config mal_device_config_init_default_playback(mal_send_proc onSendCallback, void* pUserData)
{
mal_device_config config = mal_device_config_init_default(pUserData);
config.onSendCallback = onSendCallback;
return config;
}
mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_channel channelMap[MAL_MAX_CHANNELS], mal_recv_proc onRecvCallback, mal_send_proc onSendCallback, void* pUserData)
{
mal_device_config config = mal_device_config_init_default(pUserData);
config.format = format; config.format = format;
config.channels = channels; config.channels = channels;
config.sampleRate = sampleRate; config.sampleRate = sampleRate;
config.onRecvCallback = onRecvCallback;
config.onSendCallback = onSendCallback;
if (channels > 0) { if (channels > 0) {
if (channelMap == NULL) { if (channelMap == NULL) {
...@@ -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