Commit 7afcede3 authored by David Reid's avatar David Reid

API CHANGE: Add device callbacks to mal_device_config.

Rationale:
1) It allows the callbacks to be set at initialization time which feels
   a bit more intuitive to me.
2) It avoids the need to call mal_device_set_send_callback(), etc.
3) It's a bit more consistent with the onLog callback. Previously,
   onLog would be passed to mal_device_init(), whereas onSend, etc were
   set with mal_device_set_send_callback(), etc. which feels needlessly
   inconsistent.
parent 4e2e48d2
...@@ -17,14 +17,17 @@ Features ...@@ -17,14 +17,17 @@ Features
Example Simple Playback Example
======= =======================
mini_al will request and deliver audio data through callbacks.
```c ```c
mal_uint32 on_send_frames_to_device(mal_device* pDevice, mal_uint32 frameCount, void* pSamples) mal_uint32 on_send_frames_to_device(mal_device* pDevice, mal_uint32 frameCount, void* pSamples)
{ {
drwav* pWav = (drwav*)pDevice->pUserData; drwav* pWav = (drwav*)pDevice->pUserData;
if (pWav == NULL) {
return 0;
}
return (mal_uint32)drwav_read_f32(pWav, frameCount * pDevice->channels, (float*)pSamples) / pDevice->channels; return (mal_uint32)drwav_read_f32(pWav, frameCount * pDevice->channels, (float*)pSamples) / pDevice->channels;
} }
...@@ -42,20 +45,23 @@ int main(int argc, char** argv) ...@@ -42,20 +45,23 @@ int main(int argc, char** argv)
} }
// In this example we use the default playback device with a default buffer size and period count. // In this example we use the default playback device with a default buffer size and period count.
mal_device_config config;
config.format = mal_format_f32;
config.channels = wav.channels;
config.sampleRate = wav.sampleRate;
config.bufferSizeInFrames = 0; // Use default.
config.periods = 0; // Use default.
config.onRecvCallback = NULL; // Not used for playback.
config.onSendCallback = on_send_frames_to_device;
config.onStopCallback = NULL;
config.onLogCallback = NULL;
mal_device device; mal_device device;
if (mal_device_init(&device, mal_device_type_playback, NULL, mal_format_f32, wav.channels, wav.sampleRate, 0, 0, NULL) != MAL_SUCCESS) { if (mal_device_init(&device, mal_device_type_playback, NULL, &config, &wav) != MAL_SUCCESS) {
printf("Failed to open playback device."); printf("Failed to open playback device.");
drwav_uninit(&wav); drwav_uninit(&wav);
return -3; return -3;
} }
// The pUserData member of mal_device is reserved for you.
device.pUserData = &wav;
// This is the callback for sending data to a playback device when it needs more. Make sure
// it's set before playing the device otherwise you'll end up with silence for the first
// bunch of frames.
mal_device_set_send_callback(&device, on_send_frames_to_device);
mal_device_start(&device); mal_device_start(&device);
printf("Press Enter to quit..."); printf("Press Enter to quit...");
......
...@@ -53,14 +53,17 @@ int main() ...@@ -53,14 +53,17 @@ int main()
config.sampleRate = 48000; config.sampleRate = 48000;
config.bufferSizeInFrames = 0; // Use default. config.bufferSizeInFrames = 0; // Use default.
config.periods = 0; // Use default. config.periods = 0; // Use default.
config.onRecvCallback = on_recv_frames;
config.onSendCallback = on_send_frames;
config.onStopCallback = NULL;
config.onLogCallback = NULL;
printf("Recording...\n"); printf("Recording...\n");
mal_device captureDevice; mal_device captureDevice;
if (mal_device_init(&captureDevice, mal_device_type_capture, NULL, &config, NULL, NULL)) { if (mal_device_init(&captureDevice, mal_device_type_capture, NULL, &config, NULL)) {
printf("Failed to initialize capture device.\n"); printf("Failed to initialize capture device.\n");
return -2; return -2;
} }
mal_device_set_recv_callback(&captureDevice, on_recv_frames);
mal_device_start(&captureDevice); mal_device_start(&captureDevice);
printf("Press Enter to stop recording...\n"); printf("Press Enter to stop recording...\n");
...@@ -71,11 +74,10 @@ int main() ...@@ -71,11 +74,10 @@ int main()
printf("Playing...\n"); printf("Playing...\n");
mal_device playbackDevice; mal_device playbackDevice;
if (mal_device_init(&playbackDevice, mal_device_type_playback, NULL, &config, NULL, NULL)) { if (mal_device_init(&playbackDevice, mal_device_type_playback, NULL, &config, NULL)) {
printf("Failed to initialize playback device.\n"); printf("Failed to initialize playback device.\n");
return -3; return -3;
} }
mal_device_set_send_callback(&playbackDevice, on_send_frames);
mal_device_start(&playbackDevice); mal_device_start(&playbackDevice);
printf("Press Enter to quit...\n"); printf("Press Enter to quit...\n");
......
...@@ -37,21 +37,17 @@ int main(int argc, char** argv) ...@@ -37,21 +37,17 @@ int main(int argc, char** argv)
config.sampleRate = wav.sampleRate; config.sampleRate = wav.sampleRate;
config.bufferSizeInFrames = 0; // Use default. config.bufferSizeInFrames = 0; // Use default.
config.periods = 0; // Use default. config.periods = 0; // Use default.
config.onRecvCallback = NULL; // Not used for playback.
config.onSendCallback = on_send_frames_to_device;
config.onStopCallback = NULL;
config.onLogCallback = NULL;
mal_device device; mal_device device;
if (mal_device_init(&device, mal_device_type_playback, NULL, &config, NULL, NULL) != MAL_SUCCESS) { if (mal_device_init(&device, mal_device_type_playback, NULL, &config, &wav) != MAL_SUCCESS) {
printf("Failed to open playback device."); printf("Failed to open playback device.");
drwav_uninit(&wav); drwav_uninit(&wav);
return -3; return -3;
} }
// The pUserData member of mal_device is reserved for you.
device.pUserData = &wav;
// This is the callback for sending data to a playback device when it needs more. Make sure
// it's set before playing the device otherwise you'll end up with silence for the first
// bunch of frames.
mal_device_set_send_callback(&device, on_send_frames_to_device);
mal_device_start(&device); mal_device_start(&device);
printf("Press Enter to quit..."); printf("Press Enter to quit...");
......
...@@ -75,14 +75,17 @@ ...@@ -75,14 +75,17 @@
// config.sampleRate = wav.sampleRate; // config.sampleRate = wav.sampleRate;
// config.bufferSizeInFrames = 0; // Use default. // config.bufferSizeInFrames = 0; // Use default.
// config.periods = 0; // Use default. // config.periods = 0; // Use default.
// config.onSendCallback = on_send_samples;
// config.onRecvCallback = NULL; // Not used for playback devices.
// config.onStopCallback = NULL; // We don't care about knowing when the device has stopped...
// config.onLogCallback = NULL; // ... nor do we care about logging (but you really should in a real-world application).
// //
// mal_device device; // mal_device device;
// mal_result result = mal_device_init(&device, mal_device_type_playback, NULL, &config, NULL, pMyData); // mal_result result = mal_device_init(&device, mal_device_type_playback, NULL, &config, pMyData);
// if (result != MAL_SUCCESS) { // if (result != MAL_SUCCESS) {
// return -1; // return -1;
// } // }
// //
// mal_device_set_send_callback(&device, on_send_samples);
// mal_device_start(&device); // The device is sleeping by default so you'll need to start it manually. // mal_device_start(&device); // The device is sleeping by default so you'll need to start it manually.
// //
// ... // ...
...@@ -293,11 +296,11 @@ typedef enum ...@@ -293,11 +296,11 @@ typedef enum
{ {
// I like to keep these explicitly defined because they're used as a key into a lookup table. When items are // I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
// added to this, make sure there are no gaps and that they're added to the lookup table in mal_get_sample_size_in_bytes(). // added to this, make sure there are no gaps and that they're added to the lookup table in mal_get_sample_size_in_bytes().
mal_format_u8 = 0, mal_format_u8 = 0,
mal_format_s16 = 1, // Seems to be the most widely supported format. mal_format_s16 = 1, // Seems to be the most widely supported format.
mal_format_s24 = 2, // Tightly packed. 3 bytes per sample. mal_format_s24 = 2, // Tightly packed. 3 bytes per sample.
mal_format_s32 = 3, mal_format_s32 = 3,
mal_format_f32 = 4, mal_format_f32 = 4,
} mal_format; } mal_format;
typedef union typedef union
...@@ -325,6 +328,10 @@ typedef struct ...@@ -325,6 +328,10 @@ typedef struct
mal_uint32 sampleRate; mal_uint32 sampleRate;
mal_uint32 bufferSizeInFrames; mal_uint32 bufferSizeInFrames;
mal_uint32 periods; mal_uint32 periods;
mal_recv_proc onRecvCallback;
mal_send_proc onSendCallback;
mal_stop_proc onStopCallback;
mal_log_proc onLogCallback;
} mal_device_config; } mal_device_config;
struct mal_device struct mal_device
...@@ -337,7 +344,6 @@ struct mal_device ...@@ -337,7 +344,6 @@ struct mal_device
mal_uint32 bufferSizeInFrames; mal_uint32 bufferSizeInFrames;
mal_uint32 periods; mal_uint32 periods;
mal_uint32 state; mal_uint32 state;
mal_uint32 flags; // MAL_DEVICE_FLAG_*
mal_recv_proc onRecv; mal_recv_proc onRecv;
mal_send_proc onSend; mal_send_proc onSend;
mal_stop_proc onStop; mal_stop_proc onStop;
...@@ -349,6 +355,7 @@ struct mal_device ...@@ -349,6 +355,7 @@ struct mal_device
mal_event stopEvent; mal_event stopEvent;
mal_thread thread; mal_thread thread;
mal_result workResult; // This is set by the worker thread after it's finished doing a job. mal_result workResult; // This is set by the worker thread after it's finished doing a job.
mal_uint32 flags; // MAL_DEVICE_FLAG_*
union union
{ {
...@@ -467,7 +474,7 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d ...@@ -467,7 +474,7 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d
// Efficiency: LOW // Efficiency: LOW
// This API will dynamically link to backend DLLs/SOs like dsound.dll, and is otherwise just slow // This API will dynamically link to backend DLLs/SOs like dsound.dll, and is otherwise just slow
// due to the nature of it being an initialization API. // due to the nature of it being an initialization API.
mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device_id* pDeviceID, mal_device_config* pConfig, mal_log_proc onLog, void* pUserData); mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device_id* pDeviceID, mal_device_config* pConfig, void* pUserData);
// Uninitializes a device. // Uninitializes a device.
// //
...@@ -3468,14 +3475,17 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d ...@@ -3468,14 +3475,17 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d
return result; return result;
} }
mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device_id* pDeviceID, mal_device_config* pConfig, mal_log_proc onLog, void* pUserData) mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device_id* pDeviceID, mal_device_config* pConfig, void* pUserData)
{ {
if (pDevice == NULL) return mal_post_error(pDevice, "mal_device_init() called with invalid arguments.", MAL_INVALID_ARGS); if (pDevice == NULL) return mal_post_error(pDevice, "mal_device_init() called with invalid arguments.", MAL_INVALID_ARGS);
mal_zero_object(pDevice); mal_zero_object(pDevice);
// 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 = pUserData; pDevice->pUserData = pUserData;
pDevice->onLog = onLog; pDevice->onLog = pConfig->onLogCallback;
pDevice->onStop = pConfig->onStopCallback;
pDevice->onSend = pConfig->onSendCallback;
pDevice->onRecv = pConfig->onRecvCallback;
if (((mal_uint64)pDevice % sizeof(pDevice)) != 0) { if (((mal_uint64)pDevice % sizeof(pDevice)) != 0) {
if (pDevice->onLog) { if (pDevice->onLog) {
......
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