mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Update documentation and add support for default buffer sizes and periods.
This commit is contained in:
@@ -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()
|
|||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
// 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 @@
|
|||||||
// #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 @@
|
|||||||
// 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 @@
|
|||||||
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;
|
|||||||
} 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
|
|||||||
// 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
|
|||||||
// 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
|
|||||||
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
|
|||||||
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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
// REVISION HISTORY
|
// REVISION HISTORY
|
||||||
// ================
|
// ================
|
||||||
//
|
//
|
||||||
// v0.1 - TBD
|
// v0.1 - 2016-10-21
|
||||||
// - Initial versioned release.
|
// - Initial versioned release.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user