Commit 58989ea1 authored by David Reid's avatar David Reid

Update documentation.

parent a752a49c
......@@ -10,8 +10,10 @@ C/C++, single file, public domain.
Features
========
- A very simple API.
- Both synchronous and asynchronous APIs.
- Public domain
- Single file
- A very simple API
......@@ -36,7 +38,7 @@ int main()
mal_uint32 fragmentCount = 2;
mal_device playbackDevice;
if (mal_device_init_async(&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, fragmentSizeInFrames, fragmentCount) != MAL_SUCCESS) {
return -1;
}
......
......@@ -3,10 +3,27 @@
//
// David Reid - mackron@gmail.com
// USAGE
// ABOUT
// =====
// mini_al is a small library for making it easy to do audio playback and recording. It's focused on
// simplicity and being light-weight so don't expect all the bells and whistles.
//
// mini_al's 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
// 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 backends:
// - DirectSound (Windows Only)
// - ALSA (Linux Only)
// - ... and many more in the future.
//
// (WRITE ME)
//
// USAGE
// =====
// mini_al is a single-file library. To use it, do something like the following in one .c file.
// #define MAL_IMPLEMENTATION
// #include "mini_al.h"
//
//
// NOTES
......@@ -17,6 +34,8 @@
// boundary (4 bytes on 32-bit, 8 bytes on 64-bit), it will _not_ be thread-safe. The reason for this
// is that it depends on members of mal_device being correctly aligned for atomic assignments and bit
// manipulation.
// - Sample data is always little-endian and interleaved. For example, mal_format_s16 means signed 16-bit
// integer samples, interleaved. Let me know if you need non-interleaved and I'll look into it.
//
//
// BACKEND NUANCES
......@@ -86,11 +105,8 @@ typedef void* mal_ptr;
#ifdef MAL_WIN32
typedef mal_handle mal_thread;
typedef mal_handle mal_event;
typedef mal_handle mal_semaphore;
#else
typedef pthread_t mal_thread;
typedef sem_t mal_semaphore;
typedef struct
{
pthread_mutex_t mutex;
......@@ -120,6 +136,7 @@ typedef int mal_result;
#define MAL_FAILED_TO_READ_DATA_FROM_CLIENT -23
#define MAL_FAILED_TO_START_BACKEND_DEVICE -24
#define MAL_FAILED_TO_STOP_BACKEND_DEVICE -25
#define MAL_FAILED_TO_MAP_DEVICE_BUFFER -26
typedef struct mal_device mal_device;
......@@ -223,6 +240,13 @@ struct mal_device
// Enumerates over each device of the given type (playback or capture).
//
// Return Value:
// - MAL_SUCCESS if successful.
// - MAL_INVALID_ARGS
// One or more of the input arguments is invalid.
// - MAL_NO_BACKEND
// There is no supported backend, or there was an error loading it (such as a missing dll/so).
//
// Thread Safety: SAFE, SEE NOTES.
// This API uses an application-defined buffer for output. This is thread-safe so long as the
// application ensures mutal exclusion to the output buffer at their level.
......@@ -238,23 +262,46 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d
// information.
//
// This will try it's hardest to create a valid device, even if it means adjusting input arguments.
// Use 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.
//
// Return Value:
// - MAL_SUCCESS if successful.
// - MAL_INVALID_ARGS
// One or more of the input arguments is invalid.
// - MAL_NO_BACKEND
// There is no supported backend, or there was an error loading it (such as a missing dll/so).
// - MAL_OUT_OF_MEMORY
// A necessary memory allocation failed, likely due to running out of memory. The only backend
// that performs a memory allocation is ALSA when mmap mode is not supported.
// - MAL_FORMAT_NOT_SUPPORTED
// 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
// not natively support it.
// - MAL_FAILED_TO_INIT_BACKEND
// There was a backend-specific error during initialization.
//
// Thread Safety: SAFE
// This API is thread safe so long as the application does not try to use the device object before
// this call has returned.
//
// Efficiency: LOW
// This API will dynamically link to backend DLLs/SOs like dsound.dll, and is otherwise just slow
// due to the fact that it's an initialization API.
mal_result mal_device_init_async(mal_device* pDevice, mal_device_type type, mal_device_id* pDeviceID, mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 fragmentSizeInFrames, mal_uint32 fragmentCount);
// 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_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 fragmentSizeInFrames, mal_uint32 fragmentCount);
// Uninitializes a device.
//
// This will explicitly stop the device. You do not need to call mal_device_stop() beforehand, but it's
// harmless if you do.
//
// Return Value:
// - MAL_SUCCESS if successful.
// - MAL_INVALID_ARGS
// pDevice is NULL.
// - MAL_DEVICE_NOT_INITIALIZED
// The device is not currently or was never initialized.
//
// Thread Safety: UNSAFE
// This API shouldn't crash in a multi-threaded environment, but results are undefined if an application
// attempts to do something with the device at the same time as uninitializing.
......@@ -290,18 +337,19 @@ void mal_device_set_send_callback(mal_device* pDevice, mal_send_proc proc);
// to be done _before_ the device starts playing back audio.
//
// Return Value:
// MAL_SUCCESS if successful.
//
// MAL_DEVICE_BUSY
// - MAL_SUCCESS if successful.
// - MAL_INVALID_ARGS
// One or more of the input arguments is invalid.
// - MAL_DEVICE_NOT_INITIALIZED
// The device is not currently or was never initialized.
// - MAL_DEVICE_BUSY
// The device is in the process of stopping. This will only happen if mal_device_start() and
// mal_device_stop() is called simultaneous on separate threads. This will never be returned in
// single-threaded applications.
//
// MAL_DEVICE_ALREADY_STARTING
// - MAL_DEVICE_ALREADY_STARTING
// The device is already in the process of starting. This will never be returned in single-threaded
// applications.
//
// MAL_DEVICE_ALREADY_STARTED
// - MAL_DEVICE_ALREADY_STARTED
// The device is already started.
//
// Thread Safety: SAFE
......@@ -312,6 +360,22 @@ mal_result mal_device_start(mal_device* pDevice);
// Puts the device to sleep, but does not uninitialize it. Use mal_device_start() to start it up again.
//
// Return Value:
// - MAL_SUCCESS if successful.
// - MAL_INVALID_ARGS
// One or more of the input arguments is invalid.
// - MAL_DEVICE_NOT_INITIALIZED
// The device is not currently or was never initialized.
// - MAL_DEVICE_BUSY
// The device is in the process of starting. This will only happen if mal_device_start() and
// mal_device_stop() is called simultaneous on separate threads. This will never be returned in
// single-threaded applications.
// - MAL_DEVICE_ALREADY_STOPPING
// The device is already in the process of stopping. This will never be returned in single-threaded
// applications.
// - MAL_DEVICE_ALREADY_STOPPED
// The device is already stopped.
//
// Thread Safety: SAFE
//
// Efficiency: LOW
......@@ -320,6 +384,9 @@ mal_result mal_device_stop(mal_device* pDevice);
// Determines whether or not the device is started.
//
// Return Value:
// True if the device is started, false otherwise.
//
// Thread Safety: SAFE
// If another thread calls mal_device_start() or mal_device_stop() at this same time as this function
// is called, there's a very small chance the return value will out of sync.
......@@ -559,6 +626,11 @@ void mal_thread_wait__win32(mal_thread* pThread)
WaitForSingleObject(*pThread, INFINITE);
}
void mal_sleep__win32(mal_uint32 milliseconds)
{
Sleep((DWORD)milliseconds);
}
mal_bool32 mal_event_create__win32(mal_event* pEvent)
{
......@@ -598,6 +670,11 @@ void mal_thread_wait__posix(mal_thread* pThread)
pthread_join(*pThread, NULL);
}
void mal_sleep__posix(mal_uint32 milliseconds)
{
usleep(milliseconds * 1000); // <-- usleep is in microseconds.
}
mal_bool32 mal_event_create__posix(mal_event* pEvent)
{
......@@ -673,6 +750,17 @@ void mal_thread_wait(mal_thread* pThread)
#endif
}
void mal_sleep(mal_uint32 milliseconds)
{
#ifdef MAL_WIN32
mal_sleep__win32(milliseconds);
#endif
#ifdef MAL_POSIX
mal_sleep__posix(milliseconds);
#endif
}
mal_bool32 mal_event_create(mal_event* pEvent)
{
......@@ -1283,7 +1371,7 @@ static mal_result mal_device__read_fragment_from_client__dsound(mal_device* pDev
void* pLockPtr;
DWORD lockSize;
if (FAILED(IDirectSoundBuffer_Lock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, offset, fragmentSizeInBytes, &pLockPtr, &lockSize, NULL, NULL, 0))) {
return MAL_ERROR;
return MAL_FAILED_TO_MAP_DEVICE_BUFFER;
}
mal_device__read_samples_from_client(pDevice, pDevice->fragmentSizeInFrames * pDevice->channels, pLockPtr);
......@@ -1302,7 +1390,7 @@ static mal_result mal_device__send_fragment_to_client__dsound(mal_device* pDevic
void* pLockPtr;
DWORD lockSize;
if (FAILED(IDirectSoundCaptureBuffer_Lock((LPDIRECTSOUNDCAPTUREBUFFER8)pDevice->dsound.pCaptureBuffer, offset, fragmentSizeInBytes, &pLockPtr, &lockSize, NULL, NULL, 0))) {
return MAL_ERROR;
return MAL_FAILED_TO_MAP_DEVICE_BUFFER;
}
mal_device__send_samples_to_client(pDevice, pDevice->fragmentSizeInFrames * pDevice->channels, pLockPtr);
......@@ -1630,7 +1718,7 @@ mal_result mal_enumerate_devices__alsa(mal_device_type type, mal_uint32* pCount,
char** ppDeviceHints;
if (snd_device_name_hint(-1, "pcm", (void***)&ppDeviceHints) < 0) {
return MAL_ERROR;
return MAL_SUCCESS; // <-- This is unintuitive, but it just emulates the case where there are no devices.
}
char** ppNextDeviceHint = ppDeviceHints;
......@@ -1730,7 +1818,7 @@ mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type type, mal_
if (snd_pcm_hw_params_set_format((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, formatALSA) < 0) {
snd_pcm_hw_params_free(pHWParams);
mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND;
return MAL_FORMAT_NOT_SUPPORTED;
}
if (snd_pcm_hw_params_set_rate_near((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &sampleRate, 0) < 0) {
......@@ -2154,7 +2242,9 @@ void mal_device_uninit(mal_device* pDevice)
// Make sure the device is stopped first. The backends will probably handle this naturally,
// but I like to do it explicitly for my own sanity.
mal_device_stop(pDevice);
while (mal_device_stop(pDevice) == MAL_DEVICE_BUSY) {
mal_sleep(10);
}
// Putting the device into an uninitialized state will make the worker thread return.
mal_device__set_state(pDevice, MAL_STATE_UNINITIALIZED);
......
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