Commit 46aaa53f authored by David Reid's avatar David Reid

Update documentation and add support for default buffer sizes and periods.

parent 5cbbf0b9
...@@ -19,10 +19,7 @@ Features ...@@ -19,10 +19,7 @@ Features
Examples Examples
======== ========
mini_al will request and deliver audio data through callbacks.
Asynchonous API
---------------
In asynchronous mode, mini_al will request and deliver audio data through callbacks.
``` ```
mal_uint32 on_send_audio_data(mal_device* pDevice, mal_uint32 sampleCount, void* pSamples) mal_uint32 on_send_audio_data(mal_device* pDevice, mal_uint32 sampleCount, void* pSamples)
...@@ -34,11 +31,11 @@ int main() ...@@ -34,11 +31,11 @@ int main()
{ {
mal_uint32 channels = 2; mal_uint32 channels = 2;
mal_uint32 sampleRate = 44100; mal_uint32 sampleRate = 44100;
mal_uint32 fragmentSizeInFrames = 512; mal_uint32 bufferSizeInFrames = 512;
mal_uint32 fragmentCount = 2; mal_uint32 periods = 2;
mal_device playbackDevice; mal_device playbackDevice;
if (mal_device_init(&playbackDevice, mal_device_type_playback, NULL, mal_format_f32, channels, sampleRate, fragmentSizeInFrames, fragmentCount) != MAL_SUCCESS) { if (mal_device_init(&playbackDevice, mal_device_type_playback, NULL, mal_format_f32, channels, sampleRate, bufferSizeInFrames, periods) != MAL_SUCCESS) {
return -1; return -1;
} }
......
// Mini audio library. Public domain. See "unlicense" statement at the end of this file. // Mini audio library. Public domain. See "unlicense" statement at the end of this file.
// mini_al - v0.0 - UNRELEASED // mini_al - v0.1 - 2016-10-21
// //
// David Reid - mackron@gmail.com // David Reid - mackron@gmail.com
// ABOUT // ABOUT
// ===== // =====
// mini_al is a small library for making it easy to do audio playback and recording. It's focused on // mini_al is a small library for making it easy to connect to a playback or capture device and send
// simplicity and being light-weight so don't expect all the bells and whistles. // or receive data from said device. It's focused on being simple and light-weight so don't expect
// all the bells and whistles. Indeed, this is not a full packaged audio library - it's just for
// connecting to a device and handling data transmission.
// //
// mini_al uses an asynchronous API. Every device is created with it's own thread, with audio data // mini_al uses an asynchronous API. Every device is created with it's own thread, with audio data
// being either delivered to the application from the device (in the case of recording/capture) or // being either delivered to the application from the device (in the case of recording/capture) or
// delivered from the application to the device in the case of playback. Synchronous APIs are not // delivered from the application to the device in the case of playback. Synchronous APIs are not
// supported in the interest of keeping the library as small and lightweight as possible. // supported in the interest of keeping the library as small and light-weight as possible.
// //
// Supported backends: // Supported backends:
// - DirectSound (Windows Only) // - DirectSound (Windows Only)
// - ALSA (Linux Only) // - ALSA (Linux Only)
// - null // - null
// - ... and many more in the future. // - ... and more in the future.
// - Core Audio (OSX, iOS)
// - Something for Android
// - Maybe OSS
// //
// //
// USAGE // USAGE
...@@ -26,6 +31,51 @@ ...@@ -26,6 +31,51 @@
// #define MAL_IMPLEMENTATION // #define MAL_IMPLEMENTATION
// #include "mini_al.h" // #include "mini_al.h"
// //
// You can then #include this file in other parts of the program as you would with any other header file.
//
//
// Building (Windows)
// ------------------
// You do not need to link to anything for the Windows build, but you will need dsound.h in your include paths.
//
//
// Building (Linux)
// ----------------
// The Linux build uses ALSA for it's backend so you will need to install the relevant ALSA development pacakges
// for your preferred distro. It also uses pthreads.
//
// Linking: -lasound -lpthread
//
//
// Playback Example
// ----------------
// mal_uint32 on_send_samples(mal_device* pDevice, mal_uint32 frameCount, void* pSamples)
// {
// // This callback is set with mal_device_set_send_callback() after initializing and will be
// // called when a playback device needs more data. You need to write as many frames as you can
// // to pSamples (but no more than frameCount) and then return the number of frames you wrote.
// //
// // You can pass in user data by setting pDevice->pUserData after initialization.
// return (mal_uint32)drwav_read_f32(&wav, frameCount * pDevice->channels, (float*)pSamples) / pDevice->channels;
// }
//
// ...
//
// mal_device device;
// mal_result result = mal_device_init(&device, mal_device_type_playback, &id, mal_format_f32, wav.channels, wav.sampleRate, 16384, 2, NULL);
// if (result != MAL_SUCCESS) {
// return -1;
// }
//
// device.pUserData = pMyData; // pUserData is reserved for you. Use it to pass data to callbacks.
// 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_uninit(&device); // This will stop the device so no need to do that manually.
//
//
// //
// NOTES // NOTES
// ===== // =====
...@@ -39,8 +89,34 @@ ...@@ -39,8 +89,34 @@
// integer samples, interleaved. Let me know if you need non-interleaved and I'll look into it. // integer samples, interleaved. Let me know if you need non-interleaved and I'll look into it.
// //
// //
//
// BACKEND NUANCES // BACKEND NUANCES
// =============== // ===============
// - The absolute best latency I am able to get on DirectSound is about 10 milliseconds. This seems very
// consistent so I'm suspecting there's some kind of hard coded limit there or something.
// - The ALSA backend does not support rewinding.
//
//
//
// OPTIONS
// =======
// #define these options before including this file.
//
// #define MAL_NO_DSOUND
// Disables the DirectSound backend. Note that this is the only backend for the Windows platform.
//
// #define MAL_NO_ALSA
// Disables the ALSA backend. Note that this is the only backend for the Linux platform.
//
// #define MAL_NO_NULL
// Disables the null backend.
//
// #define MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS (Default = 15)
// When a buffer size of 0 is specified when a device is initialized, it will default to a size with
// this number of milliseconds worth of data.
//
// #define MAL_DEFAULT_PERIODS (Default = 2)
// When a period count of 0 is specified when a device is initialized, it will default to this.
#ifndef mini_al_h #ifndef mini_al_h
#define mini_al_h #define mini_al_h
...@@ -49,6 +125,24 @@ ...@@ -49,6 +125,24 @@
extern "C" { extern "C" {
#endif #endif
// Config.
// The default size of the device's buffer in milliseconds. In my testing, if this is a multiple of the
// periods it can result in audio glitching on the DirectSound backend, so make sure it's not a clean
// multiple of the period.
//
// If this is too small you may get underruns and overruns in which case you'll need to either increase
// this value or use an explicit buffer size.
#ifndef MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS
#define MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS 15
#endif
// Default periods when none is specified in mal_device_init(). More periods means more work on the CPU.
#ifndef MAL_DEFAULT_PERIODS
#define MAL_DEFAULT_PERIODS 2
#endif
// Platform/backend detection. // Platform/backend detection.
#ifdef _WIN32 #ifdef _WIN32
#define MAL_WIN32 #define MAL_WIN32
...@@ -111,10 +205,6 @@ typedef void* mal_ptr; ...@@ -111,10 +205,6 @@ typedef void* mal_ptr;
} mal_event; } mal_event;
#endif #endif
#ifdef MAL_ENABLE_DSOUND
#define MAL_MAX_PERIODS_DSOUND 16
#endif
typedef int mal_result; typedef int mal_result;
#define MAL_SUCCESS 0 #define MAL_SUCCESS 0
#define MAL_ERROR -1 // A generic error. #define MAL_ERROR -1 // A generic error.
...@@ -278,16 +368,22 @@ struct mal_device ...@@ -278,16 +368,22 @@ struct mal_device
// This API dynamically links to backend DLLs/SOs (such as dsound.dll). // This API dynamically links to backend DLLs/SOs (such as dsound.dll).
mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo); mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo);
// Initializes a device in asynchronous mode. // Initializes a device.
// //
// The device ID (pDeviceID) can be null, in which case the default device is used. Otherwise, you // The device ID (pDeviceID) can be null, in which case the default device is used. Otherwise, you
// can retrieve the ID by calling mal_enumerate_devices() and retrieve the ID from the returned // can retrieve the ID by calling mal_enumerate_devices() and using the ID from the returned data.
// information.
// //
// This will try it's hardest to create a valid device, even if it means adjusting input arguments. // This will try it's hardest to create a valid device, even if it means adjusting input arguments.
// Look at pDevice->channels, pDevice->sampleRate, etc. to determine the actual properties after // Look at pDevice->channels, pDevice->sampleRate, etc. to determine the actual properties after
// initialization. // initialization.
// //
// If <bufferSizeInFrames> is 0, it will default to MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS. If
// <periods> is set to 0 it will default to MAL_DEFAULT_PERIODS.
//
// The <periods> property controls how frequently the background thread is woken to check for more
// data. It's tied to the buffer size, so as an example, if your buffer size is equivalent to 10
// milliseconds and you have 2 periods, the CPU will wake up approximately every 5 milliseconds.
//
// Return Value: // Return Value:
// - MAL_SUCCESS if successful. // - MAL_SUCCESS if successful.
// - MAL_INVALID_ARGS // - MAL_INVALID_ARGS
...@@ -299,14 +395,13 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d ...@@ -299,14 +395,13 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d
// that performs a memory allocation is ALSA when mmap mode is not supported. // that performs a memory allocation is ALSA when mmap mode is not supported.
// - MAL_FORMAT_NOT_SUPPORTED // - MAL_FORMAT_NOT_SUPPORTED
// The specified format is not supported by the backend. mini_al does not currently do any // The specified format is not supported by the backend. mini_al does not currently do any
// software format conversions which means initialization must fail if the backend device does // software format conversions which means initialization must fail if the backend does not
// not natively support it. // natively support it.
// - MAL_FAILED_TO_INIT_BACKEND // - MAL_FAILED_TO_INIT_BACKEND
// There was a backend-specific error during initialization. // There was a backend-specific error during initialization.
// //
// Thread Safety: ??? // Thread Safety: UNSAFE
// This API is thread safe so long as the application does not try to use the device object before // Results are undefined if you try using a device before this function as returned.
// this call has returned.
// //
// 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
...@@ -1508,9 +1603,6 @@ static mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type t ...@@ -1508,9 +1603,6 @@ static mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type t
return MAL_FORMAT_NOT_SUPPORTED; return MAL_FORMAT_NOT_SUPPORTED;
} }
if (periods > MAL_MAX_PERIODS_DSOUND) {
periods = MAL_MAX_PERIODS_DSOUND;
}
WAVEFORMATEXTENSIBLE wf; WAVEFORMATEXTENSIBLE wf;
mal_zero_object(&wf); mal_zero_object(&wf);
...@@ -1583,6 +1675,7 @@ static mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type t ...@@ -1583,6 +1675,7 @@ static mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type t
pDevice->sampleRate = pActualFormat->Format.nSamplesPerSec; pDevice->sampleRate = pActualFormat->Format.nSamplesPerSec;
bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format);
// Meaning of dwFlags (from MSDN): // Meaning of dwFlags (from MSDN):
// //
// DSBCAPS_GLOBALFOCUS // DSBCAPS_GLOBALFOCUS
...@@ -2774,7 +2867,11 @@ mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device ...@@ -2774,7 +2867,11 @@ mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device
} }
} }
if (channels == 0 || sampleRate == 0 || bufferSizeInFrames == 0 || periods == 0) return mal_post_error(pDevice, "mal_device_init() called with invalid arguments.", MAL_INVALID_ARGS); if (channels == 0 || sampleRate == 0) return mal_post_error(pDevice, "mal_device_init() called with invalid arguments.", MAL_INVALID_ARGS);
// Default buffer size and periods.
if (bufferSizeInFrames == 0) bufferSizeInFrames = (sampleRate/1000) * MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS;
if (periods == 0) periods = MAL_DEFAULT_PERIODS;
pDevice->type = type; pDevice->type = type;
pDevice->format = format; pDevice->format = format;
...@@ -3087,7 +3184,7 @@ mal_uint32 mal_get_sample_size_in_bytes(mal_format format) ...@@ -3087,7 +3184,7 @@ mal_uint32 mal_get_sample_size_in_bytes(mal_format format)
// REVISION HISTORY // REVISION HISTORY
// ================ // ================
// //
// v0.1 - TBD // v0.1 - 2016-10-21
// - Initial versioned release. // - Initial versioned release.
......
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