|
@@ -2,7 +2,7 @@ package miniaudio
|
|
|
|
|
|
/*
|
|
/*
|
|
Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
|
|
Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
|
|
-miniaudio - v0.11.9 - 2022-04-20
|
|
|
|
|
|
+miniaudio - v0.11.21 - 2023-11-15
|
|
|
|
|
|
David Reid - [email protected]
|
|
David Reid - [email protected]
|
|
|
|
|
|
@@ -40,7 +40,7 @@ A config/init pattern is used throughout the entire library. The idea is that yo
|
|
object and pass that into the initialization routine. The advantage to this system is that the
|
|
object and pass that into the initialization routine. The advantage to this system is that the
|
|
config object can be initialized with logical defaults and new properties added to it without
|
|
config object can be initialized with logical defaults and new properties added to it without
|
|
breaking the API. The config object can be allocated on the stack and does not need to be
|
|
breaking the API. The config object can be allocated on the stack and does not need to be
|
|
-maintained after initialization of the corresponding object.
|
|
|
|
|
|
+maintained after initialization of the corresponding object.
|
|
|
|
|
|
|
|
|
|
1.1. Low Level API
|
|
1.1. Low Level API
|
|
@@ -89,7 +89,7 @@ device on the stack, but you could allocate it on the heap if that suits your si
|
|
|
|
|
|
// Do something here. Probably your program's main loop.
|
|
// Do something here. Probably your program's main loop.
|
|
|
|
|
|
- ma_device_uninit(&device); // This will stop the device so no need to do that manually.
|
|
|
|
|
|
+ ma_device_uninit(&device);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
```
|
|
```
|
|
@@ -365,7 +365,7 @@ initialized. The easiest but least flexible way of playing a sound is like so:
|
|
This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the
|
|
This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the
|
|
internal sound up for recycling. The last parameter is used to specify which sound group the sound
|
|
internal sound up for recycling. The last parameter is used to specify which sound group the sound
|
|
should be associated with which will be explained later. This particular way of playing a sound is
|
|
should be associated with which will be explained later. This particular way of playing a sound is
|
|
-simple, but lacks flexibility and features. A more flexible way of playing a sound is to first
|
|
|
|
|
|
+simple, but lacks flexibility and features. A more flexible way of playing a sound is to first
|
|
initialize a sound:
|
|
initialize a sound:
|
|
|
|
|
|
```c
|
|
```c
|
|
@@ -388,7 +388,7 @@ Sounds should be uninitialized with `ma_sound_uninit()`.
|
|
|
|
|
|
Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with
|
|
Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with
|
|
`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use
|
|
`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use
|
|
-`ma_sound_seek_to_pcm_frames(&sound, 0)` to seek back to the start of a sound. By default, starting
|
|
|
|
|
|
+`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting
|
|
and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound
|
|
and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound
|
|
the be started and/or stopped at a specific time. This can be done with the following functions:
|
|
the be started and/or stopped at a specific time. This can be done with the following functions:
|
|
|
|
|
|
@@ -400,13 +400,13 @@ the be started and/or stopped at a specific time. This can be done with the foll
|
|
```
|
|
```
|
|
|
|
|
|
The start/stop time needs to be specified based on the absolute timer which is controlled by the
|
|
The start/stop time needs to be specified based on the absolute timer which is controlled by the
|
|
-engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`.
|
|
|
|
-The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if
|
|
|
|
-required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()`
|
|
|
|
-before anything will play:
|
|
|
|
|
|
+engine. The current global time time in PCM frames can be retrieved with
|
|
|
|
+`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with
|
|
|
|
+`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling
|
|
|
|
+a start time still requires an explicit call to `ma_sound_start()` before anything will play:
|
|
|
|
|
|
```c
|
|
```c
|
|
- ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
|
|
|
|
|
|
+ ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
|
|
ma_sound_start(&sound);
|
|
ma_sound_start(&sound);
|
|
```
|
|
```
|
|
|
|
|
|
@@ -462,6 +462,11 @@ is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled wit
|
|
miniaudio should work cleanly out of the box without the need to download or install any
|
|
miniaudio should work cleanly out of the box without the need to download or install any
|
|
dependencies. See below for platform-specific details.
|
|
dependencies. See below for platform-specific details.
|
|
|
|
|
|
|
|
+Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations.
|
|
|
|
+
|
|
|
|
+If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`,
|
|
|
|
+etc. you need to link with `-latomic`.
|
|
|
|
+
|
|
|
|
|
|
2.1. Windows
|
|
2.1. Windows
|
|
------------
|
|
------------
|
|
@@ -491,9 +496,10 @@ notarization process. To fix this there are two options. The first is to use the
|
|
#include "miniaudio.h"
|
|
#include "miniaudio.h"
|
|
```
|
|
```
|
|
|
|
|
|
-This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`.
|
|
|
|
-Alternatively, if you would rather keep using runtime linking you can add the following to your
|
|
|
|
-entitlements.xcent file:
|
|
|
|
|
|
+This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`.
|
|
|
|
+If you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when
|
|
|
|
+using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can
|
|
|
|
+add the following to your entitlements.xcent file:
|
|
|
|
|
|
```
|
|
```
|
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
|
@@ -534,6 +540,20 @@ you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link wi
|
|
The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box.
|
|
The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box.
|
|
You cannot use `-std=c*` compiler flags, nor `-ansi`.
|
|
You cannot use `-std=c*` compiler flags, nor `-ansi`.
|
|
|
|
|
|
|
|
+You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling
|
|
|
|
+with the following options:
|
|
|
|
+
|
|
|
|
+ -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY
|
|
|
|
+
|
|
|
|
+An example for compiling with AudioWorklet support might look like this:
|
|
|
|
+
|
|
|
|
+ emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY
|
|
|
|
+
|
|
|
|
+To run locally, you'll need to use emrun:
|
|
|
|
+
|
|
|
|
+ emrun bin/program.html
|
|
|
|
+
|
|
|
|
+
|
|
|
|
|
|
2.7. Build Options
|
|
2.7. Build Options
|
|
------------------
|
|
------------------
|
|
@@ -629,10 +649,29 @@ You cannot use `-std=c*` compiler flags, nor `-ansi`.
|
|
| | and `ma_device` APIs. This is useful if you only want to use |
|
|
| | and `ma_device` APIs. This is useful if you only want to use |
|
|
| | miniaudio's data conversion and/or decoding APIs. |
|
|
| | miniaudio's data conversion and/or decoding APIs. |
|
|
+----------------------------------+--------------------------------------------------------------------+
|
|
+----------------------------------+--------------------------------------------------------------------+
|
|
|
|
+ | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will |
|
|
|
|
+ | | also disable the following functions: |
|
|
|
|
+ | | |
|
|
|
|
+ | | ``` |
|
|
|
|
+ | | ma_sound_init_from_file() |
|
|
|
|
+ | | ma_sound_init_from_file_w() |
|
|
|
|
+ | | ma_sound_init_copy() |
|
|
|
|
+ | | ma_engine_play_sound_ex() |
|
|
|
|
+ | | ma_engine_play_sound() |
|
|
|
|
+ | | ``` |
|
|
|
|
+ | | |
|
|
|
|
+ | | The only way to initialize a `ma_sound` object is to initialize it |
|
|
|
|
+ | | from a data source. |
|
|
|
|
+ +----------------------------------+--------------------------------------------------------------------+
|
|
|
|
+ | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API |
|
|
|
|
+ | | because it depends on the node graph. |
|
|
|
|
+ +----------------------------------+--------------------------------------------------------------------+
|
|
|
|
+ | MA_NO_ENGINE | Disables the engine API. |
|
|
|
|
+ +----------------------------------+--------------------------------------------------------------------+
|
|
| MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and |
|
|
| MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and |
|
|
| | `ma_event` APIs. This option is useful if you only need to use |
|
|
| | `ma_event` APIs. This option is useful if you only need to use |
|
|
| | miniaudio for data conversion, decoding and/or encoding. Some |
|
|
| | miniaudio for data conversion, decoding and/or encoding. Some |
|
|
- | | families of APIsrequire threading which means the following |
|
|
|
|
|
|
+ | | families of APIs require threading which means the following |
|
|
| | options must also be set: |
|
|
| | options must also be set: |
|
|
| | |
|
|
| | |
|
|
| | ``` |
|
|
| | ``` |
|
|
@@ -731,7 +770,7 @@ To read data from a data source:
|
|
ma_result result;
|
|
ma_result result;
|
|
ma_uint64 framesRead;
|
|
ma_uint64 framesRead;
|
|
|
|
|
|
- result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead, loop);
|
|
|
|
|
|
+ result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead);
|
|
if (result != MA_SUCCESS) {
|
|
if (result != MA_SUCCESS) {
|
|
return result; // Failed to read data from the data source.
|
|
return result; // Failed to read data from the data source.
|
|
}
|
|
}
|
|
@@ -751,7 +790,7 @@ you could plug in a decoder like so:
|
|
ma_uint64 framesRead;
|
|
ma_uint64 framesRead;
|
|
ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`.
|
|
ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`.
|
|
|
|
|
|
- result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead, loop);
|
|
|
|
|
|
+ result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead);
|
|
if (result != MA_SUCCESS) {
|
|
if (result != MA_SUCCESS) {
|
|
return result; // Failed to read data from the decoder.
|
|
return result; // Failed to read data from the decoder.
|
|
}
|
|
}
|
|
@@ -805,7 +844,7 @@ retrieved like so:
|
|
ma_uint32 channels;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 sampleRate;
|
|
ma_channel channelMap[MA_MAX_CHANNELS];
|
|
ma_channel channelMap[MA_MAX_CHANNELS];
|
|
-
|
|
|
|
|
|
+
|
|
result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS);
|
|
result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS);
|
|
if (result != MA_SUCCESS) {
|
|
if (result != MA_SUCCESS) {
|
|
return result; // Failed to retrieve data format.
|
|
return result; // Failed to retrieve data format.
|
|
@@ -825,7 +864,9 @@ read data within a certain range of the underlying data. To do this you can use
|
|
```
|
|
```
|
|
|
|
|
|
This is useful if you have a sound bank where many sounds are stored in the same file and you want
|
|
This is useful if you have a sound bank where many sounds are stored in the same file and you want
|
|
-the data source to only play one of those sub-sounds.
|
|
|
|
|
|
+the data source to only play one of those sub-sounds. Note that once the range is set, everything
|
|
|
|
+that takes a position, such as cursors and loop points, should always be relatvie to the start of
|
|
|
|
+the range. When the range is set, any previously defined loop point will be reset.
|
|
|
|
|
|
Custom loop points can also be used with data sources. By default, data sources will loop after
|
|
Custom loop points can also be used with data sources. By default, data sources will loop after
|
|
they reach the end of the data source, but if you need to loop at a specific location, you can do
|
|
they reach the end of the data source, but if you need to loop at a specific location, you can do
|
|
@@ -854,19 +895,19 @@ To do this, you can use chaining:
|
|
return result; // Failed to set the next data source.
|
|
return result; // Failed to set the next data source.
|
|
}
|
|
}
|
|
|
|
|
|
- result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE);
|
|
|
|
|
|
+ result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead);
|
|
if (result != MA_SUCCESS) {
|
|
if (result != MA_SUCCESS) {
|
|
return result; // Failed to read from the decoder.
|
|
return result; // Failed to read from the decoder.
|
|
}
|
|
}
|
|
```
|
|
```
|
|
|
|
|
|
In the example above we're using decoders. When reading from a chain, you always want to read from
|
|
In the example above we're using decoders. When reading from a chain, you always want to read from
|
|
-the top level data source in the chain. In the example above, `decoder1` is the top level data
|
|
|
|
|
|
+the top level data source in the chain. In the example above, `decoder1` is the top level data
|
|
source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any
|
|
source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any
|
|
gaps.
|
|
gaps.
|
|
|
|
|
|
-Note that the `loop` parameter is set to false in the example above. When this is set to true, only
|
|
|
|
-the current data source will be looped. You can loop the entire chain by linking in a loop like so:
|
|
|
|
|
|
+Note that when looping is enabled, only the current data source will be looped. You can loop the
|
|
|
|
+entire chain by linking in a loop like so:
|
|
|
|
|
|
```c
|
|
```c
|
|
ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2
|
|
ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2
|
|
@@ -877,9 +918,9 @@ Note that setting up chaining is not thread safe, so care needs to be taken if y
|
|
changing links while the audio thread is in the middle of reading.
|
|
changing links while the audio thread is in the middle of reading.
|
|
|
|
|
|
Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple
|
|
Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple
|
|
-instances of the same sound simultaneously. Instead, initialize multiple data sources for each
|
|
|
|
-instance. This can be extremely inefficient depending on the data source and can result in
|
|
|
|
-glitching due to subtle changes to the state of internal filters.
|
|
|
|
|
|
+instances of the same sound simultaneously. This can be extremely inefficient depending on the type
|
|
|
|
+of data source and can result in glitching due to subtle changes to the state of internal filters.
|
|
|
|
+Instead, initialize multiple data sources for each instance.
|
|
|
|
|
|
|
|
|
|
4.1. Custom Data Sources
|
|
4.1. Custom Data Sources
|
|
@@ -924,7 +965,7 @@ base object (`ma_data_source_base`):
|
|
// Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown.
|
|
// Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown.
|
|
}
|
|
}
|
|
|
|
|
|
- static g_my_data_source_vtable =
|
|
|
|
|
|
+ static ma_data_source_vtable g_my_data_source_vtable =
|
|
{
|
|
{
|
|
my_data_source_read,
|
|
my_data_source_read,
|
|
my_data_source_seek,
|
|
my_data_source_seek,
|
|
@@ -954,7 +995,7 @@ base object (`ma_data_source_base`):
|
|
void my_data_source_uninit(my_data_source* pMyDataSource)
|
|
void my_data_source_uninit(my_data_source* pMyDataSource)
|
|
{
|
|
{
|
|
// ... do the uninitialization of your custom data source here ...
|
|
// ... do the uninitialization of your custom data source here ...
|
|
-
|
|
|
|
|
|
+
|
|
// You must uninitialize the base data source.
|
|
// You must uninitialize the base data source.
|
|
ma_data_source_uninit(&pMyDataSource->base);
|
|
ma_data_source_uninit(&pMyDataSource->base);
|
|
}
|
|
}
|
|
@@ -1003,7 +1044,7 @@ configure the engine with an engine config:
|
|
ma_engine_config engineConfig;
|
|
ma_engine_config engineConfig;
|
|
|
|
|
|
engineConfig = ma_engine_config_init();
|
|
engineConfig = ma_engine_config_init();
|
|
- engineConfig.pPlaybackDevice = &myDevice;
|
|
|
|
|
|
+ engineConfig.pDevice = &myDevice;
|
|
|
|
|
|
result = ma_engine_init(&engineConfig, &engine);
|
|
result = ma_engine_init(&engineConfig, &engine);
|
|
if (result != MA_SUCCESS) {
|
|
if (result != MA_SUCCESS) {
|
|
@@ -1044,7 +1085,7 @@ Note that when you're not using a device, you must set the channel count and sam
|
|
config or else miniaudio won't know what to use (miniaudio will use the device to determine this
|
|
config or else miniaudio won't know what to use (miniaudio will use the device to determine this
|
|
normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio
|
|
normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio
|
|
data from the engine. This kind of setup is useful if you want to do something like offline
|
|
data from the engine. This kind of setup is useful if you want to do something like offline
|
|
-processing.
|
|
|
|
|
|
+processing or want to use a different audio system for playback such as SDL.
|
|
|
|
|
|
When a sound is loaded it goes through a resource manager. By default the engine will initialize a
|
|
When a sound is loaded it goes through a resource manager. By default the engine will initialize a
|
|
resource manager internally, but you can also specify a pre-initialized resource manager:
|
|
resource manager internally, but you can also specify a pre-initialized resource manager:
|
|
@@ -1209,7 +1250,7 @@ might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_
|
|
|
|
|
|
By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until
|
|
By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until
|
|
the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously
|
|
the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously
|
|
-by specificying the `MA_SOUND_FLAG_ASYNC` flag:
|
|
|
|
|
|
+by specifying the `MA_SOUND_FLAG_ASYNC` flag:
|
|
|
|
|
|
```c
|
|
```c
|
|
ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);
|
|
ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);
|
|
@@ -1230,7 +1271,7 @@ counter hit's zero. You can specify a fence like so:
|
|
ma_sound sounds[4];
|
|
ma_sound sounds[4];
|
|
|
|
|
|
result = ma_fence_init(&fence);
|
|
result = ma_fence_init(&fence);
|
|
- if (result != MA_SUCCES) {
|
|
|
|
|
|
+ if (result != MA_SUCCESS) {
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1256,6 +1297,18 @@ When streaming sounds, 2 seconds worth of audio data is stored in memory. Althou
|
|
fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music
|
|
fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music
|
|
tracks in games.
|
|
tracks in games.
|
|
|
|
|
|
|
|
+When loading a sound from a file path, the engine will reference count the file to prevent it from
|
|
|
|
+being loaded if it's already in memory. When you uninitialize a sound, the reference count will be
|
|
|
|
+decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting
|
|
|
|
+system is not used for streams. The engine will use a 64-bit hash of the file name when comparing
|
|
|
|
+file paths which means there's a small chance you might encounter a name collision. If this is an
|
|
|
|
+issue, you'll need to use a different name for one of the colliding file paths, or just not load
|
|
|
|
+from files and instead load from a data source.
|
|
|
|
+
|
|
|
|
+You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this
|
|
|
|
+only works for sounds that were initialized with `ma_sound_init_from_file()` and without the
|
|
|
|
+`MA_SOUND_FLAG_STREAM` flag.
|
|
|
|
+
|
|
When you initialize a sound, if you specify a sound group the sound will be attached to that group
|
|
When you initialize a sound, if you specify a sound group the sound will be attached to that group
|
|
automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.
|
|
automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.
|
|
If you would instead rather leave the sound unattached by default, you can can specify the
|
|
If you would instead rather leave the sound unattached by default, you can can specify the
|
|
@@ -1395,19 +1448,19 @@ can be useful to schedule a sound to start or stop:
|
|
|
|
|
|
```c
|
|
```c
|
|
// Start the sound in 1 second from now.
|
|
// Start the sound in 1 second from now.
|
|
- ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1));
|
|
|
|
|
|
+ ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1));
|
|
|
|
|
|
// Stop the sound in 2 seconds from now.
|
|
// Stop the sound in 2 seconds from now.
|
|
- ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2));
|
|
|
|
|
|
+ ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2));
|
|
```
|
|
```
|
|
|
|
|
|
Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before
|
|
Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before
|
|
anything will play.
|
|
anything will play.
|
|
|
|
|
|
The time is specified in global time which is controlled by the engine. You can get the engine's
|
|
The time is specified in global time which is controlled by the engine. You can get the engine's
|
|
-current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as
|
|
|
|
-audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be
|
|
|
|
-resynchronized for some reason.
|
|
|
|
|
|
+current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented
|
|
|
|
+automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()`
|
|
|
|
+in case it needs to be resynchronized for some reason.
|
|
|
|
|
|
To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will
|
|
To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will
|
|
take the scheduled start and stop times into account.
|
|
take the scheduled start and stop times into account.
|
|
@@ -1416,7 +1469,25 @@ Whether or not a sound should loop can be controlled with `ma_sound_set_looping(
|
|
be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping.
|
|
be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping.
|
|
|
|
|
|
Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping
|
|
Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping
|
|
-sound this should never return true.
|
|
|
|
|
|
+sound this should never return true. Alternatively, you can configure a callback that will be fired
|
|
|
|
+when the sound reaches the end. Note that the callback is fired from the audio thread which means
|
|
|
|
+you cannot be uninitializing sound from the callback. To set the callback you can use
|
|
|
|
+`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it
|
|
|
|
+into the config like so:
|
|
|
|
+
|
|
|
|
+ ```c
|
|
|
|
+ soundConfig.endCallback = my_end_callback;
|
|
|
|
+ soundConfig.pEndCallbackUserData = pMyEndCallbackUserData;
|
|
|
|
+ ```
|
|
|
|
+
|
|
|
|
+The end callback is declared like so:
|
|
|
|
+
|
|
|
|
+ ```c
|
|
|
|
+ void my_end_callback(void* pUserData, ma_sound* pSound)
|
|
|
|
+ {
|
|
|
|
+ ...
|
|
|
|
+ }
|
|
|
|
+ ```
|
|
|
|
|
|
Internally a sound wraps around a data source. Some APIs exist to control the underlying data
|
|
Internally a sound wraps around a data source. Some APIs exist to control the underlying data
|
|
source, mainly for convenience:
|
|
source, mainly for convenience:
|
|
@@ -1431,7 +1502,7 @@ source, mainly for convenience:
|
|
Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do
|
|
Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do
|
|
not have any notion of a data source, anything relating to a data source is unavailable.
|
|
not have any notion of a data source, anything relating to a data source is unavailable.
|
|
|
|
|
|
-Internally, sound data is loaded via the `ma_decoder` API which means by default in only supports
|
|
|
|
|
|
+Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports
|
|
file formats that have built-in support in miniaudio. You can extend this to support any kind of
|
|
file formats that have built-in support in miniaudio. You can extend this to support any kind of
|
|
file format through the use of custom decoders. To do this you'll need to use a self-managed
|
|
file format through the use of custom decoders. To do this you'll need to use a self-managed
|
|
resource manager and configure it appropriately. See the "Resource Management" section below for
|
|
resource manager and configure it appropriately. See the "Resource Management" section below for
|
|
@@ -1446,7 +1517,7 @@ streaming. This is supported by miniaudio via the `ma_resource_manager` API.
|
|
The resource manager is mainly responsible for the following:
|
|
The resource manager is mainly responsible for the following:
|
|
|
|
|
|
* Loading of sound files into memory with reference counting.
|
|
* Loading of sound files into memory with reference counting.
|
|
- * Streaming of sound data
|
|
|
|
|
|
+ * Streaming of sound data.
|
|
|
|
|
|
When loading a sound file, the resource manager will give you back a `ma_data_source` compatible
|
|
When loading a sound file, the resource manager will give you back a `ma_data_source` compatible
|
|
object called `ma_resource_manager_data_source`. This object can be passed into any
|
|
object called `ma_resource_manager_data_source`. This object can be passed into any
|
|
@@ -1541,7 +1612,7 @@ need to retrieve a job using `ma_resource_manager_next_job()` and then process i
|
|
ma_job job;
|
|
ma_job job;
|
|
ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);
|
|
ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);
|
|
if (result != MA_SUCCESS) {
|
|
if (result != MA_SUCCESS) {
|
|
- if (result == MA_NOT_DATA_AVAILABLE) {
|
|
|
|
|
|
+ if (result == MA_NO_DATA_AVAILABLE) {
|
|
// No jobs are available. Keep going. Will only get this if the resource manager was initialized
|
|
// No jobs are available. Keep going. Will only get this if the resource manager was initialized
|
|
// with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.
|
|
// with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.
|
|
continue;
|
|
continue;
|
|
@@ -1580,7 +1651,7 @@ default. This can be done by setting `pVFS` member of the resource manager's con
|
|
|
|
|
|
This is particularly useful in programs like games where you want to read straight from an archive
|
|
This is particularly useful in programs like games where you want to read straight from an archive
|
|
rather than the normal file system. If you do not specify a custom VFS, the resource manager will
|
|
rather than the normal file system. If you do not specify a custom VFS, the resource manager will
|
|
-use the operating system's normal file operations. This is default.
|
|
|
|
|
|
+use the operating system's normal file operations.
|
|
|
|
|
|
To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When
|
|
To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When
|
|
loading a sound you need to specify the file path and options for how the sounds should be loaded.
|
|
loading a sound you need to specify the file path and options for how the sounds should be loaded.
|
|
@@ -1606,7 +1677,7 @@ an example for initializing a data source:
|
|
|
|
|
|
// ...
|
|
// ...
|
|
|
|
|
|
- ma_resource_manager_data_source_uninit(pResourceManager, &dataSource);
|
|
|
|
|
|
+ ma_resource_manager_data_source_uninit(&dataSource);
|
|
```
|
|
```
|
|
|
|
|
|
The `flags` parameter specifies how you want to perform loading of the sound file. It can be a
|
|
The `flags` parameter specifies how you want to perform loading of the sound file. It can be a
|
|
@@ -1843,19 +1914,21 @@ once after the other:
|
|
|
|
|
|
```c
|
|
```c
|
|
ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load.
|
|
ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load.
|
|
- ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded.
|
|
|
|
|
|
+ ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded.
|
|
|
|
|
|
ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it.
|
|
ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it.
|
|
- ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded.
|
|
|
|
|
|
+ ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded.
|
|
```
|
|
```
|
|
|
|
|
|
A binary search tree (BST) is used for storing data buffers as it has good balance between
|
|
A binary search tree (BST) is used for storing data buffers as it has good balance between
|
|
efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed
|
|
efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed
|
|
into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves
|
|
into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves
|
|
memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST
|
|
memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST
|
|
-due to the random nature of the hash. The disadvantage is that file names are case-sensitive. If
|
|
|
|
-this is an issue, you should normalize your file names to upper- or lower-case before initializing
|
|
|
|
-your data sources.
|
|
|
|
|
|
+due to the random nature of the hash. The disadvantages are that file names are case-sensitive and
|
|
|
|
+there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize
|
|
|
|
+your file names to upper- or lower-case before initializing your data sources. If name collisions
|
|
|
|
+become an issue, you'll need to change the name of one of the colliding names or just not use the
|
|
|
|
+resource manager.
|
|
|
|
|
|
When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC`
|
|
When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC`
|
|
flag is excluded, the file will be decoded synchronously by the calling thread. There are two
|
|
flag is excluded, the file will be decoded synchronously by the calling thread. There are two
|
|
@@ -1935,7 +2008,7 @@ miniaudio's routing infrastructure follows a node graph paradigm. The idea is th
|
|
node whose outputs are attached to inputs of another node, thereby creating a graph. There are
|
|
node whose outputs are attached to inputs of another node, thereby creating a graph. There are
|
|
different types of nodes, with each node in the graph processing input data to produce output,
|
|
different types of nodes, with each node in the graph processing input data to produce output,
|
|
which is then fed through the chain. Each node in the graph can apply their own custom effects. At
|
|
which is then fed through the chain. Each node in the graph can apply their own custom effects. At
|
|
-the start of the graph will usually be one or more data source nodes which have no inputs, but
|
|
|
|
|
|
+the start of the graph will usually be one or more data source nodes which have no inputs and
|
|
instead pull their data from a data source. At the end of the graph is an endpoint which represents
|
|
instead pull their data from a data source. At the end of the graph is an endpoint which represents
|
|
the end of the chain and is where the final output is ultimately extracted from.
|
|
the end of the chain and is where the final output is ultimately extracted from.
|
|
|
|
|
|
@@ -1961,7 +2034,7 @@ splitter node. It's at this point that the two data sources are mixed. After mix
|
|
performs it's processing routine and produces two outputs which is simply a duplication of the
|
|
performs it's processing routine and produces two outputs which is simply a duplication of the
|
|
input stream. One output is attached to a low pass filter, whereas the other output is attached to
|
|
input stream. One output is attached to a low pass filter, whereas the other output is attached to
|
|
a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and
|
|
a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and
|
|
-since they're both connected to the same input but, they'll be mixed.
|
|
|
|
|
|
+since they're both connected to the same input bus, they'll be mixed.
|
|
|
|
|
|
Each input bus must be configured to accept the same number of channels, but the number of channels
|
|
Each input bus must be configured to accept the same number of channels, but the number of channels
|
|
used by input buses can be different to the number of channels for output buses in which case
|
|
used by input buses can be different to the number of channels for output buses in which case
|
|
@@ -2001,14 +2074,14 @@ data from the graph:
|
|
```
|
|
```
|
|
|
|
|
|
When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
|
|
When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
|
|
-data from it's input attachments, which in turn recusively pull in data from their inputs, and so
|
|
|
|
|
|
+data from it's input attachments, which in turn recursively pull in data from their inputs, and so
|
|
on. At the start of the graph there will be some kind of data source node which will have zero
|
|
on. At the start of the graph there will be some kind of data source node which will have zero
|
|
inputs and will instead read directly from a data source. The base nodes don't literally need to
|
|
inputs and will instead read directly from a data source. The base nodes don't literally need to
|
|
read from a `ma_data_source` object, but they will always have some kind of underlying object that
|
|
read from a `ma_data_source` object, but they will always have some kind of underlying object that
|
|
sources some kind of audio. The `ma_data_source_node` node can be used to read from a
|
|
sources some kind of audio. The `ma_data_source_node` node can be used to read from a
|
|
`ma_data_source`. Data is always in floating-point format and in the number of channels you
|
|
`ma_data_source`. Data is always in floating-point format and in the number of channels you
|
|
specified when the graph was initialized. The sample rate is defined by the underlying data sources.
|
|
specified when the graph was initialized. The sample rate is defined by the underlying data sources.
|
|
-It's up to you to ensure they use a consistent and appropraite sample rate.
|
|
|
|
|
|
+It's up to you to ensure they use a consistent and appropriate sample rate.
|
|
|
|
|
|
The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but
|
|
The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but
|
|
miniaudio includes a few stock nodes for common functionality. This is how you would initialize a
|
|
miniaudio includes a few stock nodes for common functionality. This is how you would initialize a
|
|
@@ -2049,7 +2122,7 @@ another, you do not need to detach first. You can just call `ma_node_attach_outp
|
|
deal with it for you.
|
|
deal with it for you.
|
|
|
|
|
|
Less frequently you may want to create a specialized node. This will be a node where you implement
|
|
Less frequently you may want to create a specialized node. This will be a node where you implement
|
|
-your own processing callback to apply a custom effect of some kind. This is similar to initalizing
|
|
|
|
|
|
+your own processing callback to apply a custom effect of some kind. This is similar to initializing
|
|
one of the stock node types, only this time you need to specify a pointer to a vtable containing a
|
|
one of the stock node types, only this time you need to specify a pointer to a vtable containing a
|
|
pointer to the processing function and the number of input and output buses. Example:
|
|
pointer to the processing function and the number of input and output buses. Example:
|
|
|
|
|
|
@@ -2076,7 +2149,7 @@ pointer to the processing function and the number of input and output buses. Exa
|
|
|
|
|
|
static ma_node_vtable my_custom_node_vtable =
|
|
static ma_node_vtable my_custom_node_vtable =
|
|
{
|
|
{
|
|
- my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing.
|
|
|
|
|
|
+ my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing.
|
|
NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
|
|
NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
|
|
2, // 2 input buses.
|
|
2, // 2 input buses.
|
|
1, // 1 output bus.
|
|
1, // 1 output bus.
|
|
@@ -2088,7 +2161,7 @@ pointer to the processing function and the number of input and output buses. Exa
|
|
// Each bus needs to have a channel count specified. To do this you need to specify the channel
|
|
// Each bus needs to have a channel count specified. To do this you need to specify the channel
|
|
// counts in an array and then pass that into the node config.
|
|
// counts in an array and then pass that into the node config.
|
|
ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable.
|
|
ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable.
|
|
- ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specicied in the vtable.
|
|
|
|
|
|
+ ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable.
|
|
|
|
|
|
inputChannels[0] = channelsIn;
|
|
inputChannels[0] = channelsIn;
|
|
inputChannels[1] = channelsIn;
|
|
inputChannels[1] = channelsIn;
|
|
@@ -2172,10 +2245,19 @@ and include the following:
|
|
+-----------------------------------------+---------------------------------------------------+
|
|
+-----------------------------------------+---------------------------------------------------+
|
|
| MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even |
|
|
| MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even |
|
|
| | when no data is available to be read from input |
|
|
| | when no data is available to be read from input |
|
|
- | | attachments. This is useful for effects like |
|
|
|
|
|
|
+ | | attachments. When a node has at least one input |
|
|
|
|
+ | | bus, but there are no inputs attached or the |
|
|
|
|
+ | | inputs do not deliver any data, the node's |
|
|
|
|
+ | | processing callback will not get fired. This flag |
|
|
|
|
+ | | will make it so the callback is always fired |
|
|
|
|
+ | | regardless of whether or not any input data is |
|
|
|
|
+ | | received. This is useful for effects like |
|
|
| | echos where there will be a tail of audio data |
|
|
| | echos where there will be a tail of audio data |
|
|
| | that still needs to be processed even when the |
|
|
| | that still needs to be processed even when the |
|
|
- | | original data sources have reached their ends. |
|
|
|
|
|
|
+ | | original data sources have reached their ends. It |
|
|
|
|
+ | | may also be useful for nodes that must always |
|
|
|
|
+ | | have their processing callback fired when there |
|
|
|
|
+ | | are no inputs attached. |
|
|
+-----------------------------------------+---------------------------------------------------+
|
|
+-----------------------------------------+---------------------------------------------------+
|
|
| MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with |
|
|
| MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with |
|
|
| | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this |
|
|
| | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this |
|
|
@@ -2206,7 +2288,7 @@ called `ma_splitter_node`. This takes has 1 input bus and splits the stream into
|
|
You can use it like this:
|
|
You can use it like this:
|
|
|
|
|
|
```c
|
|
```c
|
|
- ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut);
|
|
|
|
|
|
+ ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels);
|
|
|
|
|
|
ma_splitter_node splitterNode;
|
|
ma_splitter_node splitterNode;
|
|
result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
|
|
result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
|
|
@@ -2366,7 +2448,7 @@ bus and input bus is locked. This locking is specifically for attaching and deta
|
|
different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and
|
|
different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and
|
|
unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when
|
|
unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when
|
|
considering that iterating over attachments must not break as a result of attaching or detaching a
|
|
considering that iterating over attachments must not break as a result of attaching or detaching a
|
|
-node while iteration is occuring.
|
|
|
|
|
|
+node while iteration is occurring.
|
|
|
|
|
|
Attaching and detaching are both quite simple. When an output bus of a node is attached to an input
|
|
Attaching and detaching are both quite simple. When an output bus of a node is attached to an input
|
|
bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where
|
|
bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where
|
|
@@ -2394,37 +2476,18 @@ used. The same general process applies to detachment. See `ma_node_attach_output
|
|
8. Decoding
|
|
8. Decoding
|
|
===========
|
|
===========
|
|
The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from
|
|
The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from
|
|
-devices and can be used independently. The following formats are supported:
|
|
|
|
|
|
+devices and can be used independently. Built-in support is included for the following formats:
|
|
|
|
|
|
- +---------+------------------+----------+
|
|
|
|
- | Format | Decoding Backend | Built-In |
|
|
|
|
- +---------+------------------+----------+
|
|
|
|
- | WAV | dr_wav | Yes |
|
|
|
|
- | MP3 | dr_mp3 | Yes |
|
|
|
|
- | FLAC | dr_flac | Yes |
|
|
|
|
- | Vorbis | stb_vorbis | No |
|
|
|
|
- +---------+------------------+----------+
|
|
|
|
|
|
+ +---------+
|
|
|
|
+ | Format |
|
|
|
|
+ +---------+
|
|
|
|
+ | WAV |
|
|
|
|
+ | MP3 |
|
|
|
|
+ | FLAC |
|
|
|
|
+ +---------+
|
|
|
|
|
|
-Vorbis is supported via stb_vorbis which can be enabled by including the header section before the
|
|
|
|
-implementation of miniaudio, like the following:
|
|
|
|
-
|
|
|
|
- ```c
|
|
|
|
- #define STB_VORBIS_HEADER_ONLY
|
|
|
|
- #include "extras/stb_vorbis.c" // Enables Vorbis decoding.
|
|
|
|
-
|
|
|
|
- #define MINIAUDIO_IMPLEMENTATION
|
|
|
|
- #include "miniaudio.h"
|
|
|
|
-
|
|
|
|
- // The stb_vorbis implementation must come after the implementation of miniaudio.
|
|
|
|
- #undef STB_VORBIS_HEADER_ONLY
|
|
|
|
- #include "extras/stb_vorbis.c"
|
|
|
|
- ```
|
|
|
|
-
|
|
|
|
-A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio).
|
|
|
|
-
|
|
|
|
-Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the
|
|
|
|
-built-in decoders by specifying one or more of the following options before the miniaudio
|
|
|
|
-implementation:
|
|
|
|
|
|
+You can disable the built-in decoders by specifying one or more of the following options before the
|
|
|
|
+miniaudio implementation:
|
|
|
|
|
|
```c
|
|
```c
|
|
#define MA_NO_WAV
|
|
#define MA_NO_WAV
|
|
@@ -2432,8 +2495,8 @@ implementation:
|
|
#define MA_NO_FLAC
|
|
#define MA_NO_FLAC
|
|
```
|
|
```
|
|
|
|
|
|
-Disabling built-in decoding libraries is useful if you use these libraries independantly of the
|
|
|
|
-`ma_decoder` API.
|
|
|
|
|
|
+miniaudio supports the ability to plug in custom decoders. See the section below for details on how
|
|
|
|
+to use custom decoders.
|
|
|
|
|
|
A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with
|
|
A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with
|
|
`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is
|
|
`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is
|
|
@@ -2534,7 +2597,7 @@ The `ma_decoding_backend_vtable` vtable has the following functions:
|
|
|
|
|
|
```
|
|
```
|
|
onInit
|
|
onInit
|
|
- onInitFile
|
|
|
|
|
|
+ onInitFile
|
|
onInitFileW
|
|
onInitFileW
|
|
onInitMemory
|
|
onInitMemory
|
|
onUninit
|
|
onUninit
|
|
@@ -2546,11 +2609,11 @@ these are not specified, miniaudio will deal with it for you via a generic imple
|
|
|
|
|
|
When you initialize a custom data source (by implementing the `onInit` function in the vtable) you
|
|
When you initialize a custom data source (by implementing the `onInit` function in the vtable) you
|
|
will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the
|
|
will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the
|
|
-section about data sources for details on how to implemen this. Alternatively, see the
|
|
|
|
|
|
+section about data sources for details on how to implement this. Alternatively, see the
|
|
"custom_decoders" example in the miniaudio repository.
|
|
"custom_decoders" example in the miniaudio repository.
|
|
|
|
|
|
The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data
|
|
The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data
|
|
-from some abitrary source. You'll use these functions to read from the raw data and perform the
|
|
|
|
|
|
+from some arbitrary source. You'll use these functions to read from the raw data and perform the
|
|
decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant
|
|
decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant
|
|
parameter.
|
|
parameter.
|
|
|
|
|
|
@@ -2574,8 +2637,7 @@ opportunity to clean up and internal data.
|
|
|
|
|
|
9. Encoding
|
|
9. Encoding
|
|
===========
|
|
===========
|
|
-The `ma_encoding` API is used for writing audio files. The only supported output format is WAV
|
|
|
|
-which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio.
|
|
|
|
|
|
+The `ma_encoding` API is used for writing audio files. The only supported output format is WAV.
|
|
This can be disabled by specifying the following option before the implementation of miniaudio:
|
|
This can be disabled by specifying the following option before the implementation of miniaudio:
|
|
|
|
|
|
```c
|
|
```c
|
|
@@ -2615,9 +2677,16 @@ outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frame
|
|
example below:
|
|
example below:
|
|
|
|
|
|
```c
|
|
```c
|
|
- framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite);
|
|
|
|
|
|
+ ma_uint64 framesWritten;
|
|
|
|
+ result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten);
|
|
|
|
+ if (result != MA_SUCCESS) {
|
|
|
|
+ ... handle error ...
|
|
|
|
+ }
|
|
```
|
|
```
|
|
|
|
|
|
|
|
+The `framesWritten` variable will contain the number of PCM frames that were actually written. This
|
|
|
|
+is optionally and you can pass in `NULL` if you need this.
|
|
|
|
+
|
|
Encoders must be uninitialized with `ma_encoder_uninit()`.
|
|
Encoders must be uninitialized with `ma_encoder_uninit()`.
|
|
|
|
|
|
|
|
|
|
@@ -2701,7 +2770,7 @@ To perform the conversion simply call `ma_channel_converter_process_pcm_frames()
|
|
}
|
|
}
|
|
```
|
|
```
|
|
|
|
|
|
-It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM
|
|
|
|
|
|
+It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM
|
|
frames.
|
|
frames.
|
|
|
|
|
|
Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
|
|
Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
|
|
@@ -3147,7 +3216,7 @@ you can chain first and second order filters together.
|
|
|
|
|
|
If you need to change the configuration of the filter, but need to maintain the state of internal
|
|
If you need to change the configuration of the filter, but need to maintain the state of internal
|
|
registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample
|
|
registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample
|
|
-rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the
|
|
|
|
|
|
+rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the
|
|
format or channel count after initialization is invalid and will result in an error.
|
|
format or channel count after initialization is invalid and will result in an error.
|
|
|
|
|
|
The `ma_lpf` object supports a configurable order, but if you only need a first order filter you
|
|
The `ma_lpf` object supports a configurable order, but if you only need a first order filter you
|
|
@@ -3320,8 +3389,8 @@ The noise API uses simple LCG random number generation. It supports a custom see
|
|
for things like automated testing requiring reproducibility. Setting the seed to zero will default
|
|
for things like automated testing requiring reproducibility. Setting the seed to zero will default
|
|
to `MA_DEFAULT_LCG_SEED`.
|
|
to `MA_DEFAULT_LCG_SEED`.
|
|
|
|
|
|
-The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`,
|
|
|
|
-`ma_noise_set_seed()`, and `ma_noise_set_type()` respectively.
|
|
|
|
|
|
+The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and
|
|
|
|
+`ma_noise_set_seed()` respectively.
|
|
|
|
|
|
By default, the noise API will use different values for different channels. So, for example, the
|
|
By default, the noise API will use different values for different channels. So, for example, the
|
|
left side in a stereo stream will be different to the right side. To instead have each channel use
|
|
left side in a stereo stream will be different to the right side. To instead have each channel use
|
|
@@ -3349,7 +3418,7 @@ miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buf
|
|
read from memory that's managed by the application, but can also handle the memory management for
|
|
read from memory that's managed by the application, but can also handle the memory management for
|
|
you internally. Memory management is flexible and should support most use cases.
|
|
you internally. Memory management is flexible and should support most use cases.
|
|
|
|
|
|
-Audio buffers are initialised using the standard configuration system used everywhere in miniaudio:
|
|
|
|
|
|
+Audio buffers are initialized using the standard configuration system used everywhere in miniaudio:
|
|
|
|
|
|
```c
|
|
```c
|
|
ma_audio_buffer_config config = ma_audio_buffer_config_init(
|
|
ma_audio_buffer_config config = ma_audio_buffer_config_init(
|
|
@@ -3469,7 +3538,7 @@ you will want to use. To initialize a ring buffer, do something like the followi
|
|
```
|
|
```
|
|
|
|
|
|
The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because
|
|
The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because
|
|
-it's the PCM varient of the ring buffer API. For the regular ring buffer that operates on bytes you
|
|
|
|
|
|
+it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you
|
|
would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes
|
|
would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes
|
|
instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter
|
|
instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter
|
|
is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines.
|
|
is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines.
|
|
@@ -3516,21 +3585,26 @@ producer thread.
|
|
|
|
|
|
15. Backends
|
|
15. Backends
|
|
============
|
|
============
|
|
-The following backends are supported by miniaudio.
|
|
|
|
|
|
+The following backends are supported by miniaudio. These are listed in order of default priority.
|
|
|
|
+When no backend is specified when initializing a context or device, miniaudio will attempt to use
|
|
|
|
+each of these backends in the order listed in the table below.
|
|
|
|
+
|
|
|
|
+Note that backends that are not usable by the build target will not be included in the build. For
|
|
|
|
+example, ALSA, which is specific to Linux, will not be included in the Windows build.
|
|
|
|
|
|
+-------------+-----------------------+--------------------------------------------------------+
|
|
+-------------+-----------------------+--------------------------------------------------------+
|
|
| Name | Enum Name | Supported Operating Systems |
|
|
| Name | Enum Name | Supported Operating Systems |
|
|
+-------------+-----------------------+--------------------------------------------------------+
|
|
+-------------+-----------------------+--------------------------------------------------------+
|
|
| WASAPI | ma_backend_wasapi | Windows Vista+ |
|
|
| WASAPI | ma_backend_wasapi | Windows Vista+ |
|
|
| DirectSound | ma_backend_dsound | Windows XP+ |
|
|
| DirectSound | ma_backend_dsound | Windows XP+ |
|
|
- | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
|
|
|
|
|
|
+ | WinMM | ma_backend_winmm | Windows 95+ |
|
|
| Core Audio | ma_backend_coreaudio | macOS, iOS |
|
|
| Core Audio | ma_backend_coreaudio | macOS, iOS |
|
|
- | ALSA | ma_backend_alsa | Linux |
|
|
|
|
- | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
|
|
|
|
- | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
|
|
|
|
| sndio | ma_backend_sndio | OpenBSD |
|
|
| sndio | ma_backend_sndio | OpenBSD |
|
|
| audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
|
|
| audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
|
|
| OSS | ma_backend_oss | FreeBSD |
|
|
| OSS | ma_backend_oss | FreeBSD |
|
|
|
|
+ | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
|
|
|
|
+ | ALSA | ma_backend_alsa | Linux |
|
|
|
|
+ | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
|
|
| AAudio | ma_backend_aaudio | Android 8+ |
|
|
| AAudio | ma_backend_aaudio | Android 8+ |
|
|
| OpenSL ES | ma_backend_opensl | Android (API level 16+) |
|
|
| OpenSL ES | ma_backend_opensl | Android (API level 16+) |
|
|
| Web Audio | ma_backend_webaudio | Web (via Emscripten) |
|
|
| Web Audio | ma_backend_webaudio | Web (via Emscripten) |
|
|
@@ -3569,6 +3643,12 @@ Some backends have some nuance details you may want to be aware of.
|
|
miniaudio's built-in resampler is to take advantage of any potential device-specific
|
|
miniaudio's built-in resampler is to take advantage of any potential device-specific
|
|
optimizations the driver may implement.
|
|
optimizations the driver may implement.
|
|
|
|
|
|
|
|
+BSD
|
|
|
|
+---
|
|
|
|
+- The sndio backend is currently only enabled on OpenBSD builds.
|
|
|
|
+- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can
|
|
|
|
+ use it.
|
|
|
|
+
|
|
15.4. UWP
|
|
15.4. UWP
|
|
---------
|
|
---------
|
|
- UWP only supports default playback and capture devices.
|
|
- UWP only supports default playback and capture devices.
|
|
@@ -3599,14 +3679,28 @@ Some backends have some nuance details you may want to be aware of.
|
|
|
|
|
|
16. Optimization Tips
|
|
16. Optimization Tips
|
|
=====================
|
|
=====================
|
|
|
|
+See below for some tips on improving performance.
|
|
|
|
|
|
-16.1. High Level API
|
|
|
|
|
|
+16.1. Low Level API
|
|
|
|
+-------------------
|
|
|
|
+- In the data callback, if your data is already clipped prior to copying it into the output buffer,
|
|
|
|
+ set the `noClip` config option in the device config to true. This will disable miniaudio's built
|
|
|
|
+ in clipping function.
|
|
|
|
+- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you
|
|
|
|
+ will always write valid data to the output buffer you can disable pre-silencing by setting the
|
|
|
|
+ `noPreSilence` config option in the device config to true.
|
|
|
|
+
|
|
|
|
+16.2. High Level API
|
|
--------------------
|
|
--------------------
|
|
- If a sound does not require doppler or pitch shifting, consider disabling pitching by
|
|
- If a sound does not require doppler or pitch shifting, consider disabling pitching by
|
|
initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag.
|
|
initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag.
|
|
-- If a sound does not require spatialization, disable it by initialzing the sound with the
|
|
|
|
- `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be renabled again post-initialization with
|
|
|
|
|
|
+- If a sound does not require spatialization, disable it by initializing the sound with the
|
|
|
|
+ `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with
|
|
`ma_sound_set_spatialization_enabled()`.
|
|
`ma_sound_set_spatialization_enabled()`.
|
|
|
|
+- If you know all of your sounds will always be the same sample rate, set the engine's sample
|
|
|
|
+ rate to match that of the sounds. Likewise, if you're using a self-managed resource manager,
|
|
|
|
+ consider setting the decoded sample rate to match your sounds. By configuring everything to
|
|
|
|
+ use a consistent sample rate, sample rate conversion can be avoided.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -3615,17 +3709,6 @@ Some backends have some nuance details you may want to be aware of.
|
|
- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for
|
|
- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for
|
|
WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though
|
|
WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though
|
|
not all have been tested.
|
|
not all have been tested.
|
|
-- The contents of the output buffer passed into the data callback will always be pre-initialized to
|
|
|
|
- silence unless the `noPreSilencedOutputBuffer` config variable in `ma_device_config` is set to
|
|
|
|
- true, in which case it'll be undefined which will require you to write something to the entire
|
|
|
|
- buffer.
|
|
|
|
-- By default miniaudio will automatically clip samples. This only applies when the playback sample
|
|
|
|
- format is configured as `ma_format_f32`. If you are doing clipping yourself, you can disable this
|
|
|
|
- overhead by setting `noClip` to true in the device config.
|
|
|
|
-- Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations.
|
|
|
|
-- The sndio backend is currently only enabled on OpenBSD builds.
|
|
|
|
-- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can
|
|
|
|
- use it.
|
|
|
|
- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This
|
|
- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This
|
|
is due to 64-bit file APIs not being available.
|
|
is due to 64-bit file APIs not being available.
|
|
*/
|
|
*/
|