mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
6180 lines
257 KiB
HTML
6180 lines
257 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>miniaudio - A single file audio playback and capture library.</title>
|
|
<meta name="description" content="miniaudio is a single file audio playback and capture library written in C.">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<link rel="icon" href="../../img/favicon.png">
|
|
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-81135233-2"></script>
|
|
<script>
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
gtag('config', 'UA-81135233-2');
|
|
</script>
|
|
|
|
<style>
|
|
body {
|
|
font-family:sans-serif;
|
|
font-size:11pt;
|
|
line-height:18pt;
|
|
background-color:#003800;
|
|
}
|
|
|
|
h1,h2 {
|
|
color:#333;
|
|
line-height:0.2em;
|
|
margin-bottom:0;
|
|
padding:0;
|
|
}
|
|
h1.man {
|
|
margin-top:2em;
|
|
}
|
|
h2.man {
|
|
margin-top:1.5em;
|
|
}
|
|
|
|
a {
|
|
text-decoration:none;
|
|
color:#28f;
|
|
}
|
|
a:hover {
|
|
text-decoration:underline;
|
|
color:#26d;
|
|
}
|
|
|
|
.a-download {
|
|
text-decoration:none;
|
|
color:#ddd;
|
|
border:solid 1px #000;
|
|
border-radius:4px;
|
|
padding:16px 32px;
|
|
background-color:#003800;
|
|
}
|
|
.a-download:hover {
|
|
background-color:#003000;
|
|
text-decoration:none;
|
|
color:#ddd;
|
|
}
|
|
|
|
.a-sublink {
|
|
font-size:11pt;
|
|
}
|
|
|
|
#preview {
|
|
font-family:monospace;
|
|
font-size:10pt;
|
|
text-align:left;
|
|
}
|
|
|
|
.footer-links {
|
|
margin: 0px;
|
|
margin-bottom: 10px;
|
|
padding: 0px;
|
|
}
|
|
.footer-links li {
|
|
display: inline;
|
|
padding: 0 2px;
|
|
}
|
|
.footer-links li:first-child {
|
|
padding-left: 0;
|
|
}
|
|
|
|
.feature-header {
|
|
color:#666;
|
|
font-size: 24pt;
|
|
font-weight:bold;
|
|
}
|
|
.feature-header2 {
|
|
color:#444;
|
|
font-size: 1.5em;
|
|
font-weight:bold;
|
|
/*margin-bottom:1em;*/
|
|
line-height: 1em;
|
|
text-align:left;
|
|
}
|
|
|
|
.header-link-table {
|
|
}
|
|
.header-link-table td {
|
|
padding-right:1em;
|
|
vertical-align:center;
|
|
line-height:0;
|
|
/*border:solid 1px #f00;*/
|
|
}
|
|
.header-link-table a {
|
|
/*color:#e0d7cf;*/
|
|
color:#dddddd;
|
|
text-decoration:none;
|
|
}
|
|
.header-link-table a:hover {
|
|
color:#ffffff;
|
|
}
|
|
|
|
.footer-link {
|
|
color:#e0d7cf;
|
|
text-decoration:none;
|
|
}
|
|
.footer-link:hover {
|
|
color:#ffffff;
|
|
}
|
|
|
|
|
|
|
|
.mobile-main-link {
|
|
text-align:left;
|
|
background-color:#e0d7cf;
|
|
color:#036;
|
|
border-bottom:solid 1px #333;
|
|
padding-left:16px;
|
|
}
|
|
.mobile-main-link a {
|
|
display:block;
|
|
padding-top:8px;
|
|
padding-bottom:8px;
|
|
color:#036;
|
|
width:100%;
|
|
height:100%;
|
|
max-width:100%;
|
|
}
|
|
|
|
|
|
table.doc {
|
|
border:solid 0px #333;
|
|
border-collapse:collapse;
|
|
}
|
|
|
|
th.doc, td.doc {
|
|
padding:0.5em;
|
|
}
|
|
|
|
th.doc {
|
|
border:solid 1px #003800;
|
|
background-color:#003800;
|
|
color:#FFF;
|
|
text-align:left;
|
|
}
|
|
|
|
td.doc {
|
|
border:solid 1px #666;
|
|
}
|
|
|
|
td.doc p, th.doc p {
|
|
padding:0;
|
|
margin:0;
|
|
}
|
|
|
|
a.doc-navigation {
|
|
display:block;
|
|
padding:0.5em;
|
|
color:#003800;
|
|
border-bottom:solid 1px #bbbbbb;
|
|
}
|
|
|
|
a.doc-navigation:hover {
|
|
color:#fff;
|
|
background-color:#003800;
|
|
text-decoration:none;
|
|
/*border-bottom:solid 1px #003800;*/
|
|
}
|
|
|
|
/*
|
|
a.doc-navigation:hover {
|
|
background-color:#c5ecc5;
|
|
text-decoration:none;
|
|
}
|
|
*/
|
|
|
|
a.doc-navigation-active {
|
|
background-color:#cccccc;
|
|
}
|
|
a.doc-navigation-active:hover {
|
|
color:#003800;
|
|
background-color:#cccccc;
|
|
}
|
|
|
|
a.doc-navigation-l1 {
|
|
padding:0.1em;
|
|
padding-left:1.5em;
|
|
}
|
|
a.doc-navigation-l2 {
|
|
padding:0.1em;
|
|
padding-left:3em;
|
|
}
|
|
a.doc-navigation-l3 {
|
|
padding:0.1em;
|
|
padding-left:4em;
|
|
}
|
|
a.doc-navigation-l4 {
|
|
padding:0.1em;
|
|
padding-left:5em;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body style="margin:0; padding:0">
|
|
<div style="background-color:#003800; color:#bfa792;">
|
|
<div style="max-width:100%; width:100%; margin:0 auto;">
|
|
<table class="header-link-table" style="border-collapse:collapse; border-spacing:0; padding:0; padding-right:1em;">
|
|
<tr>
|
|
<td style="padding:0.75em; width:100%; text-align:left;">
|
|
<table class="header-link-table" style="border-collapse:collapse; margin:0; padding:0">
|
|
<tr>
|
|
<td style="vertical-align:bottom; padding:0em; padding-right:2em;"><a href="../../index.html"><img src="../../img/logo1_large_white.png" style="height:24px; min-width:100%;"></a></td>
|
|
<td><a href="index.html">Documentation</a></td>
|
|
<td><a href="../examples/index.html">Examples</a></td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
|
|
<td style="padding:0.1em; width:25%; text-align:right; vertical-align:center;">
|
|
<a href="https://www.reddit.com/r/miniaudio"><img src="../../img/reddit_white.svg" style="margin:0; padding:0; height:40px; width:40px;"></a>
|
|
</td>
|
|
<td style="padding:0.1em; width:25%; text-align:right; vertical-align:center;">
|
|
<a href="https://discord.gg/9vpqbjU"><img src="../../img/Discord-Logo-White.svg" style="margin:0; padding:0; height:32px; width:32px;"></a>
|
|
</td>
|
|
<td style="padding:0.1em; width:25%; text-align:right; vertical-align:center;">
|
|
<a href="https://twitter.com/mackron"><img src="../../img/twitter_white.png" style="margin:0; padding:0; height:32px; width:32px;"></a>
|
|
</td>
|
|
<td style="padding:0.1em; padding-right:1em; width:25%; text-align:right; vertical-align:center;">
|
|
<a href="https://github.com/mackron/miniaudio"><img src="../../img/github_white.png" style="margin:0; padding:0; height:24px; width:24px;"></a>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div style="background-color:#fff; padding-bottom:0em; border-top:solid 1px #003800; background-color:#eee;">
|
|
<table border="0" style="margin:0 auto; width:100%; border-collapse:collapse; border:solid 0px #000; table-layout:fixed;"><tr>
|
|
<td valign="top" style="width:20em; padding:0; margin:0; border-right:solid 0px #000;"><div style="position:relative; height:100%; width:100%; border:solid 0px #000; padding:0; margin:0;">
|
|
<a href="../index.html" class="doc-navigation">Documentation Home</a><a href="index.html" class="doc-navigation doc-navigation-active">Programming Manual</a><a href="#Introduction" class="doc-navigation doc-navigation-l1">Introduction</a><a href="#Building" class="doc-navigation doc-navigation-l1">Building</a><a href="#Definitions" class="doc-navigation doc-navigation-l1">Definitions</a><a href="#DataSources" class="doc-navigation doc-navigation-l1">Data Sources</a><a href="#Engine" class="doc-navigation doc-navigation-l1">Engine</a><a href="#ResourceManagement" class="doc-navigation doc-navigation-l1">Resource Management</a><a href="#NodeGraph" class="doc-navigation doc-navigation-l1">Node Graph</a><a href="#Decoding" class="doc-navigation doc-navigation-l1">Decoding</a><a href="#Encoding" class="doc-navigation doc-navigation-l1">Encoding</a><a href="#DataConversion" class="doc-navigation doc-navigation-l1">Data Conversion</a><a href="#Filtering" class="doc-navigation doc-navigation-l1">Filtering</a><a href="#WaveformandNoiseGeneration" class="doc-navigation doc-navigation-l1">Waveform and Noise Generation</a><a href="#AudioBuffers" class="doc-navigation doc-navigation-l1">Audio Buffers</a><a href="#RingBuffers" class="doc-navigation doc-navigation-l1">Ring Buffers</a><a href="#Backends" class="doc-navigation doc-navigation-l1">Backends</a><a href="#OptimizationTips" class="doc-navigation doc-navigation-l1">Optimization Tips</a><a href="#MiscellaneousNotes" class="doc-navigation doc-navigation-l1">Miscellaneous Notes</a><a href="../examples/index.html" class="doc-navigation">Examples</a><a href="../api/index.html" class="doc-navigation" style="border-bottom:none;">API Reference</a></div></td><td valign="top" style="padding:1em; border-left:solid 1px #bbb;">
|
|
<div style="text-align:center; padding:1em; padding-bottom:2em;"><div style="text-align:center; overflow:hidden;"><img src="../../img/logo1_large.png" style="width:auto; height:auto; min-height:70px; overflow:hidden;"></div><div style="padding-top:1em; font-weight:bold; font-size:2em; color:#444;">Programming Manual</div><div style="padding-top:0.75em; text-align:center;"><a href="../examples/index.html">Examples</a> - <a href="../api/index.html">API Reference</a> - <a href="https://github.com/mackron/miniaudio">Source Code</a></div></div>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="Introduction" class="man">1. Introduction</h1>
|
|
<p>
|
|
miniaudio is a single file library for audio playback and capture. To use it, do the following in
|
|
one .c file:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#666666">#define</span> MINIAUDIO_IMPLEMENTATION
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300">"miniaudio.h"</span>
|
|
</pre></div><p>
|
|
|
|
You can do <span style="font-family:monospace;">#include "miniaudio.h"</span> in other parts of the program just like any other header.
|
|
</p>
|
|
<p>
|
|
|
|
miniaudio includes both low level and high level APIs. The low level API is good for those who want
|
|
to do all of their mixing themselves and only require a light weight interface to the underlying
|
|
audio device. The high level API is good for those who have complex mixing and effect requirements.
|
|
</p>
|
|
<p>
|
|
|
|
In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles
|
|
to opaque objects which means you need to allocate memory for objects yourself. In the examples
|
|
presented in this documentation you will often see objects declared on the stack. You need to be
|
|
careful when translating these examples to your own code so that you don't accidentally declare
|
|
your objects on the stack and then cause them to become invalid once the function returns. In
|
|
addition, you must ensure the memory address of your objects remain the same throughout their
|
|
lifetime. You therefore cannot be making copies of your objects.
|
|
</p>
|
|
<p>
|
|
|
|
A config/init pattern is used throughout the entire library. The idea is that you set up a config
|
|
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
|
|
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.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="LowLevelAPI" class="man">1.1. Low Level API</h2>
|
|
<p>
|
|
The low level API gives you access to the raw audio data of an audio device. It supports playback,
|
|
capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which
|
|
physical device(s) you want to connect to.
|
|
</p>
|
|
<p>
|
|
|
|
The low level API uses the concept of a "device" as the abstraction for physical devices. The idea
|
|
is that you choose a physical device to emit or capture audio from, and then move data to/from the
|
|
device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a
|
|
callback which you specify when initializing the device.
|
|
</p>
|
|
<p>
|
|
|
|
When initializing the device you first need to configure it. The device configuration allows you to
|
|
specify things like the format of the data delivered via the callback, the size of the internal
|
|
buffer and the ID of the device you want to emit or capture audio from.
|
|
</p>
|
|
<p>
|
|
|
|
Once you have the device configuration set up you can initialize the device. When initializing a
|
|
device you need to allocate memory for the device object beforehand. This gives the application
|
|
complete control over how the memory is allocated. In the example below we initialize a playback
|
|
device on the stack, but you could allocate it on the heap if that suits your situation better.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">void</span> data_callback(<span style="color:#0099cc">ma_device</span>* pDevice, <span style="color:#0033ff">void</span>* pOutput, <span style="color:#0033ff">const</span> <span style="color:#0033ff">void</span>* pInput, <span style="color:#0099cc">ma_uint32</span> frameCount)
|
|
{
|
|
<span style="color:#009900">// In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both</span>
|
|
<span style="color:#009900">// pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than</span>
|
|
<span style="color:#009900">// frameCount frames.</span>
|
|
}
|
|
|
|
<span style="color:#0033ff">int</span> main()
|
|
{
|
|
<span style="color:#0099cc">ma_device_config</span> config = ma_device_config_init(ma_device_type_playback);
|
|
config.playback.format = ma_format_f32; <span style="color:#009900">// Set to ma_format_unknown to use the device's native format.</span>
|
|
config.playback.channels = 2; <span style="color:#009900">// Set to 0 to use the device's native channel count.</span>
|
|
config.sampleRate = 48000; <span style="color:#009900">// Set to 0 to use the device's native sample rate.</span>
|
|
config.dataCallback = data_callback; <span style="color:#009900">// This function will be called when miniaudio needs more data.</span>
|
|
config.pUserData = pMyCustomData; <span style="color:#009900">// Can be accessed from the device object (device.pUserData).</span>
|
|
|
|
<span style="color:#0099cc">ma_device</span> device;
|
|
<span style="color:#0033ff">if</span> (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> -1; <span style="color:#009900">// Failed to initialize the device.</span>
|
|
}
|
|
|
|
ma_device_start(&device); <span style="color:#009900">// The device is sleeping by default so you'll need to start it manually.</span>
|
|
|
|
<span style="color:#009900">// Do something here. Probably your program's main loop.</span>
|
|
|
|
ma_device_uninit(&device); <span style="color:#009900">// This will stop the device so no need to do that manually.</span>
|
|
<span style="color:#0033ff">return</span> 0;
|
|
}
|
|
</pre></div><p>
|
|
|
|
In the example above, <span style="font-family:monospace;">data_callback()</span> is where audio data is written and read from the device.
|
|
The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data
|
|
to the output buffer (<span style="font-family:monospace;">pOutput</span> in the example). In capture mode you read data from the input
|
|
buffer (<span style="font-family:monospace;">pInput</span>) to extract sound captured by the microphone. The <span style="font-family:monospace;">frameCount</span> parameter tells you
|
|
how many frames can be written to the output buffer and read from the input buffer. A "frame" is
|
|
one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2
|
|
samples: one for the left, one for the right. The channel count is defined by the device config.
|
|
The size in bytes of an individual sample is defined by the sample format which is also specified
|
|
in the device config. Multi-channel audio data is always interleaved, which means the samples for
|
|
each frame are stored next to each other in memory. For example, in a stereo stream the first pair
|
|
of samples will be the left and right samples for the first frame, the second pair of samples will
|
|
be the left and right samples for the second frame, etc.
|
|
</p>
|
|
<p>
|
|
|
|
The configuration of the device is defined by the <span style="font-family:monospace;">ma_device_config</span> structure. The config object
|
|
is always initialized with <span style="font-family:monospace;">ma_device_config_init()</span>. It's important to always initialize the
|
|
config with this function as it initializes it with logical defaults and ensures your program
|
|
doesn't break when new members are added to the <span style="font-family:monospace;">ma_device_config</span> structure. The example above
|
|
uses a fairly simple and standard device configuration. The call to <span style="font-family:monospace;">ma_device_config_init()</span> takes
|
|
a single parameter, which is whether or not the device is a playback, capture, duplex or loopback
|
|
device (loopback devices are not supported on all backends). The <span style="font-family:monospace;">config.playback.format</span> member
|
|
sets the sample format which can be one of the following (all formats are native-endian):
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Symbol</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Range</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_f32</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
32-bit floating point</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[-1, 1]</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_s16</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
16-bit signed integer</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[-32768, 32767]</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_s24</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
24-bit signed integer (tightly packed)</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[-8388608, 8388607]</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_s32</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
32-bit signed integer</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[-2147483648, 2147483647]</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_u8</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
8-bit unsigned integer</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[0, 255]</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
The <span style="font-family:monospace;">config.playback.channels</span> member sets the number of channels to use with the device. The
|
|
channel count cannot exceed MA_MAX_CHANNELS. The <span style="font-family:monospace;">config.sampleRate</span> member sets the sample rate
|
|
(which must be the same for both playback and capture in full-duplex configurations). This is
|
|
usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between
|
|
8000 and 384000, however.
|
|
</p>
|
|
<p>
|
|
|
|
Note that leaving the format, channel count and/or sample rate at their default values will result
|
|
in the internal device's native configuration being used which is useful if you want to avoid the
|
|
overhead of miniaudio's automatic data conversion.
|
|
</p>
|
|
<p>
|
|
|
|
In addition to the sample format, channel count and sample rate, the data callback and user data
|
|
pointer are also set via the config. The user data pointer is not passed into the callback as a
|
|
parameter, but is instead set to the <span style="font-family:monospace;">pUserData</span> member of <span style="font-family:monospace;">ma_device</span> which you can access
|
|
directly since all miniaudio structures are transparent.
|
|
</p>
|
|
<p>
|
|
|
|
Initializing the device is done with <span style="font-family:monospace;">ma_device_init()</span>. This will return a result code telling you
|
|
what went wrong, if anything. On success it will return <span style="font-family:monospace;">MA_SUCCESS</span>. After initialization is
|
|
complete the device will be in a stopped state. To start it, use <span style="font-family:monospace;">ma_device_start()</span>.
|
|
Uninitializing the device will stop it, which is what the example above does, but you can also stop
|
|
the device with <span style="font-family:monospace;">ma_device_stop()</span>. To resume the device simply call <span style="font-family:monospace;">ma_device_start()</span> again.
|
|
Note that it's important to never stop or start the device from inside the callback. This will
|
|
result in a deadlock. Instead you set a variable or signal an event indicating that the device
|
|
needs to stop and handle it in a different thread. The following APIs must never be called inside
|
|
the callback:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_device_init()
|
|
ma_device_init_ex()
|
|
ma_device_uninit()
|
|
ma_device_start()
|
|
ma_device_stop()
|
|
</pre></div><p>
|
|
|
|
You must never try uninitializing and reinitializing a device inside the callback. You must also
|
|
never try to stop and start it from inside the callback. There are a few other things you shouldn't
|
|
do in the callback depending on your requirements, however this isn't so much a thread-safety
|
|
thing, but rather a real-time processing thing which is beyond the scope of this introduction.
|
|
</p>
|
|
<p>
|
|
|
|
The example above demonstrates the initialization of a playback device, but it works exactly the
|
|
same for capture. All you need to do is change the device type from <span style="font-family:monospace;">ma_device_type_playback</span> to
|
|
<span style="font-family:monospace;">ma_device_type_capture</span> when setting up the config, like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_device_config</span> config = ma_device_config_init(ma_device_type_capture);
|
|
config.capture.format = MY_FORMAT;
|
|
config.capture.channels = MY_CHANNEL_COUNT;
|
|
</pre></div><p>
|
|
|
|
In the data callback you just read from the input buffer (<span style="font-family:monospace;">pInput</span> in the example above) and leave
|
|
the output buffer alone (it will be set to NULL when the device type is set to
|
|
<span style="font-family:monospace;">ma_device_type_capture</span>).
|
|
</p>
|
|
<p>
|
|
|
|
These are the available device types and how you should handle the buffers in the callback:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Device Type</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Callback Behavior</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_device_type_playback</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Write to output buffer, leave input buffer untouched.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_device_type_capture</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Read from input buffer, leave output buffer untouched.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_device_type_duplex</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Read from input buffer, write to output buffer.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_device_type_loopback</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Read from input buffer, leave output buffer untouched.</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
You will notice in the example above that the sample format and channel count is specified
|
|
separately for playback and capture. This is to support different data formats between the playback
|
|
and capture devices in a full-duplex system. An example may be that you want to capture audio data
|
|
as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you
|
|
use different formats between playback and capture in a full-duplex configuration you will need to
|
|
convert the data yourself. There are functions available to help you do this which will be
|
|
explained later.
|
|
</p>
|
|
<p>
|
|
|
|
The example above did not specify a physical device to connect to which means it will use the
|
|
operating system's default device. If you have multiple physical devices connected and you want to
|
|
use a specific one you will need to specify the device ID in the configuration, like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
config.playback.pDeviceID = pMyPlaybackDeviceID; <span style="color:#009900">// Only if requesting a playback or duplex device.</span>
|
|
config.capture.pDeviceID = pMyCaptureDeviceID; <span style="color:#009900">// Only if requesting a capture, duplex or loopback device.</span>
|
|
</pre></div><p>
|
|
|
|
To retrieve the device ID you will need to perform device enumeration, however this requires the
|
|
use of a new concept called the "context". Conceptually speaking the context sits above the device.
|
|
There is one context to many devices. The purpose of the context is to represent the backend at a
|
|
more global level and to perform operations outside the scope of an individual device. Mainly it is
|
|
used for performing run-time linking against backend libraries, initializing backends and
|
|
enumerating devices. The example below shows how to enumerate devices.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_context</span> context;
|
|
<span style="color:#0033ff">if</span> (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
|
|
<span style="color:#0099cc">ma_device_info</span>* pPlaybackInfos;
|
|
<span style="color:#0099cc">ma_uint32</span> playbackCount;
|
|
<span style="color:#0099cc">ma_device_info</span>* pCaptureInfos;
|
|
<span style="color:#0099cc">ma_uint32</span> captureCount;
|
|
<span style="color:#0033ff">if</span> (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
|
|
<span style="color:#009900">// Loop over each device info and do something with it. Here we just print the name with their index. You may want</span>
|
|
<span style="color:#009900">// to give the user the opportunity to choose which device they'd prefer.</span>
|
|
<span style="color:#0033ff">for</span> (<span style="color:#0099cc">ma_uint32</span> iDevice = 0; iDevice < playbackCount; iDevice += 1) {
|
|
printf(<span style="color:#cc3300">"%d - %s\n"</span>, iDevice, pPlaybackInfos[iDevice].name);
|
|
}
|
|
|
|
<span style="color:#0099cc">ma_device_config</span> config = ma_device_config_init(ma_device_type_playback);
|
|
config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id;
|
|
config.playback.format = MY_FORMAT;
|
|
config.playback.channels = MY_CHANNEL_COUNT;
|
|
config.sampleRate = MY_SAMPLE_RATE;
|
|
config.dataCallback = data_callback;
|
|
config.pUserData = pMyCustomData;
|
|
|
|
<span style="color:#0099cc">ma_device</span> device;
|
|
<span style="color:#0033ff">if</span> (ma_device_init(&context, &config, &device) != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error</span>
|
|
}
|
|
|
|
...
|
|
|
|
ma_device_uninit(&device);
|
|
ma_context_uninit(&context);
|
|
</pre></div><p>
|
|
|
|
The first thing we do in this example is initialize a <span style="font-family:monospace;">ma_context</span> object with <span style="font-family:monospace;">ma_context_init()</span>.
|
|
The first parameter is a pointer to a list of <span style="font-family:monospace;">ma_backend</span> values which are used to override the
|
|
default backend priorities. When this is NULL, as in this example, miniaudio's default priorities
|
|
are used. The second parameter is the number of backends listed in the array pointed to by the
|
|
first parameter. The third parameter is a pointer to a <span style="font-family:monospace;">ma_context_config</span> object which can be
|
|
NULL, in which case defaults are used. The context configuration is used for setting the logging
|
|
callback, custom memory allocation callbacks, user-defined data and some backend-specific
|
|
configurations.
|
|
</p>
|
|
<p>
|
|
|
|
Once the context has been initialized you can enumerate devices. In the example above we use the
|
|
simpler <span style="font-family:monospace;">ma_context_get_devices()</span>, however you can also use a callback for handling devices by
|
|
using <span style="font-family:monospace;">ma_context_enumerate_devices()</span>. When using <span style="font-family:monospace;">ma_context_get_devices()</span> you provide a pointer
|
|
to a pointer that will, upon output, be set to a pointer to a buffer containing a list of
|
|
<span style="font-family:monospace;">ma_device_info</span> structures. You also provide a pointer to an unsigned integer that will receive
|
|
the number of items in the returned buffer. Do not free the returned buffers as their memory is
|
|
managed internally by miniaudio.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">ma_device_info</span> structure contains an <span style="font-family:monospace;">id</span> member which is the ID you pass to the device
|
|
config. It also contains the name of the device which is useful for presenting a list of devices
|
|
to the user via the UI.
|
|
</p>
|
|
<p>
|
|
|
|
When creating your own context you will want to pass it to <span style="font-family:monospace;">ma_device_init()</span> when initializing the
|
|
device. Passing in NULL, like we do in the first example, will result in miniaudio creating the
|
|
context for you, which you don't want to do since you've already created a context. Note that
|
|
internally the context is only tracked by it's pointer which means you must not change the location
|
|
of the <span style="font-family:monospace;">ma_context</span> object. If this is an issue, consider using <span style="font-family:monospace;">malloc()</span> to allocate memory for
|
|
the context.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="HighLevelAPI" class="man">1.2. High Level API</h2>
|
|
<p>
|
|
The high level API consists of three main parts:
|
|
</p>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
Resource management for loading and streaming sounds.</li>
|
|
<li>
|
|
A node graph for advanced mixing and effect processing.</li>
|
|
<li>
|
|
A high level "engine" that wraps around the resource manager and node graph.</li>
|
|
</ul>
|
|
<p>
|
|
|
|
The resource manager (<span style="font-family:monospace;">ma_resource_manager</span>) is used for loading sounds. It supports loading sounds
|
|
fully into memory and also streaming. It will also deal with reference counting for you which
|
|
avoids the same sound being loaded multiple times.
|
|
</p>
|
|
<p>
|
|
|
|
The node graph is used for mixing and effect processing. The idea is that you connect a number of
|
|
nodes into the graph by connecting each node's outputs to another node's inputs. Each node can
|
|
implement it's own effect. By chaining nodes together, advanced mixing and effect processing can
|
|
be achieved.
|
|
</p>
|
|
<p>
|
|
|
|
The engine encapsulates both the resource manager and the node graph to create a simple, easy to
|
|
use high level API. The resource manager and node graph APIs are covered in more later sections of
|
|
this manual.
|
|
</p>
|
|
<p>
|
|
|
|
The code below shows how you can initialize an engine using it's default configuration.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
ma_engine engine;
|
|
|
|
result = ma_engine_init(NULL, &engine);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to initialize the engine.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
This creates an engine instance which will initialize a device internally which you can access with
|
|
<span style="font-family:monospace;">ma_engine_get_device()</span>. It will also initialize a resource manager for you which can be accessed
|
|
with <span style="font-family:monospace;">ma_engine_get_resource_manager()</span>. The engine itself is a node graph (<span style="font-family:monospace;">ma_node_graph</span>) which
|
|
means you can pass a pointer to the engine object into any of the <span style="font-family:monospace;">ma_node_graph</span> APIs (with a
|
|
cast). Alternatively, you can use <span style="font-family:monospace;">ma_engine_get_node_graph()</span> instead of a cast.
|
|
</p>
|
|
<p>
|
|
|
|
Note that all objects in miniaudio, including the <span style="font-family:monospace;">ma_engine</span> object in the example above, are
|
|
transparent structures. There are no handles to opaque structures in miniaudio which means you need
|
|
to be mindful of how you declare them. In the example above we are declaring it on the stack, but
|
|
this will result in the struct being invalidated once the function encapsulating it returns. If
|
|
allocating the engine on the heap is more appropriate, you can easily do so with a standard call
|
|
to <span style="font-family:monospace;">malloc()</span> or whatever heap allocation routine you like:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_engine* pEngine = malloc(<span style="color:#0033ff">sizeof</span>(*pEngine));
|
|
</pre></div><p>
|
|
|
|
The <span style="font-family:monospace;">ma_engine</span> API uses the same config/init pattern used all throughout miniaudio. To configure
|
|
an engine, you can fill out a <span style="font-family:monospace;">ma_engine_config</span> object and pass it into the first parameter of
|
|
<span style="font-family:monospace;">ma_engine_init()</span>:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
ma_engine engine;
|
|
ma_engine_config engineConfig;
|
|
|
|
engineConfig = ma_engine_config_init();
|
|
engineConfig.pResourceManager = &myCustomResourceManager; <span style="color:#009900">// <-- Initialized as some earlier stage.</span>
|
|
|
|
result = ma_engine_init(&engineConfig, &engine);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result;
|
|
}
|
|
</pre></div><p>
|
|
|
|
This creates an engine instance using a custom config. In this particular example it's showing how
|
|
you can specify a custom resource manager rather than having the engine initialize one internally.
|
|
This is particularly useful if you want to have multiple engine's share the same resource manager.
|
|
</p>
|
|
<p>
|
|
|
|
The engine must be uninitialized with <span style="font-family:monospace;">ma_engine_uninit()</span> when it's no longer needed.
|
|
</p>
|
|
<p>
|
|
|
|
By default the engine will be started, but nothing will be playing because no sounds have been
|
|
initialized. The easiest but least flexible way of playing a sound is like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_engine_play_sound(&engine, <span style="color:#cc3300">"my_sound.wav"</span>, NULL);
|
|
</pre></div><p>
|
|
|
|
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
|
|
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
|
|
initialize a sound:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
ma_sound sound;
|
|
|
|
result = ma_sound_init_from_file(&engine, <span style="color:#cc3300">"my_sound.wav"</span>, 0, NULL, NULL, &sound);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result;
|
|
}
|
|
|
|
ma_sound_start(&sound);
|
|
</pre></div><p>
|
|
|
|
This returns a <span style="font-family:monospace;">ma_sound</span> object which represents a single instance of the specified sound file. If
|
|
you want to play the same file multiple times simultaneously, you need to create one sound for each
|
|
instance.
|
|
</p>
|
|
<p>
|
|
|
|
Sounds should be uninitialized with <span style="font-family:monospace;">ma_sound_uninit()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Sounds are not started by default. Start a sound with <span style="font-family:monospace;">ma_sound_start()</span> and stop it with
|
|
<span style="font-family:monospace;">ma_sound_stop()</span>. When a sound is stopped, it is not rewound to the start. Use
|
|
<span style="font-family:monospace;">ma_sound_seek_to_pcm_frames(&sound, 0)</span> 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
|
|
the be started and/or stopped at a specific time. This can be done with the following functions:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_start_time_in_pcm_frames()
|
|
ma_sound_set_start_time_in_milliseconds()
|
|
ma_sound_set_stop_time_in_pcm_frames()
|
|
ma_sound_set_stop_time_in_milliseconds()
|
|
</pre></div><p>
|
|
|
|
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 <span style="font-family:monospace;">ma_engine_get_time()</span>.
|
|
The engine's global time can be changed with <span style="font-family:monospace;">ma_engine_set_time()</span> for synchronization purposes if
|
|
required. Note that scheduling a start time still requires an explicit call to <span style="font-family:monospace;">ma_sound_start()</span>
|
|
before anything will play:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
|
|
ma_sound_start(&sound);
|
|
</pre></div><p>
|
|
|
|
The third parameter of <span style="font-family:monospace;">ma_sound_init_from_file()</span> is a set of flags that control how the sound be
|
|
loaded and a few options on which features should be enabled for that sound. By default, the sound
|
|
is synchronously loaded fully into memory straight from the file system without any kind of
|
|
decoding. If you want to decode the sound before storing it in memory, you need to specify the
|
|
<span style="font-family:monospace;">MA_SOUND_FLAG_DECODE</span> flag. This is useful if you want to incur the cost of decoding at an earlier
|
|
stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing
|
|
time which might be too expensive on the audio thread.
|
|
</p>
|
|
<p>
|
|
|
|
If you want to load the sound asynchronously, you can specify the <span style="font-family:monospace;">MA_SOUND_FLAG_ASYNC</span> flag. This
|
|
will result in <span style="font-family:monospace;">ma_sound_init_from_file()</span> returning quickly, but the sound will not start playing
|
|
until the sound has had some audio decoded.
|
|
</p>
|
|
<p>
|
|
|
|
The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise
|
|
sounds into groups which have their own effect processing and volume control. An example is a game
|
|
which might have separate groups for sfx, voice and music. Each of these groups have their own
|
|
independent volume control. Use <span style="font-family:monospace;">ma_sound_group_init()</span> or <span style="font-family:monospace;">ma_sound_group_init_ex()</span> to initialize
|
|
a sound group.
|
|
</p>
|
|
<p>
|
|
|
|
Sounds and sound groups are nodes in the engine's node graph and can be plugged into any <span style="font-family:monospace;">ma_node</span>
|
|
API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex
|
|
effect chains.
|
|
</p>
|
|
<p>
|
|
|
|
A sound can have it's volume changed with <span style="font-family:monospace;">ma_sound_set_volume()</span>. If you prefer decibel volume
|
|
control you can use <span style="font-family:monospace;">ma_volume_db_to_linear()</span> to convert from decibel representation to linear.
|
|
</p>
|
|
<p>
|
|
|
|
Panning and pitching is supported with <span style="font-family:monospace;">ma_sound_set_pan()</span> and <span style="font-family:monospace;">ma_sound_set_pitch()</span>. If you know
|
|
a sound will never have it's pitch changed with <span style="font-family:monospace;">ma_sound_set_pitch()</span> or via the doppler effect,
|
|
you can specify the <span style="font-family:monospace;">MA_SOUND_FLAG_NO_PITCH</span> flag when initializing the sound for an optimization.
|
|
</p>
|
|
<p>
|
|
|
|
By default, sounds and sound groups have spatialization enabled. If you don't ever want to
|
|
spatialize your sounds, initialize the sound with the <span style="font-family:monospace;">MA_SOUND_FLAG_NO_SPATIALIZATION</span> flag. The
|
|
spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and
|
|
environmental occlusion are not currently supported, but planned for the future. The supported
|
|
features include:
|
|
</p>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
Sound and listener positioning and orientation with cones</li>
|
|
<li>
|
|
Attenuation models: none, inverse, linear and exponential</li>
|
|
<li>
|
|
Doppler effect</li>
|
|
</ul>
|
|
<p>
|
|
|
|
Sounds can be faded in and out with <span style="font-family:monospace;">ma_sound_set_fade_in_pcm_frames()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
To check if a sound is currently playing, you can use <span style="font-family:monospace;">ma_sound_is_playing()</span>. To check if a sound
|
|
is at the end, use <span style="font-family:monospace;">ma_sound_at_end()</span>. Looping of a sound can be controlled with
|
|
<span style="font-family:monospace;">ma_sound_set_looping()</span>. Use <span style="font-family:monospace;">ma_sound_is_looping()</span> to check whether or not the sound is looping.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="Building" class="man">2. Building</h1>
|
|
<p>
|
|
miniaudio should work cleanly out of the box without the need to download or install any
|
|
dependencies. See below for platform-specific details.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Windows" class="man">2.1. Windows</h2>
|
|
<p>
|
|
The Windows build should compile cleanly on all popular compilers without the need to configure any
|
|
include paths nor link to any libraries.
|
|
</p>
|
|
<p>
|
|
|
|
The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external
|
|
symbol for <span style="font-family:monospace;">ActivateAudioInterfaceAsync()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="macOSandiOS" class="man">2.2. macOS and iOS</h2>
|
|
<p>
|
|
The macOS build should compile cleanly without the need to download any dependencies nor link to
|
|
any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to
|
|
link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling
|
|
through the command line requires linking to <span style="font-family:monospace;">-lpthread</span> and <span style="font-family:monospace;">-lm</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's
|
|
notarization process. To fix this there are two options. The first is to use the
|
|
<span style="font-family:monospace;">MA_NO_RUNTIME_LINKING</span> option, like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#666666">#ifdef</span> __APPLE__
|
|
<span style="color:#666666">#define</span> MA_NO_RUNTIME_LINKING
|
|
<span style="color:#666666">#endif</span>
|
|
<span style="color:#666666">#define</span> MINIAUDIO_IMPLEMENTATION
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300">"miniaudio.h"</span>
|
|
</pre></div><p>
|
|
|
|
This will require linking with <span style="font-family:monospace;">-framework CoreFoundation -framework CoreAudio -framework AudioUnit</span>.
|
|
Alternatively, if you would rather keep using runtime linking you can add the following to your
|
|
entitlements.xcent file:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
|
<true/>
|
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
|
<true/>
|
|
</pre></div><p>
|
|
|
|
See this discussion for more info: <a href="https://github.com/mackron/miniaudio/issues/203">https://github.com/mackron/miniaudio/issues/203</a>.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Linux" class="man">2.3. Linux</h2>
|
|
<p>
|
|
The Linux build only requires linking to <span style="font-family:monospace;">-ldl</span>, <span style="font-family:monospace;">-lpthread</span> and <span style="font-family:monospace;">-lm</span>. You do not need any
|
|
development packages. You may need to link with <span style="font-family:monospace;">-latomic</span> if you're compiling for 32-bit ARM.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="BSD" class="man">2.4. BSD</h2>
|
|
<p>
|
|
The BSD build only requires linking to <span style="font-family:monospace;">-lpthread</span> and <span style="font-family:monospace;">-lm</span>. NetBSD uses audio(4), OpenBSD uses
|
|
sndio and FreeBSD uses OSS. You may need to link with <span style="font-family:monospace;">-latomic</span> if you're compiling for 32-bit
|
|
ARM.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Android" class="man">2.5. Android</h2>
|
|
<p>
|
|
AAudio is the highest priority backend on Android. This should work out of the box without needing
|
|
any kind of compiler configuration. Support for AAudio starts with Android 8 which means older
|
|
versions will fall back to OpenSL|ES which requires API level 16+.
|
|
</p>
|
|
<p>
|
|
|
|
There have been reports that the OpenSL|ES backend fails to initialize on some Android based
|
|
devices due to <span style="font-family:monospace;">dlopen()</span> failing to open "libOpenSLES.so". If this happens on your platform
|
|
you'll need to disable run-time linking with <span style="font-family:monospace;">MA_NO_RUNTIME_LINKING</span> and link with -lOpenSLES.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Emscripten" class="man">2.6. Emscripten</h2>
|
|
<p>
|
|
The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box.
|
|
You cannot use <span style="font-family:monospace;">-std=c*</span> compiler flags, nor <span style="font-family:monospace;">-ansi</span>.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="BuildOptions" class="man">2.7. Build Options</h2>
|
|
<p>
|
|
<span style="font-family:monospace;">#define</span> these options before including miniaudio.h.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Option</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_WASAPI</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the WASAPI backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_DSOUND</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the DirectSound backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_WINMM</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the WinMM backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_ALSA</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the ALSA backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_PULSEAUDIO</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the PulseAudio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_JACK</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the JACK backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_COREAUDIO</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the Core Audio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_SNDIO</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the sndio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_AUDIO4</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the audio(4) backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_OSS</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the OSS backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_AAUDIO</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the AAudio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_OPENSL</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the OpenSL</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_WEBAUDIO</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the Web Audio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_NULL</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the null backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_ONLY_SPECIFIC_BACKENDS
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables all backends by default and requires <span style="font-family:monospace;">MA_ENABLE_*</span> to
|
|
enable specific backends.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_WASAPI
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the WASAPI backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_DSOUND
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the DirectSound backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_WINMM
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the WinMM backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_ALSA
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the ALSA backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_PULSEAUDIO
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the PulseAudio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_JACK
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the JACK backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_COREAUDIO
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the Core Audio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_SNDIO
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the sndio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_AUDIO4
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the audio(4) backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_OSS
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the OSS backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_AAUDIO
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the AAudio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_OPENSL
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the OpenSL</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_WEBAUDIO
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the Web Audio backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_ENABLE_NULL
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to
|
|
enable the null backend.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_DECODING</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables decoding APIs.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_ENCODING</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables encoding APIs.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_WAV</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the built-in WAV decoder and encoder.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_FLAC</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the built-in FLAC decoder.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_MP3</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the built-in MP3 decoder.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_DEVICE_IO
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables playback and recording. This will disable <span style="font-family:monospace;">ma_context</span>
|
|
and <span style="font-family:monospace;">ma_device</span> APIs. This is useful if you only want to use
|
|
miniaudio's data conversion and/or decoding APIs.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_THREADING
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the <span style="font-family:monospace;">ma_thread</span>, <span style="font-family:monospace;">ma_mutex</span>, <span style="font-family:monospace;">ma_semaphore</span> and
|
|
<span style="font-family:monospace;">ma_event</span> APIs. This option is useful if you only need to use
|
|
miniaudio for data conversion, decoding and/or encoding. Some
|
|
families of APIsrequire threading which means the following
|
|
options must also be set:
|
|
|
|
</p>
|
|
<div style="font-family:monospace; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
MA_NO_DEVICE_IO
|
|
</pre></div></td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_GENERATION</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables generation APIs such a <span style="font-family:monospace;">ma_waveform</span> and <span style="font-family:monospace;">ma_noise</span>.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_SSE2</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables SSE2 optimizations.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_AVX2</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables AVX2 optimizations.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_NEON</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables NEON optimizations.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NO_RUNTIME_LINKING
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables runtime linking. This is useful for passing Apple's
|
|
notarization process. When enabling this, you may need to avoid
|
|
using <span style="font-family:monospace;">-std=c89</span> or <span style="font-family:monospace;">-std=c99</span> on Linux builds or else you may end
|
|
up with compilation errors due to conflicts with <span style="font-family:monospace;">timespec</span> and
|
|
<span style="font-family:monospace;">timeval</span> data types.
|
|
|
|
You may need to enable this if your target platform does not allow
|
|
runtime linking via <span style="font-family:monospace;">dlopen()</span>.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_DEBUG_OUTPUT</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Enable <span style="font-family:monospace;">printf()</span> output of debug logs (<span style="font-family:monospace;">MA_LOG_LEVEL_DEBUG</span>).</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_COINIT_VALUE
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Windows only. The value to pass to internal calls to
|
|
<span style="font-family:monospace;">CoInitializeEx()</span>. Defaults to <span style="font-family:monospace;">COINIT_MULTITHREADED</span>.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_API</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Controls how public APIs should be decorated. Default is <span style="font-family:monospace;">extern</span>.</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
|
|
</p>
|
|
<h1 id="Definitions" class="man">3. Definitions</h1>
|
|
<p>
|
|
This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity
|
|
in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio
|
|
uses each term.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Sample" class="man">3.1. Sample</h2>
|
|
<p>
|
|
A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit
|
|
floating point number.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Frame/PCMFrame" class="man">3.2. Frame / PCM Frame</h2>
|
|
<p>
|
|
A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2
|
|
samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame"
|
|
and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame.
|
|
If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always
|
|
clarify what it's referring to with something like "FLAC frame".
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Channel" class="man">3.3. Channel</h2>
|
|
<p>
|
|
A stream of monaural audio that is emitted from an individual speaker in a speaker system, or
|
|
received from an individual microphone in a microphone system. A stereo stream has two channels (a
|
|
left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio
|
|
systems refer to a channel as a complex audio stream that's mixed with other channels to produce
|
|
the final mix - this is completely different to miniaudio's use of the term "channel" and should
|
|
not be confused.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="SampleRate" class="man">3.4. Sample Rate</h2>
|
|
<p>
|
|
The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number
|
|
of PCM frames that are processed per second.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Formats" class="man">3.5. Formats</h2>
|
|
<p>
|
|
Throughout miniaudio you will see references to different sample formats:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Symbol</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Range</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_f32</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
32-bit floating point</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[-1, 1]</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_s16</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
16-bit signed integer</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[-32768, 32767]</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_s24</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
24-bit signed integer (tightly packed)</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[-8388608, 8388607]</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_s32</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
32-bit signed integer</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[-2147483648, 2147483647]</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_format_u8</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
8-bit unsigned integer</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
[0, 255]</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
All formats are native-endian.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="DataSources" class="man">4. Data Sources</h1>
|
|
<p>
|
|
The data source abstraction in miniaudio is used for retrieving audio data from some source. A few
|
|
examples include <span style="font-family:monospace;">ma_decoder</span>, <span style="font-family:monospace;">ma_noise</span> and <span style="font-family:monospace;">ma_waveform</span>. You will need to be familiar with data
|
|
sources in order to make sense of some of the higher level concepts in miniaudio.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">ma_data_source</span> API is a generic interface for reading from a data source. Any object that
|
|
implements the data source interface can be plugged into any <span style="font-family:monospace;">ma_data_source</span> function.
|
|
</p>
|
|
<p>
|
|
|
|
To read data from a data source:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
<span style="color:#0099cc">ma_uint64</span> framesRead;
|
|
|
|
result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead, loop);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to read data from the data source.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
If you don't need the number of frames that were successfully read you can pass in <span style="font-family:monospace;">NULL</span> to the
|
|
<span style="font-family:monospace;">pFramesRead</span> parameter. If this returns a value less than the number of frames requested it means
|
|
the end of the file has been reached. <span style="font-family:monospace;">MA_AT_END</span> will be returned only when the number of frames
|
|
read is 0.
|
|
</p>
|
|
<p>
|
|
|
|
When calling any data source function, with the exception of <span style="font-family:monospace;">ma_data_source_init()</span> and
|
|
<span style="font-family:monospace;">ma_data_source_uninit()</span>, you can pass in any object that implements a data source. For example,
|
|
you could plug in a decoder like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
<span style="color:#0099cc">ma_uint64</span> framesRead;
|
|
<span style="color:#0099cc">ma_decoder</span> decoder; <span style="color:#009900">// <-- This would be initialized with <span style="font-family:monospace;">ma_decoder_init_*()</span>.</span>
|
|
|
|
result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead, loop);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to read data from the decoder.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
If you want to seek forward you can pass in <span style="font-family:monospace;">NULL</span> to the <span style="font-family:monospace;">pFramesOut</span> parameter. Alternatively you
|
|
can use <span style="font-family:monospace;">ma_data_source_seek_pcm_frames()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
To seek to a specific PCM frame:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to seek to PCM frame.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
You can retrieve the total length of a data source in PCM frames, but note that some data sources
|
|
may not have the notion of a length, such as noise and waveforms, and others may just not have a
|
|
way of determining the length such as some decoders. To retrieve the length:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_uint64</span> length;
|
|
|
|
result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to retrieve the length.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
Care should be taken when retrieving the length of a data source where the underlying decoder is
|
|
pulling data from a data stream with an undefined length, such as internet radio or some kind of
|
|
broadcast. If you do this, <span style="font-family:monospace;">ma_data_source_get_length_in_pcm_frames()</span> may never return.
|
|
</p>
|
|
<p>
|
|
|
|
The current position of the cursor in PCM frames can also be retrieved:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_uint64</span> cursor;
|
|
|
|
result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to retrieve the cursor.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
You will often need to know the data format that will be returned after reading. This can be
|
|
retrieved like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_format format;
|
|
<span style="color:#0099cc">ma_uint32</span> channels;
|
|
<span style="color:#0099cc">ma_uint32</span> sampleRate;
|
|
<span style="color:#0099cc">ma_channel</span> channelMap[MA_MAX_CHANNELS];
|
|
|
|
result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to retrieve data format.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
If you do not need a specific data format property, just pass in NULL to the respective parameter.
|
|
</p>
|
|
<p>
|
|
|
|
There may be cases where you want to implement something like a sound bank where you only want to
|
|
read data within a certain range of the underlying data. To do this you can use a range:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to set the range.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
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.
|
|
</p>
|
|
<p>
|
|
|
|
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
|
|
the following:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to set the loop point.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
The loop point is relative to the current range.
|
|
</p>
|
|
<p>
|
|
|
|
It's sometimes useful to chain data sources together so that a seamless transition can be achieved.
|
|
To do this, you can use chaining:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_decoder</span> decoder1;
|
|
<span style="color:#0099cc">ma_decoder</span> decoder2;
|
|
|
|
<span style="color:#009900">// ... initialize decoders with ma_decoder_init_*() ...</span>
|
|
|
|
result = ma_data_source_set_next(&decoder1, &decoder2);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to set the next data source.</span>
|
|
}
|
|
|
|
result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to read from the decoder.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
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, <span style="font-family:monospace;">decoder1</span> is the top level data
|
|
source in the chain. When <span style="font-family:monospace;">decoder1</span> reaches the end, <span style="font-family:monospace;">decoder2</span> will start seamlessly without any
|
|
gaps.
|
|
</p>
|
|
<p>
|
|
|
|
Note that the <span style="font-family:monospace;">loop</span> 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:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_data_source_set_next(&decoder1, &decoder2); <span style="color:#009900">// decoder1 -> decoder2</span>
|
|
ma_data_source_set_next(&decoder2, &decoder1); <span style="color:#009900">// decoder2 -> decoder1 (loop back to the start).</span>
|
|
</pre></div><p>
|
|
|
|
Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically
|
|
changing links while the audio thread is in the middle of reading.
|
|
</p>
|
|
<p>
|
|
|
|
Do not use <span style="font-family:monospace;">ma_decoder_seek_to_pcm_frame()</span> 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.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="CustomDataSources" class="man">4.1. Custom Data Sources</h2>
|
|
<p>
|
|
You can implement a custom data source by implementing the functions in <span style="font-family:monospace;">ma_data_source_vtable</span>.
|
|
Your custom object must have <span style="font-family:monospace;">ma_data_source_base</span> as it's first member:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">struct</span> my_data_source
|
|
{
|
|
ma_data_source_base base;
|
|
...
|
|
};
|
|
</pre></div><p>
|
|
|
|
In your initialization routine, you need to call <span style="font-family:monospace;">ma_data_source_init()</span> in order to set up the
|
|
base object (<span style="font-family:monospace;">ma_data_source_base</span>):
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">static</span> <span style="color:#0099cc">ma_result</span> my_data_source_read(<span style="color:#0099cc">ma_data_source</span>* pDataSource, <span style="color:#0033ff">void</span>* pFramesOut, <span style="color:#0099cc">ma_uint64</span> frameCount, <span style="color:#0099cc">ma_uint64</span>* pFramesRead)
|
|
{
|
|
<span style="color:#009900">// Read data here. Output in the same format returned by my_data_source_get_data_format().</span>
|
|
}
|
|
|
|
<span style="color:#0033ff">static</span> <span style="color:#0099cc">ma_result</span> my_data_source_seek(<span style="color:#0099cc">ma_data_source</span>* pDataSource, <span style="color:#0099cc">ma_uint64</span> frameIndex)
|
|
{
|
|
<span style="color:#009900">// Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported.</span>
|
|
}
|
|
|
|
<span style="color:#0033ff">static</span> <span style="color:#0099cc">ma_result</span> my_data_source_get_data_format(<span style="color:#0099cc">ma_data_source</span>* pDataSource, ma_format* pFormat, <span style="color:#0099cc">ma_uint32</span>* pChannels, <span style="color:#0099cc">ma_uint32</span>* pSampleRate, <span style="color:#0099cc">ma_channel</span>* pChannelMap, size_t channelMapCap)
|
|
{
|
|
<span style="color:#009900">// Return the format of the data here.</span>
|
|
}
|
|
|
|
<span style="color:#0033ff">static</span> <span style="color:#0099cc">ma_result</span> my_data_source_get_cursor(<span style="color:#0099cc">ma_data_source</span>* pDataSource, <span style="color:#0099cc">ma_uint64</span>* pCursor)
|
|
{
|
|
<span style="color:#009900">// Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor.</span>
|
|
}
|
|
|
|
<span style="color:#0033ff">static</span> <span style="color:#0099cc">ma_result</span> my_data_source_get_length(<span style="color:#0099cc">ma_data_source</span>* pDataSource, <span style="color:#0099cc">ma_uint64</span>* pLength)
|
|
{
|
|
<span style="color:#009900">// 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.</span>
|
|
}
|
|
|
|
<span style="color:#0033ff">static</span> g_my_data_source_vtable =
|
|
{
|
|
my_data_source_read,
|
|
my_data_source_seek,
|
|
my_data_source_get_data_format,
|
|
my_data_source_get_cursor,
|
|
my_data_source_get_length
|
|
};
|
|
|
|
<span style="color:#0099cc">ma_result</span> my_data_source_init(my_data_source* pMyDataSource)
|
|
{
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
ma_data_source_config baseConfig;
|
|
|
|
baseConfig = ma_data_source_config_init();
|
|
baseConfig.vtable = &g_my_data_source_vtable;
|
|
|
|
result = ma_data_source_init(&baseConfig, &pMyDataSource->base);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result;
|
|
}
|
|
|
|
<span style="color:#009900">// ... do the initialization of your custom data source here ...</span>
|
|
|
|
<span style="color:#0033ff">return</span> MA_SUCCESS;
|
|
}
|
|
|
|
<span style="color:#0033ff">void</span> my_data_source_uninit(my_data_source* pMyDataSource)
|
|
{
|
|
<span style="color:#009900">// ... do the uninitialization of your custom data source here ...</span>
|
|
|
|
<span style="color:#009900">// You must uninitialize the base data source.</span>
|
|
ma_data_source_uninit(&pMyDataSource->base);
|
|
}
|
|
</pre></div><p>
|
|
|
|
Note that <span style="font-family:monospace;">ma_data_source_init()</span> and <span style="font-family:monospace;">ma_data_source_uninit()</span> are never called directly outside
|
|
of the custom data source. It's up to the custom data source itself to call these within their own
|
|
init/uninit functions.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="Engine" class="man">5. Engine</h1>
|
|
<p>
|
|
The <span style="font-family:monospace;">ma_engine</span> API is a high level API for managing and mixing sounds and effect processing. The
|
|
<span style="font-family:monospace;">ma_engine</span> object encapsulates a resource manager and a node graph, both of which will be
|
|
explained in more detail later.
|
|
</p>
|
|
<p>
|
|
|
|
Sounds are called <span style="font-family:monospace;">ma_sound</span> and are created from an engine. Sounds can be associated with a mixing
|
|
group called <span style="font-family:monospace;">ma_sound_group</span> which are also created from the engine. Both <span style="font-family:monospace;">ma_sound</span> and
|
|
<span style="font-family:monospace;">ma_sound_group</span> objects are nodes within the engine's node graph.
|
|
</p>
|
|
<p>
|
|
|
|
When the engine is initialized, it will normally create a device internally. If you would rather
|
|
manage the device yourself, you can do so and just pass a pointer to it via the engine config when
|
|
you initialize the engine. You can also just use the engine without a device, which again can be
|
|
configured via the engine config.
|
|
</p>
|
|
<p>
|
|
|
|
The most basic way to initialize the engine is with a default config, like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
ma_engine engine;
|
|
|
|
result = ma_engine_init(NULL, &engine);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to initialize the engine.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
This will result in the engine initializing a playback device using the operating system's default
|
|
device. This will be sufficient for many use cases, but if you need more flexibility you'll want to
|
|
configure the engine with an engine config:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
ma_engine engine;
|
|
ma_engine_config engineConfig;
|
|
|
|
engineConfig = ma_engine_config_init();
|
|
engineConfig.pPlaybackDevice = &myDevice;
|
|
|
|
result = ma_engine_init(&engineConfig, &engine);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to initialize the engine.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
In the example above we're passing in a pre-initialized device. Since the caller is the one in
|
|
control of the device's data callback, it's their responsibility to manually call
|
|
<span style="font-family:monospace;">ma_engine_read_pcm_frames()</span> from inside their data callback:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">void</span> playback_data_callback(<span style="color:#0099cc">ma_device</span>* pDevice, <span style="color:#0033ff">void</span>* pOutput, <span style="color:#0033ff">const</span> <span style="color:#0033ff">void</span>* pInput, <span style="color:#0099cc">ma_uint32</span> frameCount)
|
|
{
|
|
ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL);
|
|
}
|
|
</pre></div><p>
|
|
|
|
You can also use the engine independent of a device entirely:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
ma_engine engine;
|
|
ma_engine_config engineConfig;
|
|
|
|
engineConfig = ma_engine_config_init();
|
|
engineConfig.noDevice = MA_TRUE;
|
|
engineConfig.channels = 2; <span style="color:#009900">// Must be set when not using a device.</span>
|
|
engineConfig.sampleRate = 48000; <span style="color:#009900">// Must be set when not using a device.</span>
|
|
|
|
result = ma_engine_init(&engineConfig, &engine);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to initialize the engine.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
Note that when you're not using a device, you must set the channel count and sample rate in the
|
|
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 <span style="font-family:monospace;">ma_engine_read_pcm_frames()</span> to process audio
|
|
data from the engine. This kind of setup is useful if you want to do something like offline
|
|
processing.
|
|
</p>
|
|
<p>
|
|
|
|
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:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
ma_engine engine1;
|
|
ma_engine engine2;
|
|
ma_engine_config engineConfig;
|
|
|
|
engineConfig = ma_engine_config_init();
|
|
engineConfig.pResourceManager = &myResourceManager;
|
|
|
|
ma_engine_init(&engineConfig, &engine1);
|
|
ma_engine_init(&engineConfig, &engine2);
|
|
</pre></div><p>
|
|
|
|
In this example we are initializing two engines, both of which are sharing the same resource
|
|
manager. This is especially useful for saving memory when loading the same file across multiple
|
|
engines. If you were not to use a shared resource manager, each engine instance would use their own
|
|
which would result in any sounds that are used between both engine's being loaded twice. By using
|
|
a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you
|
|
need to output to multiple playback devices, such as in a local multiplayer game where each player
|
|
is using their own set of headphones.
|
|
</p>
|
|
<p>
|
|
|
|
By default an engine will be in a started state. To make it so the engine is not automatically
|
|
started you can configure it as such:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
engineConfig.noAutoStart = MA_TRUE;
|
|
|
|
<span style="color:#009900">// The engine will need to be started manually.</span>
|
|
ma_engine_start(&engine);
|
|
|
|
<span style="color:#009900">// Later on the engine can be stopped with ma_engine_stop().</span>
|
|
ma_engine_stop(&engine);
|
|
</pre></div><p>
|
|
|
|
The concept of starting or stopping an engine is only relevant when using the engine with a
|
|
device. Attempting to start or stop an engine that is not associated with a device will result in
|
|
<span style="font-family:monospace;">MA_INVALID_OPERATION</span>.
|
|
</p>
|
|
<p>
|
|
|
|
The master volume of the engine can be controlled with <span style="font-family:monospace;">ma_engine_set_volume()</span> which takes a
|
|
linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you
|
|
prefer decibel based volume control, use <span style="font-family:monospace;">ma_volume_db_to_linear()</span> to convert from dB to linear.
|
|
</p>
|
|
<p>
|
|
|
|
When a sound is spatialized, it is done so relative to a listener. An engine can be configured to
|
|
have multiple listeners which can be configured via the config:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
engineConfig.listenerCount = 2;
|
|
</pre></div><p>
|
|
|
|
The maximum number of listeners is restricted to <span style="font-family:monospace;">MA_ENGINE_MAX_LISTENERS</span>. By default, when a
|
|
sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound
|
|
to a specific listener which will be explained later. Listener's have a position, direction, cone,
|
|
and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up
|
|
to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The
|
|
position, direction and velocity are all specified in absolute terms:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ);
|
|
</pre></div><p>
|
|
|
|
The direction of the listener represents it's forward vector. The listener's up vector can also be
|
|
specified and defaults to +1 on the Y axis.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ);
|
|
ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0);
|
|
</pre></div><p>
|
|
|
|
The engine supports directional attenuation. The listener can have a cone the controls how sound is
|
|
attenuated based on the listener's direction. When a sound is between the inner and outer cones, it
|
|
will be attenuated between 1 and the cone's outer gain:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain);
|
|
</pre></div><p>
|
|
|
|
When a sound is inside the inner code, no directional attenuation is applied. When the sound is
|
|
outside of the outer cone, the attenuation will be set to <span style="font-family:monospace;">outerGain</span> in the example above. When
|
|
the sound is in between the inner and outer cones, the attenuation will be interpolated between 1
|
|
and the outer gain.
|
|
</p>
|
|
<p>
|
|
|
|
The engine's coordinate system follows the OpenGL coordinate system where positive X points right,
|
|
positive Y points up and negative Z points forward.
|
|
</p>
|
|
<p>
|
|
|
|
The simplest and least flexible way to play a sound is like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_engine_play_sound(&engine, <span style="color:#cc3300">"my_sound.wav"</span>, pGroup);
|
|
</pre></div><p>
|
|
|
|
This is a "fire and forget" style of function. The engine will manage the <span style="font-family:monospace;">ma_sound</span> object
|
|
internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility
|
|
you'll want to initialize a sound object:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound sound;
|
|
|
|
result = ma_sound_init_from_file(&engine, <span style="color:#cc3300">"my_sound.wav"</span>, flags, pGroup, NULL, &sound);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result; <span style="color:#009900">// Failed to load sound.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
Sounds need to be uninitialized with <span style="font-family:monospace;">ma_sound_uninit()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
The example above loads a sound from a file. If the resource manager has been disabled you will not
|
|
be able to use this function and instead you'll need to initialize a sound directly from a data
|
|
source:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound sound;
|
|
|
|
result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result;
|
|
}
|
|
</pre></div><p>
|
|
|
|
Each <span style="font-family:monospace;">ma_sound</span> object represents a single instance of the sound. If you want to play the same
|
|
sound multiple times at the same time, you need to initialize a separate <span style="font-family:monospace;">ma_sound</span> object.
|
|
</p>
|
|
<p>
|
|
|
|
For the most flexibility when initializing sounds, use <span style="font-family:monospace;">ma_sound_init_ex()</span>. This uses miniaudio's
|
|
standard config/init pattern:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound sound;
|
|
ma_sound_config soundConfig;
|
|
|
|
soundConfig = ma_sound_config_init();
|
|
soundConfig.pFilePath = NULL; <span style="color:#009900">// Set this to load from a file path.</span>
|
|
soundConfig.pDataSource = NULL; <span style="color:#009900">// Set this to initialize from an existing data source.</span>
|
|
soundConfig.pInitialAttachment = &someNodeInTheNodeGraph;
|
|
soundConfig.initialAttachmentInputBusIndex = 0;
|
|
soundConfig.channelsIn = 1;
|
|
soundConfig.channelsOut = 0; <span style="color:#009900">// Set to 0 to use the engine's native channel count.</span>
|
|
|
|
result = ma_sound_init_ex(&soundConfig, &sound);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result;
|
|
}
|
|
</pre></div><p>
|
|
|
|
In the example above, the sound is being initialized without a file nor a data source. This is
|
|
valid, in which case the sound acts as a node in the middle of the node graph. This means you can
|
|
connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly
|
|
what a <span style="font-family:monospace;">ma_sound_group</span> is.
|
|
</p>
|
|
<p>
|
|
|
|
When loading a sound, you specify a set of flags that control how the sound is loaded and what
|
|
features are enabled for that sound. When no flags are set, the sound will be fully loaded into
|
|
memory in exactly the same format as how it's stored on the file system. The resource manager will
|
|
allocate a block of memory and then load the file directly into it. When reading audio data, it
|
|
will be decoded dynamically on the fly. In order to save processing time on the audio thread, it
|
|
might be beneficial to pre-decode the sound. You can do this with the <span style="font-family:monospace;">MA_SOUND_FLAG_DECODE</span> flag:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_init_from_file(&engine, <span style="color:#cc3300">"my_sound.wav"</span>, MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound);
|
|
</pre></div><p>
|
|
|
|
By default, sounds will be loaded synchronously, meaning <span style="font-family:monospace;">ma_sound_init_*()</span> will not return until
|
|
the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously
|
|
by specificying the <span style="font-family:monospace;">MA_SOUND_FLAG_ASYNC</span> flag:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_init_from_file(&engine, <span style="color:#cc3300">"my_sound.wav"</span>, MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);
|
|
</pre></div><p>
|
|
|
|
This will result in <span style="font-family:monospace;">ma_sound_init_*()</span> returning quickly, but the sound won't yet have been fully
|
|
loaded. When you start the sound, it won't output anything until some sound is available. The sound
|
|
will start outputting audio before the sound has been fully decoded when the <span style="font-family:monospace;">MA_SOUND_FLAG_DECODE</span>
|
|
is specified.
|
|
</p>
|
|
<p>
|
|
|
|
If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A
|
|
fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal
|
|
counter hit's zero. You can specify a fence like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
ma_fence fence;
|
|
ma_sound sounds[4];
|
|
|
|
result = ma_fence_init(&fence);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCES) {
|
|
<span style="color:#0033ff">return</span> result;
|
|
}
|
|
|
|
<span style="color:#009900">// Load some sounds asynchronously.</span>
|
|
<span style="color:#0033ff">for</span> (<span style="color:#0033ff">int</span> iSound = 0; iSound < 4; iSound += 1) {
|
|
ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]);
|
|
}
|
|
|
|
<span style="color:#009900">// ... do some other stuff here in the mean time ...</span>
|
|
|
|
<span style="color:#009900">// Wait for all sounds to finish loading.</span>
|
|
ma_fence_wait(&fence);
|
|
</pre></div><p>
|
|
|
|
If loading the entire sound into memory is prohibitive, you can also configure the engine to stream
|
|
the audio data:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_init_from_file(&engine, <span style="color:#cc3300">"my_sound.wav"</span>, MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound);
|
|
</pre></div><p>
|
|
|
|
When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work
|
|
fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music
|
|
tracks in games.
|
|
</p>
|
|
<p>
|
|
|
|
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.
|
|
If you would instead rather leave the sound unattached by default, you can can specify the
|
|
<span style="font-family:monospace;">MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT</span> flag. This is useful if you want to set up a complex node
|
|
graph.
|
|
</p>
|
|
<p>
|
|
|
|
Sounds are not started by default. To start a sound, use <span style="font-family:monospace;">ma_sound_start()</span>. Stop a sound with
|
|
<span style="font-family:monospace;">ma_sound_stop()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Sounds can have their volume controlled with <span style="font-family:monospace;">ma_sound_set_volume()</span> in the same way as the
|
|
engine's master volume.
|
|
</p>
|
|
<p>
|
|
|
|
Sounds support stereo panning and pitching. Set the pan with <span style="font-family:monospace;">ma_sound_set_pan()</span>. Setting the pan
|
|
to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas
|
|
+1 will shift it to the right. The pitch can be controlled with <span style="font-family:monospace;">ma_sound_set_pitch()</span>. A larger
|
|
value will result in a higher pitch. The pitch must be greater than 0.
|
|
</p>
|
|
<p>
|
|
|
|
The engine supports 3D spatialization of sounds. By default sounds will have spatialization
|
|
enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways
|
|
to disable spatialization of a sound:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#009900">// Disable spatialization at initialization time via a flag:</span>
|
|
ma_sound_init_from_file(&engine, <span style="color:#cc3300">"my_sound.wav"</span>, MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound);
|
|
|
|
<span style="color:#009900">// Dynamically disable or enable spatialization post-initialization:</span>
|
|
ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled);
|
|
</pre></div><p>
|
|
|
|
By default sounds will be spatialized based on the closest listener. If a sound should always be
|
|
spatialized relative to a specific listener it can be pinned to one:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_pinned_listener_index(&sound, listenerIndex);
|
|
</pre></div><p>
|
|
|
|
Like listeners, sounds have a position. By default, the position of a sound is in absolute space,
|
|
but it can be changed to be relative to a listener:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_positioning(&sound, ma_positioning_relative);
|
|
</pre></div><p>
|
|
|
|
Note that relative positioning of a sound only makes sense if there is either only one listener, or
|
|
the sound is pinned to a specific listener. To set the position of a sound:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_position(&sound, posX, posY, posZ);
|
|
</pre></div><p>
|
|
|
|
The direction works the same way as a listener and represents the sound's forward direction:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ);
|
|
</pre></div><p>
|
|
|
|
Sound's also have a cone for controlling directional attenuation. This works exactly the same as
|
|
listeners:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain);
|
|
</pre></div><p>
|
|
|
|
The velocity of a sound is used for doppler effect and can be set as such:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ);
|
|
</pre></div><p>
|
|
|
|
The engine supports different attenuation models which can be configured on a per-sound basis. By
|
|
default the attenuation model is set to <span style="font-family:monospace;">ma_attenuation_model_inverse</span> which is the equivalent to
|
|
OpenAL's <span style="font-family:monospace;">AL_INVERSE_DISTANCE_CLAMPED</span>. Configure the attenuation model like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse);
|
|
</pre></div><p>
|
|
|
|
The supported attenuation models include the following:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
ma_attenuation_model_none</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
No distance attenuation.</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_attenuation_model_inverse</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Equivalent to <span style="font-family:monospace;">AL_INVERSE_DISTANCE_CLAMPED</span>.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_attenuation_model_linear</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Linear attenuation.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_attenuation_model_exponential</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Exponential attenuation.</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
To control how quickly a sound rolls off as it moves away from the listener, you need to configure
|
|
the rolloff:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_rolloff(&sound, rolloff);
|
|
</pre></div><p>
|
|
|
|
You can control the minimum and maximum gain to apply from spatialization:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_min_gain(&sound, minGain);
|
|
ma_sound_set_max_gain(&sound, maxGain);
|
|
</pre></div><p>
|
|
|
|
Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for
|
|
the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain
|
|
volume after the listener moves further away and to have sounds play a maximum volume when the
|
|
listener is within a certain distance:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_min_distance(&sound, minDistance);
|
|
ma_sound_set_max_distance(&sound, maxDistance);
|
|
</pre></div><p>
|
|
|
|
The engine's spatialization system supports doppler effect. The doppler factor can be configure on
|
|
a per-sound basis like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_set_doppler_factor(&sound, dopplerFactor);
|
|
</pre></div><p>
|
|
|
|
You can fade sounds in and out with <span style="font-family:monospace;">ma_sound_set_fade_in_pcm_frames()</span> and
|
|
<span style="font-family:monospace;">ma_sound_set_fade_in_milliseconds()</span>. Set the volume to -1 to use the current volume as the
|
|
starting volume:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#009900">// Fade in over 1 second.</span>
|
|
ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000);
|
|
|
|
<span style="color:#009900">// ... sometime later ...</span>
|
|
|
|
<span style="color:#009900">// Fade out over 1 second, starting from the current volume.</span>
|
|
ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000);
|
|
</pre></div><p>
|
|
|
|
By default sounds will start immediately, but sometimes for timing and synchronization purposes it
|
|
can be useful to schedule a sound to start or stop:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#009900">// Start the sound in 1 second from now.</span>
|
|
ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1));
|
|
|
|
<span style="color:#009900">// Stop the sound in 2 seconds from now.</span>
|
|
ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2));
|
|
</pre></div><p>
|
|
|
|
Note that scheduling a start time still requires an explicit call to <span style="font-family:monospace;">ma_sound_start()</span> before
|
|
anything will play.
|
|
</p>
|
|
<p>
|
|
|
|
The time is specified in global time which is controlled by the engine. You can get the engine's
|
|
current time with <span style="font-family:monospace;">ma_engine_get_time()</span>. The engine's global time is incremented automatically as
|
|
audio data is read, but it can be reset with <span style="font-family:monospace;">ma_engine_set_time()</span> in case it needs to be
|
|
resynchronized for some reason.
|
|
</p>
|
|
<p>
|
|
|
|
To determine whether or not a sound is currently playing, use <span style="font-family:monospace;">ma_sound_is_playing()</span>. This will
|
|
take the scheduled start and stop times into account.
|
|
</p>
|
|
<p>
|
|
|
|
Whether or not a sound should loop can be controlled with <span style="font-family:monospace;">ma_sound_set_looping()</span>. Sounds will not
|
|
be looping by default. Use <span style="font-family:monospace;">ma_sound_is_looping()</span> to determine whether or not a sound is looping.
|
|
</p>
|
|
<p>
|
|
|
|
Use <span style="font-family:monospace;">ma_sound_at_end()</span> to determine whether or not a sound is currently at the end. For a looping
|
|
sound this should never return true.
|
|
</p>
|
|
<p>
|
|
|
|
Internally a sound wraps around a data source. Some APIs exist to control the underlying data
|
|
source, mainly for convenience:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_sound_seek_to_pcm_frame(&sound, frameIndex);
|
|
ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity);
|
|
ma_sound_get_cursor_in_pcm_frames(&sound, &cursor);
|
|
ma_sound_get_length_in_pcm_frames(&sound, &length);
|
|
</pre></div><p>
|
|
|
|
Sound groups have the same API as sounds, only they are called <span style="font-family:monospace;">ma_sound_group</span>, and since they do
|
|
not have any notion of a data source, anything relating to a data source is unavailable.
|
|
</p>
|
|
<p>
|
|
|
|
Internally, sound data is loaded via the <span style="font-family:monospace;">ma_decoder</span> API which means by default in only supports
|
|
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
|
|
resource manager and configure it appropriately. See the "Resource Management" section below for
|
|
details on how to set this up.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="ResourceManagement" class="man">6. Resource Management</h1>
|
|
<p>
|
|
Many programs will want to manage sound resources for things such as reference counting and
|
|
streaming. This is supported by miniaudio via the <span style="font-family:monospace;">ma_resource_manager</span> API.
|
|
</p>
|
|
<p>
|
|
|
|
The resource manager is mainly responsible for the following:
|
|
</p>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
Loading of sound files into memory with reference counting.</li>
|
|
<li>
|
|
Streaming of sound data</li>
|
|
</ul>
|
|
<p>
|
|
|
|
When loading a sound file, the resource manager will give you back a <span style="font-family:monospace;">ma_data_source</span> compatible
|
|
object called <span style="font-family:monospace;">ma_resource_manager_data_source</span>. This object can be passed into any
|
|
<span style="font-family:monospace;">ma_data_source</span> API which is how you can read and seek audio data. When loading a sound file, you
|
|
specify whether or not you want the sound to be fully loaded into memory (and optionally
|
|
pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want
|
|
the data to be loaded asynchronously.
|
|
</p>
|
|
<p>
|
|
|
|
The example below is how you can initialize a resource manager using it's default configuration:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_resource_manager_config config;
|
|
ma_resource_manager resourceManager;
|
|
|
|
config = ma_resource_manager_config_init();
|
|
result = ma_resource_manager_init(&config, &resourceManager);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
ma_device_uninit(&device);
|
|
printf(<span style="color:#cc3300">"Failed to initialize the resource manager."</span>);
|
|
<span style="color:#0033ff">return</span> -1;
|
|
}
|
|
</pre></div><p>
|
|
|
|
You can configure the format, channels and sample rate of the decoded audio data. By default it
|
|
will use the file's native data format, but you can configure it to use a consistent format. This
|
|
is useful for offloading the cost of data conversion to load time rather than dynamically
|
|
converting at mixing time. To do this, you configure the decoded format, channels and sample rate
|
|
like the code below:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
config = ma_resource_manager_config_init();
|
|
config.decodedFormat = device.playback.format;
|
|
config.decodedChannels = device.playback.channels;
|
|
config.decodedSampleRate = device.sampleRate;
|
|
</pre></div><p>
|
|
|
|
In the code above, the resource manager will be configured so that any decoded audio data will be
|
|
pre-converted at load time to the device's native data format. If instead you used defaults and
|
|
the data format of the file did not match the device's data format, you would need to convert the
|
|
data at mixing time which may be prohibitive in high-performance and large scale scenarios like
|
|
games.
|
|
</p>
|
|
<p>
|
|
|
|
Internally the resource manager uses the <span style="font-family:monospace;">ma_decoder</span> API to load sounds. This means by default it
|
|
only supports decoders that are built into miniaudio. It's possible to support additional encoding
|
|
formats through the use of custom decoders. To do so, pass in your <span style="font-family:monospace;">ma_decoding_backend_vtable</span>
|
|
vtables into the resource manager config:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_decoding_backend_vtable* pCustomBackendVTables[] =
|
|
{
|
|
&g_ma_decoding_backend_vtable_libvorbis,
|
|
&g_ma_decoding_backend_vtable_libopus
|
|
};
|
|
|
|
...
|
|
|
|
resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables;
|
|
resourceManagerConfig.customDecodingBackendCount = <span style="color:#0033ff">sizeof</span>(pCustomBackendVTables) / <span style="color:#0033ff">sizeof</span>(pCustomBackendVTables[0]);
|
|
resourceManagerConfig.pCustomDecodingBackendUserData = NULL;
|
|
</pre></div><p>
|
|
|
|
This system can allow you to support any kind of file format. See the "Decoding" section for
|
|
details on how to implement custom decoders. The miniaudio repository includes examples for Opus
|
|
via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile.
|
|
</p>
|
|
<p>
|
|
|
|
Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the
|
|
decoding of a page, a job will be posted to a queue which will then be processed by a job thread.
|
|
By default there will be only one job thread running, but this can be configured, like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
config = ma_resource_manager_config_init();
|
|
config.jobThreadCount = MY_JOB_THREAD_COUNT;
|
|
</pre></div><p>
|
|
|
|
By default job threads are managed internally by the resource manager, however you can also self
|
|
manage your job threads if, for example, you want to integrate the job processing into your
|
|
existing job infrastructure, or if you simply don't like the way the resource manager does it. To
|
|
do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first
|
|
need to retrieve a job using <span style="font-family:monospace;">ma_resource_manager_next_job()</span> and then process it using
|
|
<span style="font-family:monospace;">ma_job_process()</span>:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
config = ma_resource_manager_config_init();
|
|
config.jobThreadCount = 0; <span style="color:#009900">// Don't manage any job threads internally.</span>
|
|
config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; <span style="color:#009900">// Optional. Makes <span style="font-family:monospace;">ma_resource_manager_next_job()</span> non-blocking.</span>
|
|
|
|
<span style="color:#009900">// ... Initialize your custom job threads ...</span>
|
|
|
|
<span style="color:#0033ff">void</span> my_custom_job_thread(...)
|
|
{
|
|
<span style="color:#0033ff">for</span> (;;) {
|
|
ma_job job;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_resource_manager_next_job(pMyResourceManager, &job);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">if</span> (result == MA_NOT_DATA_AVAILABLE) {
|
|
<span style="color:#009900">// No jobs are available. Keep going. Will only get this if the resource manager was initialized</span>
|
|
<span style="color:#009900">// with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.</span>
|
|
<span style="color:#0033ff">continue</span>;
|
|
} <span style="color:#0033ff">else</span> <span style="color:#0033ff">if</span> (result == MA_CANCELLED) {
|
|
<span style="color:#009900">// MA_JOB_TYPE_QUIT was posted. Exit.</span>
|
|
<span style="color:#0033ff">break</span>;
|
|
} <span style="color:#0033ff">else</span> {
|
|
<span style="color:#009900">// Some other error occurred.</span>
|
|
<span style="color:#0033ff">break</span>;
|
|
}
|
|
}
|
|
|
|
ma_job_process(&job);
|
|
}
|
|
}
|
|
</pre></div><p>
|
|
|
|
In the example above, the <span style="font-family:monospace;">MA_JOB_TYPE_QUIT</span> event is the used as the termination
|
|
indicator, but you can use whatever you would like to terminate the thread. The call to
|
|
<span style="font-family:monospace;">ma_resource_manager_next_job()</span> is blocking by default, but can be configured to be non-blocking
|
|
by initializing the resource manager with the <span style="font-family:monospace;">MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING</span> configuration
|
|
flag. Note that the <span style="font-family:monospace;">MA_JOB_TYPE_QUIT</span> will never be removed from the job queue. This
|
|
is to give every thread the opportunity to catch the event and terminate naturally.
|
|
</p>
|
|
<p>
|
|
|
|
When loading a file, it's sometimes convenient to be able to customize how files are opened and
|
|
read instead of using standard <span style="font-family:monospace;">fopen()</span>, <span style="font-family:monospace;">fclose()</span>, etc. which is what miniaudio will use by
|
|
default. This can be done by setting <span style="font-family:monospace;">pVFS</span> member of the resource manager's config:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#009900">// Initialize your custom VFS object. See documentation for VFS for information on how to do this.</span>
|
|
my_custom_vfs vfs = my_custom_vfs_init();
|
|
|
|
config = ma_resource_manager_config_init();
|
|
config.pVFS = &vfs;
|
|
</pre></div><p>
|
|
|
|
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
|
|
use the operating system's normal file operations. This is default.
|
|
</p>
|
|
<p>
|
|
|
|
To load a sound file and create a data source, call <span style="font-family:monospace;">ma_resource_manager_data_source_init()</span>. When
|
|
loading a sound you need to specify the file path and options for how the sounds should be loaded.
|
|
By default a sound will be loaded synchronously. The returned data source is owned by the caller
|
|
which means the caller is responsible for the allocation and freeing of the data source. Below is
|
|
an example for initializing a data source:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_resource_manager_data_source dataSource;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
|
|
<span style="color:#009900">// ...</span>
|
|
|
|
<span style="color:#009900">// A ma_resource_manager_data_source object is compatible with the <span style="font-family:monospace;">ma_data_source</span> API. To read data, just call</span>
|
|
<span style="color:#009900">// the <span style="font-family:monospace;">ma_data_source_read_pcm_frames()</span> like you would with any normal data source.</span>
|
|
result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Failed to read PCM frames.</span>
|
|
}
|
|
|
|
<span style="color:#009900">// ...</span>
|
|
|
|
ma_resource_manager_data_source_uninit(pResourceManager, &dataSource);
|
|
</pre></div><p>
|
|
|
|
The <span style="font-family:monospace;">flags</span> parameter specifies how you want to perform loading of the sound file. It can be a
|
|
combination of the following flags:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT
|
|
</pre></div><p>
|
|
|
|
When no flags are specified (set to 0), the sound will be fully loaded into memory, but not
|
|
decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when
|
|
<span style="font-family:monospace;">ma_data_source_read_pcm_frames()</span> is called. To instead decode the audio data before storing it in
|
|
memory, use the <span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE</span> flag. By default, the sound file will
|
|
be loaded synchronously, meaning <span style="font-family:monospace;">ma_resource_manager_data_source_init()</span> will only return after
|
|
the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You
|
|
can instead load the sound asynchronously using the <span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC</span> flag.
|
|
This will result in <span style="font-family:monospace;">ma_resource_manager_data_source_init()</span> returning quickly, but no data will be
|
|
returned by <span style="font-family:monospace;">ma_data_source_read_pcm_frames()</span> until some data is available. When no data is
|
|
available because the asynchronous decoding hasn't caught up, <span style="font-family:monospace;">MA_BUSY</span> will be returned by
|
|
<span style="font-family:monospace;">ma_data_source_read_pcm_frames()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you
|
|
can instead stream audio data which you can do by specifying the
|
|
<span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM</span> flag. When streaming, data will be decoded in 1
|
|
second pages. When a new page needs to be decoded, a job will be posted to the job queue and then
|
|
subsequently processed in a job thread.
|
|
</p>
|
|
<p>
|
|
|
|
For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means
|
|
multiple calls to <span style="font-family:monospace;">ma_resource_manager_data_source_init()</span> with the same file path will result in
|
|
the file data only being loaded once. Each call to <span style="font-family:monospace;">ma_resource_manager_data_source_init()</span> must be
|
|
matched up with a call to <span style="font-family:monospace;">ma_resource_manager_data_source_uninit()</span>. Sometimes it can be useful
|
|
for a program to register self-managed raw audio data and associate it with a file path. Use the
|
|
<span style="font-family:monospace;">ma_resource_manager_register_*()</span> and <span style="font-family:monospace;">ma_resource_manager_unregister_*()</span> APIs to do this.
|
|
<span style="font-family:monospace;">ma_resource_manager_register_decoded_data()</span> is used to associate a pointer to raw, self-managed
|
|
decoded audio data in the specified data format with the specified name. Likewise,
|
|
<span style="font-family:monospace;">ma_resource_manager_register_encoded_data()</span> is used to associate a pointer to raw self-managed
|
|
encoded audio data (the raw file data) with the specified name. Note that these names need not be
|
|
actual file paths. When <span style="font-family:monospace;">ma_resource_manager_data_source_init()</span> is called (without the
|
|
<span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM</span> flag), the resource manager will look for these
|
|
explicitly registered data buffers and, if found, will use it as the backing data for the data
|
|
source. Note that the resource manager does *not* make a copy of this data so it is up to the
|
|
caller to ensure the pointer stays valid for it's lifetime. Use
|
|
<span style="font-family:monospace;">ma_resource_manager_unregister_data()</span> to unregister the self-managed data. You can also use
|
|
<span style="font-family:monospace;">ma_resource_manager_register_file()</span> and <span style="font-family:monospace;">ma_resource_manager_unregister_file()</span> to register and
|
|
unregister a file. It does not make sense to use the <span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM</span>
|
|
flag with a self-managed data pointer.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="AsynchronousLoadingandSynchronization" class="man">6.1. Asynchronous Loading and Synchronization</h2>
|
|
<p>
|
|
When loading asynchronously, it can be useful to poll whether or not loading has finished. Use
|
|
<span style="font-family:monospace;">ma_resource_manager_data_source_result()</span> to determine this. For in-memory sounds, this will
|
|
return <span style="font-family:monospace;">MA_SUCCESS</span> when the file has been *entirely* decoded. If the sound is still being decoded,
|
|
<span style="font-family:monospace;">MA_BUSY</span> will be returned. Otherwise, some other error code will be returned if the sound failed
|
|
to load. For streaming data sources, <span style="font-family:monospace;">MA_SUCCESS</span> will be returned when the first page has been
|
|
decoded and the sound is ready to be played. If the first page is still being decoded, <span style="font-family:monospace;">MA_BUSY</span>
|
|
will be returned. Otherwise, some other error code will be returned if the sound failed to load.
|
|
</p>
|
|
<p>
|
|
|
|
In addition to polling, you can also use a simple synchronization object called a "fence" to wait
|
|
for asynchronously loaded sounds to finish. This is called <span style="font-family:monospace;">ma_fence</span>. The advantage to using a
|
|
fence is that it can be used to wait for a group of sounds to finish loading rather than waiting
|
|
for sounds on an individual basis. There are two stages to loading a sound:
|
|
</p>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
Initialization of the internal decoder; and</li>
|
|
<li>
|
|
Completion of decoding of the file (the file is fully decoded)</li>
|
|
</ul>
|
|
<p>
|
|
|
|
You can specify separate fences for each of the different stages. Waiting for the initialization
|
|
of the internal decoder is important for when you need to know the sample format, channels and
|
|
sample rate of the file.
|
|
</p>
|
|
<p>
|
|
|
|
The example below shows how you could use a fence when loading a number of sounds:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#009900">// This fence will be released when all sounds are finished loading entirely.</span>
|
|
ma_fence fence;
|
|
ma_fence_init(&fence);
|
|
|
|
<span style="color:#009900">// This will be passed into the initialization routine for each sound.</span>
|
|
ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
|
|
notifications.done.pFence = &fence;
|
|
|
|
<span style="color:#009900">// Now load a bunch of sounds:</span>
|
|
<span style="color:#0033ff">for</span> (iSound = 0; iSound < soundCount; iSound += 1) {
|
|
ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, &notifications, &pSoundSources[iSound]);
|
|
}
|
|
|
|
<span style="color:#009900">// ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ...</span>
|
|
|
|
<span style="color:#009900">// Wait for loading of sounds to finish.</span>
|
|
ma_fence_wait(&fence);
|
|
</pre></div><p>
|
|
|
|
In the example above we used a fence for waiting until the entire file has been fully decoded. If
|
|
you only need to wait for the initialization of the internal decoder to complete, you can use the
|
|
<span style="font-family:monospace;">init</span> member of the <span style="font-family:monospace;">ma_resource_manager_pipeline_notifications</span> object:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
notifications.init.pFence = &fence;
|
|
</pre></div><p>
|
|
|
|
If a fence is not appropriate for your situation, you can instead use a callback that is fired on
|
|
an individual sound basis. This is done in a very similar way to fences:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">typedef</span> <span style="color:#0033ff">struct</span>
|
|
{
|
|
ma_async_notification_callbacks cb;
|
|
<span style="color:#0033ff">void</span>* pMyData;
|
|
} my_notification;
|
|
|
|
<span style="color:#0033ff">void</span> my_notification_callback(ma_async_notification* pNotification)
|
|
{
|
|
my_notification* pMyNotification = (my_notification*)pNotification;
|
|
|
|
<span style="color:#009900">// Do something in response to the sound finishing loading.</span>
|
|
}
|
|
|
|
...
|
|
|
|
my_notification myCallback;
|
|
myCallback.cb.onSignal = my_notification_callback;
|
|
myCallback.pMyData = pMyData;
|
|
|
|
ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
|
|
notifications.done.pNotification = &myCallback;
|
|
|
|
ma_resource_manager_data_source_init(pResourceManager, <span style="color:#cc3300">"my_sound.wav"</span>, flags, &notifications, &mySound);
|
|
</pre></div><p>
|
|
|
|
In the example above we just extend the <span style="font-family:monospace;">ma_async_notification_callbacks</span> object and pass an
|
|
instantiation into the <span style="font-family:monospace;">ma_resource_manager_pipeline_notifications</span> in the same way as we did with
|
|
the fence, only we set <span style="font-family:monospace;">pNotification</span> instead of <span style="font-family:monospace;">pFence</span>. You can set both of these at the same
|
|
time and they should both work as expected. If using the <span style="font-family:monospace;">pNotification</span> system, you need to ensure
|
|
your <span style="font-family:monospace;">ma_async_notification_callbacks</span> object stays valid.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="ResourceManagerImplementationDetails" class="man">6.2. Resource Manager Implementation Details</h2>
|
|
<p>
|
|
Resources are managed in two main ways:
|
|
</p>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
By storing the entire sound inside an in-memory buffer (referred to as a data buffer)</li>
|
|
<li>
|
|
By streaming audio data on the fly (referred to as a data stream)</li>
|
|
</ul>
|
|
<p>
|
|
|
|
A resource managed data source (<span style="font-family:monospace;">ma_resource_manager_data_source</span>) encapsulates a data buffer or
|
|
data stream, depending on whether or not the data source was initialized with the
|
|
<span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM</span> flag. If so, it will make use of a
|
|
<span style="font-family:monospace;">ma_resource_manager_data_stream</span> object. Otherwise it will use a <span style="font-family:monospace;">ma_resource_manager_data_buffer</span>
|
|
object. Both of these objects are data sources which means they can be used with any
|
|
<span style="font-family:monospace;">ma_data_source_*()</span> API.
|
|
</p>
|
|
<p>
|
|
|
|
Another major feature of the resource manager is the ability to asynchronously decode audio files.
|
|
This relieves the audio thread of time-consuming decoding which can negatively affect scalability
|
|
due to the audio thread needing to complete it's work extremely quickly to avoid glitching.
|
|
Asynchronous decoding is achieved through a job system. There is a central multi-producer,
|
|
multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is
|
|
posted to the queue which is then read by a job thread. The number of job threads can be
|
|
configured for improved scalability, and job threads can all run in parallel without needing to
|
|
worry about the order of execution (how this is achieved is explained below).
|
|
</p>
|
|
<p>
|
|
|
|
When a sound is being loaded asynchronously, playback can begin before the sound has been fully
|
|
decoded. This enables the application to start playback of the sound quickly, while at the same
|
|
time allowing to resource manager to keep loading in the background. Since there may be less
|
|
threads than the number of sounds being loaded at a given time, a simple scheduling system is used
|
|
to keep decoding time balanced and fair. The resource manager solves this by splitting decoding
|
|
into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a
|
|
new job will be posted to start decoding the next page. By dividing up decoding into pages, an
|
|
individual sound shouldn't ever delay every other sound from having their first page decoded. Of
|
|
course, when loading many sounds at the same time, there will always be an amount of time required
|
|
to process jobs in the queue so in heavy load situations there will still be some delay. To
|
|
determine if a data source is ready to have some frames read, use
|
|
<span style="font-family:monospace;">ma_resource_manager_data_source_get_available_frames()</span>. This will return the number of frames
|
|
available starting from the current position.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="JobQueue" class="man">6.2.1. Job Queue</h2>
|
|
<p>
|
|
The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity.
|
|
This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety.
|
|
Only a fixed number of jobs can be allocated and inserted into the queue which is done through a
|
|
lock-free data structure for allocating an index into a fixed sized array, with reference counting
|
|
for mitigation of the ABA problem. The reference count is 32-bit.
|
|
</p>
|
|
<p>
|
|
|
|
For many types of jobs it's important that they execute in a specific order. In these cases, jobs
|
|
are executed serially. For the resource manager, serial execution of jobs is only required on a
|
|
per-object basis (per data buffer or per data stream). Each of these objects stores an execution
|
|
counter. When a job is posted it is associated with an execution counter. When the job is
|
|
processed, it checks if the execution counter of the job equals the execution counter of the
|
|
owning object and if so, processes the job. If the counters are not equal, the job will be posted
|
|
back onto the job queue for later processing. When the job finishes processing the execution order
|
|
of the main object is incremented. This system means the no matter how many job threads are
|
|
executing, decoding of an individual sound will always get processed serially. The advantage to
|
|
having multiple threads comes into play when loading multiple sounds at the same time.
|
|
</p>
|
|
<p>
|
|
|
|
The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve
|
|
thread-safety for a very small section of code. This is only relevant when the resource manager
|
|
uses more than one job thread. If only using a single job thread, which is the default, the
|
|
lock should never actually wait in practice. The amount of time spent locking should be quite
|
|
short, but it's something to be aware of for those who have pedantic lock-free requirements and
|
|
need to use more than one job thread. There are plans to remove this lock in a future version.
|
|
</p>
|
|
<p>
|
|
|
|
In addition, posting a job will release a semaphore, which on Win32 is implemented with
|
|
<span style="font-family:monospace;">ReleaseSemaphore</span> and on POSIX platforms via a condition variable:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
pthread_mutex_lock(&pSemaphore->lock);
|
|
{
|
|
pSemaphore->value += 1;
|
|
pthread_cond_signal(&pSemaphore->cond);
|
|
}
|
|
pthread_mutex_unlock(&pSemaphore->lock);
|
|
</pre></div><p>
|
|
|
|
Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid
|
|
this, you can use non-blocking mode (via the <span style="font-family:monospace;">MA_JOB_QUEUE_FLAG_NON_BLOCKING</span>
|
|
flag) and implement your own job processing routine (see the "Resource Manager" section above for
|
|
details on how to do this).
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="DataBuffers" class="man">6.2.2. Data Buffers</h2>
|
|
<p>
|
|
When the <span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM</span> flag is excluded at initialization time, the
|
|
resource manager will try to load the data into an in-memory data buffer. Before doing so, however,
|
|
it will first check if the specified file is already loaded. If so, it will increment a reference
|
|
counter and just use the already loaded data. This saves both time and memory. When the data buffer
|
|
is uninitialized, the reference counter will be decremented. If the counter hits zero, the file
|
|
will be unloaded. This is a detail to keep in mind because it could result in excessive loading and
|
|
unloading of a sound. For example, the following sequence will result in a file be loaded twice,
|
|
once after the other:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_resource_manager_data_source_init(pResourceManager, <span style="color:#cc3300">"my_file"</span>, ..., &myDataBuffer0); <span style="color:#009900">// Refcount = 1. Initial load.</span>
|
|
ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); <span style="color:#009900">// Refcount = 0. Unloaded.</span>
|
|
|
|
ma_resource_manager_data_source_init(pResourceManager, <span style="color:#cc3300">"my_file"</span>, ..., &myDataBuffer1); <span style="color:#009900">// Refcount = 1. Reloaded because previous uninit() unloaded it.</span>
|
|
ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); <span style="color:#009900">// Refcount = 0. Unloaded.</span>
|
|
</pre></div><p>
|
|
|
|
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
|
|
into <span style="font-family:monospace;">ma_resource_manager_data_source_init()</span>. 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
|
|
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.
|
|
</p>
|
|
<p>
|
|
|
|
When a sound file has not already been loaded and the <span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC</span>
|
|
flag is excluded, the file will be decoded synchronously by the calling thread. There are two
|
|
options for controlling how the audio is stored in the data buffer - encoded or decoded. When the
|
|
<span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE</span> option is excluded, the raw file data will be stored
|
|
in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is
|
|
a very simple and standard process of simply adding an item to the BST, allocating a block of
|
|
memory and then decoding (if <span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE</span> is specified).
|
|
</p>
|
|
<p>
|
|
|
|
When the <span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC</span> flag is specified, loading of the data buffer
|
|
is done asynchronously. In this case, a job is posted to the queue to start loading and then the
|
|
function immediately returns, setting an internal result code to <span style="font-family:monospace;">MA_BUSY</span>. This result code is
|
|
returned when the program calls <span style="font-family:monospace;">ma_resource_manager_data_source_result()</span>. When decoding has fully
|
|
completed <span style="font-family:monospace;">MA_SUCCESS</span> will be returned. This can be used to know if loading has fully completed.
|
|
</p>
|
|
<p>
|
|
|
|
When loading asynchronously, a single job is posted to the queue of the type
|
|
<span style="font-family:monospace;">MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE</span>. This involves making a copy of the file path and
|
|
associating it with job. When the job is processed by the job thread, it will first load the file
|
|
using the VFS associated with the resource manager. When using a custom VFS, it's important that it
|
|
be completely thread-safe because it will be used from one or more job threads at the same time.
|
|
Individual files should only ever be accessed by one thread at a time, however. After opening the
|
|
file via the VFS, the job will determine whether or not the file is being decoded. If not, it
|
|
simply allocates a block of memory and loads the raw file contents into it and returns. On the
|
|
other hand, when the file is being decoded, it will first allocate a decoder on the heap and
|
|
initialize it. Then it will check if the length of the file is known. If so it will allocate a
|
|
block of memory to store the decoded output and initialize it to silence. If the size is unknown,
|
|
it will allocate room for one page. After memory has been allocated, the first page will be
|
|
decoded. If the sound is shorter than a page, the result code will be set to <span style="font-family:monospace;">MA_SUCCESS</span> and the
|
|
completion event will be signalled and loading is now complete. If, however, there is more to
|
|
decode, a job with the code <span style="font-family:monospace;">MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE</span> is posted. This job
|
|
will decode the next page and perform the same process if it reaches the end. If there is more to
|
|
decode, the job will post another <span style="font-family:monospace;">MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE</span> job which will
|
|
keep on happening until the sound has been fully decoded. For sounds of an unknown length, each
|
|
page will be linked together as a linked list. Internally this is implemented via the
|
|
<span style="font-family:monospace;">ma_paged_audio_buffer</span> object.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="DataStreams" class="man">6.2.3. Data Streams</h2>
|
|
<p>
|
|
Data streams only ever store two pages worth of data for each instance. They are most useful for
|
|
large sounds like music tracks in games that would consume too much memory if fully decoded in
|
|
memory. After every frame from a page has been read, a job will be posted to load the next page
|
|
which is done from the VFS.
|
|
</p>
|
|
<p>
|
|
|
|
For data streams, the <span style="font-family:monospace;">MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC</span> flag will determine whether or
|
|
not initialization of the data source waits until the two pages have been decoded. When unset,
|
|
<span style="font-family:monospace;">ma_resource_manager_data_source_init()</span> will wait until the two pages have been loaded, otherwise
|
|
it will return immediately.
|
|
</p>
|
|
<p>
|
|
|
|
When frames are read from a data stream using <span style="font-family:monospace;">ma_resource_manager_data_source_read_pcm_frames()</span>,
|
|
<span style="font-family:monospace;">MA_BUSY</span> will be returned if there are no frames available. If there are some frames available,
|
|
but less than the number requested, <span style="font-family:monospace;">MA_SUCCESS</span> will be returned, but the actual number of frames
|
|
read will be less than the number requested. Due to the asynchronous nature of data streams,
|
|
seeking is also asynchronous. If the data stream is in the middle of a seek, <span style="font-family:monospace;">MA_BUSY</span> will be
|
|
returned when trying to read frames.
|
|
</p>
|
|
<p>
|
|
|
|
When <span style="font-family:monospace;">ma_resource_manager_data_source_read_pcm_frames()</span> results in a page getting fully consumed
|
|
a job is posted to load the next page. This will be posted from the same thread that called
|
|
<span style="font-family:monospace;">ma_resource_manager_data_source_read_pcm_frames()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Data streams are uninitialized by posting a job to the queue, but the function won't return until
|
|
that job has been processed. The reason for this is that the caller owns the data stream object and
|
|
therefore miniaudio needs to ensure everything completes before handing back control to the caller.
|
|
Also, if the data stream is uninitialized while pages are in the middle of decoding, they must
|
|
complete before destroying any underlying object and the job system handles this cleanly.
|
|
</p>
|
|
<p>
|
|
|
|
Note that when a new page needs to be loaded, a job will be posted to the resource manager's job
|
|
thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue"
|
|
section above regarding locking when posting an event if you require a strictly lock-free audio
|
|
thread.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="NodeGraph" class="man">7. Node Graph</h1>
|
|
<p>
|
|
miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a
|
|
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,
|
|
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
|
|
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.
|
|
</p>
|
|
<p>
|
|
|
|
Each node has a number of input buses and a number of output buses. An output bus from a node is
|
|
attached to an input bus of another. Multiple nodes can connect their output buses to another
|
|
node's input bus, in which case their outputs will be mixed before processing by the node. Below is
|
|
a diagram that illustrates a hypothetical node graph setup:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
|
|
+---------------+ +-----------------+
|
|
| Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+
|
|
+---------------+ | | =----+ +-----------------+ | +----------+
|
|
+----= Splitter | +----= ENDPOINT |
|
|
+---------------+ | | =----+ +-----------------+ | +----------+
|
|
| Data Source 2 =----+ +----------+ +----= Echo / Delay =----+
|
|
+---------------+ +-----------------+
|
|
</pre></div><p>
|
|
|
|
In the above graph, it starts with two data sources whose outputs are attached to the input of a
|
|
splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter
|
|
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
|
|
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.
|
|
</p>
|
|
<p>
|
|
|
|
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
|
|
miniaudio will automatically convert the input data to the output channel count before processing.
|
|
The number of channels of an output bus of one node must match the channel count of the input bus
|
|
it's attached to. The channel counts cannot be changed after the node has been initialized. If you
|
|
attempt to attach an output bus to an input bus with a different channel count, attachment will
|
|
fail.
|
|
</p>
|
|
<p>
|
|
|
|
To use a node graph, you first need to initialize a <span style="font-family:monospace;">ma_node_graph</span> object. This is essentially a
|
|
container around the entire graph. The <span style="font-family:monospace;">ma_node_graph</span> object is required for some thread-safety
|
|
issues which will be explained later. A <span style="font-family:monospace;">ma_node_graph</span> object is initialized using miniaudio's
|
|
standard config/init system:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount);
|
|
|
|
result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); <span style="color:#009900">// Second parameter is a pointer to allocation callbacks.</span>
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Failed to initialize node graph.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
When you initialize the node graph, you're specifying the channel count of the endpoint. The
|
|
endpoint is a special node which has one input bus and one output bus, both of which have the
|
|
same channel count, which is specified in the config. Any nodes that connect directly to the
|
|
endpoint must be configured such that their output buses have the same channel count. When you read
|
|
audio data from the node graph, it'll have the channel count you specified in the config. To read
|
|
data from the graph:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_uint32</span> framesRead;
|
|
result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Failed to read data from the node graph.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
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
|
|
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
|
|
read from a <span style="font-family:monospace;">ma_data_source</span> object, but they will always have some kind of underlying object that
|
|
sources some kind of audio. The <span style="font-family:monospace;">ma_data_source_node</span> node can be used to read from a
|
|
<span style="font-family:monospace;">ma_data_source</span>. 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.
|
|
It's up to you to ensure they use a consistent and appropraite sample rate.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">ma_node</span> 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
|
|
node which reads directly from a data source (<span style="font-family:monospace;">ma_data_source_node</span>) which is an example of one
|
|
of the stock nodes that comes with miniaudio:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource);
|
|
|
|
ma_data_source_node dataSourceNode;
|
|
result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Failed to create data source node.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
The data source node will use the output channel count to determine the channel count of the output
|
|
bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data
|
|
source). The data source must output to floating-point (<span style="font-family:monospace;">ma_format_f32</span>) or else an error will be
|
|
returned from <span style="font-family:monospace;">ma_data_source_node_init()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
By default the node will not be attached to the graph. To do so, use <span style="font-family:monospace;">ma_node_attach_output_bus()</span>:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Failed to attach node.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
The code above connects the data source node directly to the endpoint. Since the data source node
|
|
has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single
|
|
input bus which means the input bus index will also always be 0.
|
|
</p>
|
|
<p>
|
|
|
|
To detach a specific output bus, use <span style="font-family:monospace;">ma_node_detach_output_bus()</span>. To detach all output buses, use
|
|
<span style="font-family:monospace;">ma_node_detach_all_output_buses()</span>. If you want to just move the output bus from one attachment to
|
|
another, you do not need to detach first. You can just call <span style="font-family:monospace;">ma_node_attach_output_bus()</span> and it'll
|
|
deal with it for you.
|
|
</p>
|
|
<p>
|
|
|
|
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
|
|
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:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">static</span> <span style="color:#0033ff">void</span> my_custom_node_process_pcm_frames(ma_node* pNode, <span style="color:#0033ff">const</span> <span style="color:#0033ff">float</span>** ppFramesIn, <span style="color:#0099cc">ma_uint32</span>* pFrameCountIn, <span style="color:#0033ff">float</span>** ppFramesOut, <span style="color:#0099cc">ma_uint32</span>* pFrameCountOut)
|
|
{
|
|
<span style="color:#009900">// Do some processing of ppFramesIn (one stream of audio data per input bus)</span>
|
|
<span style="color:#0033ff">const</span> <span style="color:#0033ff">float</span>* pFramesIn_0 = ppFramesIn[0]; <span style="color:#009900">// Input bus @ index 0.</span>
|
|
<span style="color:#0033ff">const</span> <span style="color:#0033ff">float</span>* pFramesIn_1 = ppFramesIn[1]; <span style="color:#009900">// Input bus @ index 1.</span>
|
|
<span style="color:#0033ff">float</span>* pFramesOut_0 = ppFramesOut[0]; <span style="color:#009900">// Output bus @ index 0.</span>
|
|
|
|
<span style="color:#009900">// Do some processing. On input, <span style="font-family:monospace;">pFrameCountIn</span> will be the number of input frames in each</span>
|
|
<span style="color:#009900">// buffer in <span style="font-family:monospace;">ppFramesIn</span> and <span style="font-family:monospace;">pFrameCountOut</span> will be the capacity of each of the buffers</span>
|
|
<span style="color:#009900">// in <span style="font-family:monospace;">ppFramesOut</span>. On output, <span style="font-family:monospace;">pFrameCountIn</span> should be set to the number of input frames</span>
|
|
<span style="color:#009900">// your node consumed and <span style="font-family:monospace;">pFrameCountOut</span> should be set the number of output frames that</span>
|
|
<span style="color:#009900">// were produced.</span>
|
|
<span style="color:#009900">//</span>
|
|
<span style="color:#009900">// You should process as many frames as you can. If your effect consumes input frames at the</span>
|
|
<span style="color:#009900">// same rate as output frames (always the case, unless you're doing resampling), you need</span>
|
|
<span style="color:#009900">// only look at <span style="font-family:monospace;">ppFramesOut</span> and process that exact number of frames. If you're doing</span>
|
|
<span style="color:#009900">// resampling, you'll need to be sure to set both <span style="font-family:monospace;">pFrameCountIn</span> and <span style="font-family:monospace;">pFrameCountOut</span></span>
|
|
<span style="color:#009900">// properly.</span>
|
|
}
|
|
|
|
<span style="color:#0033ff">static</span> ma_node_vtable my_custom_node_vtable =
|
|
{
|
|
my_custom_node_process_pcm_frames, <span style="color:#009900">// The function that will be called process your custom node. This is where you'd implement your effect processing.</span>
|
|
NULL, <span style="color:#009900">// Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.</span>
|
|
2, <span style="color:#009900">// 2 input buses.</span>
|
|
1, <span style="color:#009900">// 1 output bus.</span>
|
|
0 <span style="color:#009900">// Default flags.</span>
|
|
};
|
|
|
|
...
|
|
|
|
<span style="color:#009900">// Each bus needs to have a channel count specified. To do this you need to specify the channel</span>
|
|
<span style="color:#009900">// counts in an array and then pass that into the node config.</span>
|
|
<span style="color:#0099cc">ma_uint32</span> inputChannels[2]; <span style="color:#009900">// Equal in size to the number of input channels specified in the vtable.</span>
|
|
<span style="color:#0099cc">ma_uint32</span> outputChannels[1]; <span style="color:#009900">// Equal in size to the number of output channels specicied in the vtable.</span>
|
|
|
|
inputChannels[0] = channelsIn;
|
|
inputChannels[1] = channelsIn;
|
|
outputChannels[0] = channelsOut;
|
|
|
|
ma_node_config nodeConfig = ma_node_config_init();
|
|
nodeConfig.vtable = &my_custom_node_vtable;
|
|
nodeConfig.pInputChannels = inputChannels;
|
|
nodeConfig.pOutputChannels = outputChannels;
|
|
|
|
ma_node_base node;
|
|
result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Failed to initialize node.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
When initializing a custom node, as in the code above, you'll normally just place your vtable in
|
|
static space. The number of input and output buses are specified as part of the vtable. If you need
|
|
a variable number of buses on a per-node bases, the vtable should have the relevant bus count set
|
|
to <span style="font-family:monospace;">MA_NODE_BUS_COUNT_UNKNOWN</span>. In this case, the bus count should be set in the node config:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">static</span> ma_node_vtable my_custom_node_vtable =
|
|
{
|
|
my_custom_node_process_pcm_frames, <span style="color:#009900">// The function that will be called process your custom node. This is where you'd implement your effect processing.</span>
|
|
NULL, <span style="color:#009900">// Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.</span>
|
|
MA_NODE_BUS_COUNT_UNKNOWN, <span style="color:#009900">// The number of input buses is determined on a per-node basis.</span>
|
|
1, <span style="color:#009900">// 1 output bus.</span>
|
|
0 <span style="color:#009900">// Default flags.</span>
|
|
};
|
|
|
|
...
|
|
|
|
ma_node_config nodeConfig = ma_node_config_init();
|
|
nodeConfig.vtable = &my_custom_node_vtable;
|
|
nodeConfig.inputBusCount = myBusCount; <span style="color:#009900">// <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here.</span>
|
|
nodeConfig.pInputChannels = inputChannels; <span style="color:#009900">// <-- Make sure there are nodeConfig.inputBusCount elements in this array.</span>
|
|
nodeConfig.pOutputChannels = outputChannels; <span style="color:#009900">// <-- The vtable specifies 1 output bus, so there must be 1 element in this array.</span>
|
|
</pre></div><p>
|
|
|
|
In the above example it's important to never set the <span style="font-family:monospace;">inputBusCount</span> and <span style="font-family:monospace;">outputBusCount</span> members
|
|
to anything other than their defaults if the vtable specifies an explicit count. They can only be
|
|
set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count.
|
|
</p>
|
|
<p>
|
|
|
|
Most often you'll want to create a structure to encapsulate your node with some extra data. You
|
|
need to make sure the <span style="font-family:monospace;">ma_node_base</span> object is your first member of the structure:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">typedef</span> <span style="color:#0033ff">struct</span>
|
|
{
|
|
ma_node_base base; <span style="color:#009900">// <-- Make sure this is always the first member.</span>
|
|
<span style="color:#0033ff">float</span> someCustomData;
|
|
} my_custom_node;
|
|
</pre></div><p>
|
|
|
|
By doing this, your object will be compatible with all <span style="font-family:monospace;">ma_node</span> APIs and you can attach it to the
|
|
graph just like any other node.
|
|
</p>
|
|
<p>
|
|
|
|
In the custom processing callback (<span style="font-family:monospace;">my_custom_node_process_pcm_frames()</span> in the example above), the
|
|
number of channels for each bus is what was specified by the config when the node was initialized
|
|
with <span style="font-family:monospace;">ma_node_init()</span>. In addition, all attachments to each of the input buses will have been
|
|
pre-mixed by miniaudio. The config allows you to specify different channel counts for each
|
|
individual input and output bus. It's up to the effect to handle it appropriate, and if it can't,
|
|
return an error in it's initialization routine.
|
|
</p>
|
|
<p>
|
|
|
|
Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable
|
|
and include the following:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Flag Name</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NODE_FLAG_PASSTHROUGH
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Useful for nodes that do not do any kind of audio
|
|
processing, but are instead used for tracking
|
|
time, handling events, etc. Also used by the
|
|
internal endpoint node. It reads directly from
|
|
the input bus to the output bus. Nodes with this
|
|
flag must have exactly 1 input bus and 1 output
|
|
bus, and both buses must have the same channel
|
|
counts.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NODE_FLAG_CONTINUOUS_PROCESSING
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Causes the processing callback to be called even
|
|
when no data is available to be read from input
|
|
attachments. This is useful for effects like
|
|
echos where there will be a tail of audio data
|
|
that still needs to be processed even when the
|
|
original data sources have reached their ends.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NODE_FLAG_ALLOW_NULL_INPUT
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used in conjunction with
|
|
<span style="font-family:monospace;">MA_NODE_FLAG_CONTINUOUS_PROCESSING</span>. When this
|
|
is set, the <span style="font-family:monospace;">ppFramesIn</span> parameter of the
|
|
processing callback will be set to NULL when
|
|
there are no input frames are available. When
|
|
this is unset, silence will be posted to the
|
|
processing callback.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used to tell miniaudio that input and output
|
|
frames are processed at different rates. You
|
|
should set this for any nodes that perform
|
|
resampling.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_NODE_FLAG_SILENT_OUTPUT
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Used to tell miniaudio that a node produces only
|
|
silent output. This is useful for nodes where you
|
|
don't want the output to contribute to the final
|
|
mix. An example might be if you want split your
|
|
stream and have one branch be output to a file.
|
|
When using this flag, you should avoid writing to
|
|
the output buffer of the node's processing
|
|
callback because miniaudio will ignore it anyway.</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
|
|
If you need to make a copy of an audio stream for effect processing you can use a splitter node
|
|
called <span style="font-family:monospace;">ma_splitter_node</span>. This takes has 1 input bus and splits the stream into 2 output buses.
|
|
You can use it like this:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut);
|
|
|
|
ma_splitter_node splitterNode;
|
|
result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Failed to create node.</span>
|
|
}
|
|
|
|
<span style="color:#009900">// Attach your output buses to two different input buses (can be on two different nodes).</span>
|
|
ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); <span style="color:#009900">// Attach directly to the endpoint.</span>
|
|
ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); <span style="color:#009900">// Attach to input bus 0 of some effect node.</span>
|
|
</pre></div><p>
|
|
|
|
The volume of an output bus can be configured on a per-bus basis:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f);
|
|
ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f);
|
|
</pre></div><p>
|
|
|
|
In the code above we're using the splitter node from before and changing the volume of each of the
|
|
copied streams.
|
|
</p>
|
|
<p>
|
|
|
|
You can start and stop a node with the following:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_node_set_state(&splitterNode, ma_node_state_started); <span style="color:#009900">// The default state.</span>
|
|
ma_node_set_state(&splitterNode, ma_node_state_stopped);
|
|
</pre></div><p>
|
|
|
|
By default the node is in a started state, but since it won't be connected to anything won't
|
|
actually be invoked by the node graph until it's connected. When you stop a node, data will not be
|
|
read from any of it's input connections. You can use this property to stop a group of sounds
|
|
atomically.
|
|
</p>
|
|
<p>
|
|
|
|
You can configure the initial state of a node in it's config:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
nodeConfig.initialState = ma_node_state_stopped;
|
|
</pre></div><p>
|
|
|
|
Note that for the stock specialized nodes, all of their configs will have a <span style="font-family:monospace;">nodeConfig</span> member
|
|
which is the config to use with the base node. This is where the initial state can be configured
|
|
for specialized nodes:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped;
|
|
</pre></div><p>
|
|
|
|
When using a specialized node like <span style="font-family:monospace;">ma_data_source_node</span> or <span style="font-family:monospace;">ma_splitter_node</span>, be sure to not
|
|
modify the <span style="font-family:monospace;">vtable</span> member of the <span style="font-family:monospace;">nodeConfig</span> object.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Timing" class="man">7.1. Timing</h2>
|
|
<p>
|
|
The node graph supports starting and stopping nodes at scheduled times. This is especially useful
|
|
for data source nodes where you want to get the node set up, but only start playback at a specific
|
|
time. There are two clocks: local and global.
|
|
</p>
|
|
<p>
|
|
|
|
A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can
|
|
only be done based on the global clock because the local clock will not be running while the node
|
|
is stopped. The global clocks advances whenever <span style="font-family:monospace;">ma_node_graph_read_pcm_frames()</span> is called. On the
|
|
other hand, the local clock only advances when the node's processing callback is fired, and is
|
|
advanced based on the output frame count.
|
|
</p>
|
|
<p>
|
|
|
|
To retrieve the global time, use <span style="font-family:monospace;">ma_node_graph_get_time()</span>. The global time can be set with
|
|
<span style="font-family:monospace;">ma_node_graph_set_time()</span> which might be useful if you want to do seeking on a global timeline.
|
|
Getting and setting the local time is similar. Use <span style="font-family:monospace;">ma_node_get_time()</span> to retrieve the local time,
|
|
and <span style="font-family:monospace;">ma_node_set_time()</span> to set the local time. The global and local times will be advanced by the
|
|
audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these
|
|
outside of the node processing callbacks which are always run on the audio thread.
|
|
</p>
|
|
<p>
|
|
|
|
There is basic support for scheduling the starting and stopping of nodes. You can only schedule one
|
|
start and one stop at a time. This is mainly intended for putting nodes into a started or stopped
|
|
state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited
|
|
to the resolution of a call to <span style="font-family:monospace;">ma_node_graph_read_pcm_frames()</span> which would typically be in blocks
|
|
of several milliseconds. The following APIs can be used for scheduling node states:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_node_set_state_time()
|
|
ma_node_get_state_time()
|
|
</pre></div><p>
|
|
|
|
The time is absolute and must be based on the global clock. An example is below:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); <span style="color:#009900">// Delay starting to 1 second.</span>
|
|
ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); <span style="color:#009900">// Delay stopping to 5 seconds.</span>
|
|
</pre></div><p>
|
|
|
|
An example for changing the state using a relative time.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph));
|
|
ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph));
|
|
</pre></div><p>
|
|
|
|
Note that due to the nature of multi-threading the times may not be 100% exact. If this is an
|
|
issue, consider scheduling state changes from within a processing callback. An idea might be to
|
|
have some kind of passthrough trigger node that is used specifically for tracking time and handling
|
|
events.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="ThreadSafetyandLocking" class="man">7.2. Thread Safety and Locking</h2>
|
|
<p>
|
|
When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's
|
|
expected that <span style="font-family:monospace;">ma_node_graph_read_pcm_frames()</span> would be run on the audio thread, it does so
|
|
without the use of any locks. This section discusses the implementation used by miniaudio and goes
|
|
over some of the compromises employed by miniaudio to achieve this goal. Note that the current
|
|
implementation may not be ideal - feedback and critiques are most welcome.
|
|
</p>
|
|
<p>
|
|
|
|
The node graph API is not *entirely* lock-free. Only <span style="font-family:monospace;">ma_node_graph_read_pcm_frames()</span> is expected
|
|
to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the
|
|
implementation, but are crafted in a way such that such locking is not required when reading audio
|
|
data from the graph. Locking in these areas are achieved by means of spinlocks.
|
|
</p>
|
|
<p>
|
|
|
|
The main complication with keeping <span style="font-family:monospace;">ma_node_graph_read_pcm_frames()</span> lock-free stems from the fact
|
|
that a node can be uninitialized, and it's memory potentially freed, while in the middle of being
|
|
processed on the audio thread. There are times when the audio thread will be referencing a node,
|
|
which means the uninitialization process of a node needs to make sure it delays returning until the
|
|
audio thread is finished so that control is not handed back to the caller thereby giving them a
|
|
chance to free the node's memory.
|
|
</p>
|
|
<p>
|
|
|
|
When the audio thread is processing a node, it does so by reading from each of the output buses of
|
|
the node. In order for a node to process data for one of it's output buses, it needs to read from
|
|
each of it's input buses, and so on an so forth. It follows that once all output buses of a node
|
|
are detached, the node as a whole will be disconnected and no further processing will occur unless
|
|
it's output buses are reattached, which won't be happening when the node is being uninitialized.
|
|
By having <span style="font-family:monospace;">ma_node_detach_output_bus()</span> wait until the audio thread is finished with it, we can
|
|
simplify a few things, at the expense of making <span style="font-family:monospace;">ma_node_detach_output_bus()</span> a bit slower. By
|
|
doing this, the implementation of <span style="font-family:monospace;">ma_node_uninit()</span> becomes trivial - just detach all output
|
|
nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean
|
|
up.
|
|
</p>
|
|
<p>
|
|
|
|
With the above design, the worst-case scenario is <span style="font-family:monospace;">ma_node_detach_output_bus()</span> taking as long as
|
|
it takes to process the output bus being detached. This will happen if it's called at just the
|
|
wrong moment where the audio thread has just iterated it and has just started processing. The
|
|
caller of <span style="font-family:monospace;">ma_node_detach_output_bus()</span> will stall until the audio thread is finished, which
|
|
includes the cost of recursively processing it's inputs. This is the biggest compromise made with
|
|
the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes
|
|
earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching
|
|
higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass
|
|
detachments, detach starting from the lowest level nodes and work your way towards the final
|
|
endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not
|
|
running, detachment will be fast and detachment in any order will be the same. The reason nodes
|
|
need to wait for their input attachments to complete is due to the potential for desyncs between
|
|
data sources. If the node was to terminate processing mid way through processing it's inputs,
|
|
there's a chance that some of the underlying data sources will have been read, but then others not.
|
|
That will then result in a potential desynchronization when detaching and reattaching higher-level
|
|
nodes. A possible solution to this is to have an option when detaching to terminate processing
|
|
before processing all input attachments which should be fairly simple.
|
|
</p>
|
|
<p>
|
|
|
|
Another compromise, albeit less significant, is locking when attaching and detaching nodes. This
|
|
locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present
|
|
for each input bus and output bus. When an output bus is connected to an input bus, both the output
|
|
bus and input bus is locked. This locking is specifically for attaching and detaching across
|
|
different threads and does not affect <span style="font-family:monospace;">ma_node_graph_read_pcm_frames()</span> in any way. The locking and
|
|
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
|
|
node while iteration is occuring.
|
|
</p>
|
|
<p>
|
|
|
|
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
|
|
each item in the list is and output bus. We have some intentional (and convenient) restrictions on
|
|
what can done with the linked list in order to simplify the implementation. First of all, whenever
|
|
something needs to iterate over the list, it must do so in a forward direction. Backwards iteration
|
|
is not supported. Also, items can only be added to the start of the list.
|
|
</p>
|
|
<p>
|
|
|
|
The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer
|
|
to the next item in the list, and another to the previous item. A pointer to the previous item is
|
|
only required for fast detachment of the node - it is never used in iteration. This is an
|
|
important property because it means from the perspective of iteration, attaching and detaching of
|
|
an item can be done with a single atomic assignment. This is exploited by both the attachment and
|
|
detachment process. When attaching the node, the first thing that is done is the setting of the
|
|
local "next" and "previous" pointers of the node. After that, the item is "attached" to the list
|
|
by simply performing an atomic exchange with the head pointer. After that, the node is "attached"
|
|
to the list from the perspective of iteration. Even though the "previous" pointer of the next item
|
|
hasn't yet been set, from the perspective of iteration it's been attached because iteration will
|
|
only be happening in a forward direction which means the "previous" pointer won't actually ever get
|
|
used. The same general process applies to detachment. See <span style="font-family:monospace;">ma_node_attach_output_bus()</span> and
|
|
<span style="font-family:monospace;">ma_node_detach_output_bus()</span> for the implementation of this mechanism.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="Decoding" class="man">8. Decoding</h1>
|
|
<p>
|
|
The <span style="font-family:monospace;">ma_decoder</span> API is used for reading audio files. Decoders are completely decoupled from
|
|
devices and can be used independently. The following formats are supported:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Format</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Decoding Backend</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Built-In</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
WAV</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
dr_wav</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Yes</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MP3</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
dr_mp3</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Yes</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
FLAC</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
dr_flac</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Yes</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Vorbis</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
stb_vorbis</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
No</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
Vorbis is supported via stb_vorbis which can be enabled by including the header section before the
|
|
implementation of miniaudio, like the following:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#666666">#define</span> STB_VORBIS_HEADER_ONLY
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300">"extras/stb_vorbis.c"</span> <span style="color:#009900">// Enables Vorbis decoding.</span>
|
|
|
|
<span style="color:#666666">#define</span> MINIAUDIO_IMPLEMENTATION
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300">"miniaudio.h"</span>
|
|
|
|
<span style="color:#009900">// The stb_vorbis implementation must come after the implementation of miniaudio.</span>
|
|
<span style="color:#666666">#undef</span> STB_VORBIS_HEADER_ONLY
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300">"extras/stb_vorbis.c"</span>
|
|
</pre></div><p>
|
|
|
|
A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (<a href="https://github.com/mackron/miniaudio">https://github.com/mackron/miniaudio</a>).
|
|
</p>
|
|
<p>
|
|
|
|
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:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#666666">#define</span> MA_NO_WAV
|
|
<span style="color:#666666">#define</span> MA_NO_MP3
|
|
<span style="color:#666666">#define</span> MA_NO_FLAC
|
|
</pre></div><p>
|
|
|
|
Disabling built-in decoding libraries is useful if you use these libraries independantly of the
|
|
<span style="font-family:monospace;">ma_decoder</span> API.
|
|
</p>
|
|
<p>
|
|
|
|
A decoder can be initialized from a file with <span style="font-family:monospace;">ma_decoder_init_file()</span>, a block of memory with
|
|
<span style="font-family:monospace;">ma_decoder_init_memory()</span>, or from data delivered via callbacks with <span style="font-family:monospace;">ma_decoder_init()</span>. Here is
|
|
an example for loading a decoder from a file:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_decoder</span> decoder;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_decoder_init_file(<span style="color:#cc3300">"MySong.mp3"</span>, NULL, &decoder);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> false; <span style="color:#009900">// An error occurred.</span>
|
|
}
|
|
|
|
...
|
|
|
|
ma_decoder_uninit(&decoder);
|
|
</pre></div><p>
|
|
|
|
When initializing a decoder, you can optionally pass in a pointer to a <span style="font-family:monospace;">ma_decoder_config</span> object
|
|
(the <span style="font-family:monospace;">NULL</span> argument in the example above) which allows you to configure the output format, channel
|
|
count, sample rate and channel map:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_decoder_config</span> config = ma_decoder_config_init(ma_format_f32, 2, 48000);
|
|
</pre></div><p>
|
|
|
|
When passing in <span style="font-family:monospace;">NULL</span> for decoder config in <span style="font-family:monospace;">ma_decoder_init*()</span>, the output format will be the
|
|
same as that defined by the decoding backend.
|
|
</p>
|
|
<p>
|
|
|
|
Data is read from the decoder as PCM frames. This will output the number of PCM frames actually
|
|
read. If this is less than the requested number of PCM frames it means you've reached the end. The
|
|
return value will be <span style="font-family:monospace;">MA_AT_END</span> if no samples have been read and the end has been reached.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead);
|
|
<span style="color:#0033ff">if</span> (framesRead < framesToRead) {
|
|
<span style="color:#009900">// Reached the end.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
You can also seek to a specific frame like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> false; <span style="color:#009900">// An error occurred.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
If you want to loop back to the start, you can simply seek back to the first PCM frame:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_decoder_seek_to_pcm_frame(pDecoder, 0);
|
|
</pre></div><p>
|
|
|
|
When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding
|
|
backend. This can be unnecessarily inefficient if the type is already known. In this case you can
|
|
use <span style="font-family:monospace;">encodingFormat</span> variable in the device config to specify a specific encoding format you want
|
|
to decode:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
decoderConfig.encodingFormat = ma_encoding_format_wav;
|
|
</pre></div><p>
|
|
|
|
See the <span style="font-family:monospace;">ma_encoding_format</span> enum for possible encoding formats.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">ma_decoder_init_file()</span> API will try using the file extension to determine which decoding
|
|
backend to prefer.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="CustomDecoders" class="man">8.1. Custom Decoders</h2>
|
|
<p>
|
|
It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful
|
|
when you want to use the <span style="font-family:monospace;">ma_decoder</span> API, but need to support an encoding format that's not one of
|
|
the stock formats supported by miniaudio. This can be put to particularly good use when using the
|
|
<span style="font-family:monospace;">ma_engine</span> and/or <span style="font-family:monospace;">ma_resource_manager</span> APIs because they use <span style="font-family:monospace;">ma_decoder</span> internally. If, for
|
|
example, you wanted to support Opus, you can do so with a custom decoder (there if a reference
|
|
Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile).
|
|
</p>
|
|
<p>
|
|
|
|
A custom decoder must implement a data source. A vtable called <span style="font-family:monospace;">ma_decoding_backend_vtable</span> needs
|
|
to be implemented which is then passed into the decoder config:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_decoding_backend_vtable* pCustomBackendVTables[] =
|
|
{
|
|
&g_ma_decoding_backend_vtable_libvorbis,
|
|
&g_ma_decoding_backend_vtable_libopus
|
|
};
|
|
|
|
...
|
|
|
|
decoderConfig = ma_decoder_config_init_default();
|
|
decoderConfig.pCustomBackendUserData = NULL;
|
|
decoderConfig.ppCustomBackendVTables = pCustomBackendVTables;
|
|
decoderConfig.customBackendCount = <span style="color:#0033ff">sizeof</span>(pCustomBackendVTables) / <span style="color:#0033ff">sizeof</span>(pCustomBackendVTables[0]);
|
|
</pre></div><p>
|
|
|
|
The <span style="font-family:monospace;">ma_decoding_backend_vtable</span> vtable has the following functions:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
onInit
|
|
onInitFile
|
|
onInitFileW
|
|
onInitMemory
|
|
onUninit
|
|
</pre></div><p>
|
|
|
|
There are only two functions that must be implemented - <span style="font-family:monospace;">onInit</span> and <span style="font-family:monospace;">onUninit</span>. The other
|
|
functions can be implemented for a small optimization for loading from a file path or memory. If
|
|
these are not specified, miniaudio will deal with it for you via a generic implementation.
|
|
</p>
|
|
<p>
|
|
|
|
When you initialize a custom data source (by implementing the <span style="font-family:monospace;">onInit</span> function in the vtable) you
|
|
will need to output a pointer to a <span style="font-family:monospace;">ma_data_source</span> which implements your custom decoder. See the
|
|
section about data sources for details on how to implemen this. Alternatively, see the
|
|
"custom_decoders" example in the miniaudio repository.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">onInit</span> 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
|
|
decoding. When you call them, you will pass in the <span style="font-family:monospace;">pReadSeekTellUserData</span> pointer to the relevant
|
|
parameter.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">pConfig</span> parameter in <span style="font-family:monospace;">onInit</span> can be used to configure the backend if appropriate. It's only
|
|
used as a hint and can be ignored. However, if any of the properties are relevant to your decoder,
|
|
an optimal implementation will handle the relevant properties appropriately.
|
|
</p>
|
|
<p>
|
|
|
|
If memory allocation is required, it should be done so via the specified allocation callbacks if
|
|
possible (the <span style="font-family:monospace;">pAllocationCallbacks</span> parameter).
|
|
</p>
|
|
<p>
|
|
|
|
If an error occurs when initializing the decoder, you should leave <span style="font-family:monospace;">ppBackend</span> unset, or set to
|
|
NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned.
|
|
When multiple custom backends are specified, miniaudio will cycle through the vtables in the order
|
|
they're listed in the array that's passed into the decoder config so it's important that your
|
|
initialization routine is clean.
|
|
</p>
|
|
<p>
|
|
|
|
When a decoder is uninitialized, the <span style="font-family:monospace;">onUninit</span> callback will be fired which will give you an
|
|
opportunity to clean up and internal data.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="Encoding" class="man">9. Encoding</h1>
|
|
<p>
|
|
The <span style="font-family:monospace;">ma_encoding</span> 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.
|
|
This can be disabled by specifying the following option before the implementation of miniaudio:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#666666">#define</span> MA_NO_WAV
|
|
</pre></div><p>
|
|
|
|
An encoder can be initialized to write to a file with <span style="font-family:monospace;">ma_encoder_init_file()</span> or from data
|
|
delivered via callbacks with <span style="font-family:monospace;">ma_encoder_init()</span>. Below is an example for initializing an encoder
|
|
to output to a file.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_encoder_config</span> config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE);
|
|
<span style="color:#0099cc">ma_encoder</span> encoder;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_encoder_init_file(<span style="color:#cc3300">"my_file.wav"</span>, &config, &encoder);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error</span>
|
|
}
|
|
|
|
...
|
|
|
|
ma_encoder_uninit(&encoder);
|
|
</pre></div><p>
|
|
|
|
When initializing an encoder you must specify a config which is initialized with
|
|
<span style="font-family:monospace;">ma_encoder_config_init()</span>. Here you must specify the file type, the output sample format, output
|
|
channel count and output sample rate. The following file types are supported:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Enum</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_encoding_format_wav</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
WAV</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
If the format, channel count or sample rate is not supported by the output file type an error will
|
|
be returned. The encoder will not perform data conversion so you will need to convert it before
|
|
outputting any audio data. To output audio data, use <span style="font-family:monospace;">ma_encoder_write_pcm_frames()</span>, like in the
|
|
example below:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite);
|
|
</pre></div><p>
|
|
|
|
Encoders must be uninitialized with <span style="font-family:monospace;">ma_encoder_uninit()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="DataConversion" class="man">10. Data Conversion</h1>
|
|
<p>
|
|
A data conversion API is included with miniaudio which supports the majority of data conversion
|
|
requirements. This supports conversion between sample formats, channel counts (with channel
|
|
mapping) and sample rates.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="SampleFormatConversion" class="man">10.1. Sample Format Conversion</h2>
|
|
<p>
|
|
Conversion between sample formats is achieved with the <span style="font-family:monospace;">ma_pcm_*_to_*()</span>, <span style="font-family:monospace;">ma_pcm_convert()</span> and
|
|
<span style="font-family:monospace;">ma_convert_pcm_frames_format()</span> APIs. Use <span style="font-family:monospace;">ma_pcm_*_to_*()</span> to convert between two specific
|
|
formats. Use <span style="font-family:monospace;">ma_pcm_convert()</span> to convert based on a <span style="font-family:monospace;">ma_format</span> variable. Use
|
|
<span style="font-family:monospace;">ma_convert_pcm_frames_format()</span> to convert PCM frames where you want to specify the frame count
|
|
and channel count as a variable instead of the total sample count.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Dithering" class="man">10.1.1. Dithering</h2>
|
|
<p>
|
|
Dithering can be set using the ditherMode parameter.
|
|
</p>
|
|
<p>
|
|
|
|
The different dithering modes include the following, in order of efficiency:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Type</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Enum Token</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
None</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_dither_mode_none</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Rectangle</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_dither_mode_rectangle</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Triangle</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_dither_mode_triangle</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
Note that even if the dither mode is set to something other than <span style="font-family:monospace;">ma_dither_mode_none</span>, it will be
|
|
ignored for conversions where dithering is not needed. Dithering is available for the following
|
|
conversions:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
s16 -> u8
|
|
s24 -> u8
|
|
s32 -> u8
|
|
f32 -> u8
|
|
s24 -> s16
|
|
s32 -> s16
|
|
f32 -> s16
|
|
</pre></div><p>
|
|
|
|
Note that it is not an error to pass something other than ma_dither_mode_none for conversions where
|
|
dither is not used. It will just be ignored.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="ChannelConversion" class="man">10.2. Channel Conversion</h2>
|
|
<p>
|
|
Channel conversion is used for channel rearrangement and conversion from one channel count to
|
|
another. The <span style="font-family:monospace;">ma_channel_converter</span> API is used for channel conversion. Below is an example of
|
|
initializing a simple channel converter which converts from mono to stereo.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_channel_converter_config</span> config = ma_channel_converter_config_init(
|
|
ma_format, <span style="color:#009900">// Sample format</span>
|
|
1, <span style="color:#009900">// Input channels</span>
|
|
NULL, <span style="color:#009900">// Input channel map</span>
|
|
2, <span style="color:#009900">// Output channels</span>
|
|
NULL, <span style="color:#009900">// Output channel map</span>
|
|
ma_channel_mix_mode_default); <span style="color:#009900">// The mixing algorithm to use when combining channels.</span>
|
|
|
|
result = ma_channel_converter_init(&config, NULL, &converter);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
To perform the conversion simply call <span style="font-family:monospace;">ma_channel_converter_process_pcm_frames()</span> like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_result</span> result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM
|
|
frames.
|
|
</p>
|
|
<p>
|
|
|
|
Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="ChannelMapping" class="man">10.2.1. Channel Mapping</h2>
|
|
<p>
|
|
In addition to converting from one channel count to another, like the example above, the channel
|
|
converter can also be used to rearrange channels. When initializing the channel converter, you can
|
|
optionally pass in channel maps for both the input and output frames. If the channel counts are the
|
|
same, and each channel map contains the same channel positions with the exception that they're in
|
|
a different order, a simple shuffling of the channels will be performed. If, however, there is not
|
|
a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed
|
|
based on a mixing mode which is specified when initializing the <span style="font-family:monospace;">ma_channel_converter_config</span>
|
|
object.
|
|
</p>
|
|
<p>
|
|
|
|
When converting from mono to multi-channel, the mono channel is simply copied to each output
|
|
channel. When going the other way around, the audio of each output channel is simply averaged and
|
|
copied to the mono channel.
|
|
</p>
|
|
<p>
|
|
|
|
In more complicated cases blending is used. The <span style="font-family:monospace;">ma_channel_mix_mode_simple</span> mode will drop excess
|
|
channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th
|
|
channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and
|
|
4th channels.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">ma_channel_mix_mode_rectangle</span> mode uses spacial locality based on a rectangle to compute a
|
|
simple distribution between input and output. Imagine sitting in the middle of a room, with
|
|
speakers on the walls representing channel positions. The <span style="font-family:monospace;">MA_CHANNEL_FRONT_LEFT</span> position can be
|
|
thought of as being in the corner of the front and left walls.
|
|
</p>
|
|
<p>
|
|
|
|
Finally, the <span style="font-family:monospace;">ma_channel_mix_mode_custom_weights</span> mode can be used to use custom user-defined
|
|
weights. Custom weights can be passed in as the last parameter of
|
|
<span style="font-family:monospace;">ma_channel_converter_config_init()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Predefined channel maps can be retrieved with <span style="font-family:monospace;">ma_channel_map_init_standard()</span>. This takes a
|
|
<span style="font-family:monospace;">ma_standard_channel_map</span> enum as it's first parameter, which can be one of the following:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Name</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_standard_channel_map_default</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Default channel map used by miniaudio. See below.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_standard_channel_map_microsoft</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Channel map used by Microsoft's bitfield channel maps.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_standard_channel_map_alsa</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Default ALSA channel map.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_standard_channel_map_rfc3551</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
RFC 3551. Based on AIFF.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_standard_channel_map_flac</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
FLAC channel map.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_standard_channel_map_vorbis</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Vorbis channel map.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_standard_channel_map_sound4</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
FreeBSD's sound(4).</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_standard_channel_map_sndio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
sndio channel map. <a href="http://www.sndio.org/tips.html">http://www.sndio.org/tips.html</a>.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_standard_channel_map_webaudio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
<a href="https://webaudio.github.io/web-audio-api/#ChannelOrdering">https://webaudio.github.io/web-audio-api/#ChannelOrdering</a></p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
Below are the channel maps used by default in miniaudio (<span style="font-family:monospace;">ma_standard_channel_map_default</span>):
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Channel Count</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Mapping</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
1 (Mono)</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
0: MA_CHANNEL_MONO</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
2 (Stereo)
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
0: MA_CHANNEL_FRONT_LEFT <br>
|
|
1: MA_CHANNEL_FRONT_RIGHT</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
3
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
0: MA_CHANNEL_FRONT_LEFT <br>
|
|
1: MA_CHANNEL_FRONT_RIGHT <br>
|
|
2: MA_CHANNEL_FRONT_CENTER</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
4 (Surround)
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
0: MA_CHANNEL_FRONT_LEFT <br>
|
|
1: MA_CHANNEL_FRONT_RIGHT <br>
|
|
2: MA_CHANNEL_FRONT_CENTER <br>
|
|
3: MA_CHANNEL_BACK_CENTER</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
5
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
0: MA_CHANNEL_FRONT_LEFT <br>
|
|
1: MA_CHANNEL_FRONT_RIGHT <br>
|
|
2: MA_CHANNEL_FRONT_CENTER <br>
|
|
3: MA_CHANNEL_BACK_LEFT <br>
|
|
4: MA_CHANNEL_BACK_RIGHT</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
6 (5.1)
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
0: MA_CHANNEL_FRONT_LEFT <br>
|
|
1: MA_CHANNEL_FRONT_RIGHT <br>
|
|
2: MA_CHANNEL_FRONT_CENTER <br>
|
|
3: MA_CHANNEL_LFE <br>
|
|
4: MA_CHANNEL_SIDE_LEFT <br>
|
|
5: MA_CHANNEL_SIDE_RIGHT</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
7
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
0: MA_CHANNEL_FRONT_LEFT <br>
|
|
1: MA_CHANNEL_FRONT_RIGHT <br>
|
|
2: MA_CHANNEL_FRONT_CENTER <br>
|
|
3: MA_CHANNEL_LFE <br>
|
|
4: MA_CHANNEL_BACK_CENTER <br>
|
|
4: MA_CHANNEL_SIDE_LEFT <br>
|
|
5: MA_CHANNEL_SIDE_RIGHT</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
8 (7.1)
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
0: MA_CHANNEL_FRONT_LEFT <br>
|
|
1: MA_CHANNEL_FRONT_RIGHT <br>
|
|
2: MA_CHANNEL_FRONT_CENTER <br>
|
|
3: MA_CHANNEL_LFE <br>
|
|
4: MA_CHANNEL_BACK_LEFT <br>
|
|
5: MA_CHANNEL_BACK_RIGHT <br>
|
|
6: MA_CHANNEL_SIDE_LEFT <br>
|
|
7: MA_CHANNEL_SIDE_RIGHT</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Other
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
All channels set to 0. This
|
|
is equivalent to the same
|
|
mapping as the device.</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Resampling" class="man">10.3. Resampling</h2>
|
|
<p>
|
|
Resampling is achieved with the <span style="font-family:monospace;">ma_resampler</span> object. To create a resampler object, do something
|
|
like the following:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_resampler_config</span> config = ma_resampler_config_init(
|
|
ma_format_s16,
|
|
channels,
|
|
sampleRateIn,
|
|
sampleRateOut,
|
|
ma_resample_algorithm_linear);
|
|
|
|
<span style="color:#0099cc">ma_resampler</span> resampler;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_resampler_init(&config, &resampler);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// An error occurred...</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
Do the following to uninitialize the resampler:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_resampler_uninit(&resampler);
|
|
</pre></div><p>
|
|
|
|
The following example shows how data can be processed
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_uint64</span> frameCountIn = 1000;
|
|
<span style="color:#0099cc">ma_uint64</span> frameCountOut = 2000;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// An error occurred...</span>
|
|
}
|
|
|
|
<span style="color:#009900">// At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the</span>
|
|
<span style="color:#009900">// number of output frames written.</span>
|
|
</pre></div><p>
|
|
|
|
To initialize the resampler you first need to set up a config (<span style="font-family:monospace;">ma_resampler_config</span>) with
|
|
<span style="font-family:monospace;">ma_resampler_config_init()</span>. You need to specify the sample format you want to use, the number of
|
|
channels, the input and output sample rate, and the algorithm.
|
|
</p>
|
|
<p>
|
|
|
|
The sample format can be either <span style="font-family:monospace;">ma_format_s16</span> or <span style="font-family:monospace;">ma_format_f32</span>. If you need a different format
|
|
you will need to perform pre- and post-conversions yourself where necessary. Note that the format
|
|
is the same for both input and output. The format cannot be changed after initialization.
|
|
</p>
|
|
<p>
|
|
|
|
The resampler supports multiple channels and is always interleaved (both input and output). The
|
|
channel count cannot be changed after initialization.
|
|
</p>
|
|
<p>
|
|
|
|
The sample rates can be anything other than zero, and are always specified in hertz. They should be
|
|
set to something like 44100, etc. The sample rate is the only configuration property that can be
|
|
changed after initialization.
|
|
</p>
|
|
<p>
|
|
|
|
The miniaudio resampler has built-in support for the following algorithms:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Algorithm</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Enum Token</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Linear</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_resample_algorithm_linear</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Custom</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_resample_algorithm_custom</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
The algorithm cannot be changed after initialization.
|
|
</p>
|
|
<p>
|
|
|
|
Processing always happens on a per PCM frame basis and always assumes interleaved input and output.
|
|
De-interleaved processing is not supported. To process frames, use
|
|
<span style="font-family:monospace;">ma_resampler_process_pcm_frames()</span>. On input, this function takes the number of output frames you
|
|
can fit in the output buffer and the number of input frames contained in the input buffer. On
|
|
output these variables contain the number of output frames that were written to the output buffer
|
|
and the number of input frames that were consumed in the process. You can pass in NULL for the
|
|
input buffer in which case it will be treated as an infinitely large buffer of zeros. The output
|
|
buffer can also be NULL, in which case the processing will be treated as seek.
|
|
</p>
|
|
<p>
|
|
|
|
The sample rate can be changed dynamically on the fly. You can change this with explicit sample
|
|
rates with <span style="font-family:monospace;">ma_resampler_set_rate()</span> and also with a decimal ratio with
|
|
<span style="font-family:monospace;">ma_resampler_set_rate_ratio()</span>. The ratio is in/out.
|
|
</p>
|
|
<p>
|
|
|
|
Sometimes it's useful to know exactly how many input frames will be required to output a specific
|
|
number of frames. You can calculate this with <span style="font-family:monospace;">ma_resampler_get_required_input_frame_count()</span>.
|
|
Likewise, it's sometimes useful to know exactly how many frames would be output given a certain
|
|
number of input frames. You can do this with <span style="font-family:monospace;">ma_resampler_get_expected_output_frame_count()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Due to the nature of how resampling works, the resampler introduces some latency. This can be
|
|
retrieved in terms of both the input rate and the output rate with
|
|
<span style="font-family:monospace;">ma_resampler_get_input_latency()</span> and <span style="font-family:monospace;">ma_resampler_get_output_latency()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="ResamplingAlgorithms" class="man">10.3.1. Resampling Algorithms</h2>
|
|
<p>
|
|
The choice of resampling algorithm depends on your situation and requirements.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="LinearResampling" class="man">10.3.1.1. Linear Resampling</h2>
|
|
<p>
|
|
The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however,
|
|
some control over the quality of the linear resampler which may make it a suitable option depending
|
|
on your requirements.
|
|
</p>
|
|
<p>
|
|
|
|
The linear resampler performs low-pass filtering before or after downsampling or upsampling,
|
|
depending on the sample rates you're converting between. When decreasing the sample rate, the
|
|
low-pass filter will be applied before downsampling. When increasing the rate it will be performed
|
|
after upsampling. By default a fourth order low-pass filter will be applied. This can be configured
|
|
via the <span style="font-family:monospace;">lpfOrder</span> configuration variable. Setting this to 0 will disable filtering.
|
|
</p>
|
|
<p>
|
|
|
|
The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of
|
|
the input and output sample rates (Nyquist Frequency).
|
|
</p>
|
|
<p>
|
|
|
|
The API for the linear resampler is the same as the main resampler API, only it's called
|
|
<span style="font-family:monospace;">ma_linear_resampler</span>.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="CustomResamplers" class="man">10.3.2. Custom Resamplers</h2>
|
|
<p>
|
|
You can implement a custom resampler by using the <span style="font-family:monospace;">ma_resample_algorithm_custom</span> resampling
|
|
algorithm and setting a vtable in the resampler config:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_resampler_config</span> config = ma_resampler_config_init(..., ma_resample_algorithm_custom);
|
|
config.pBackendVTable = &g_customResamplerVTable;
|
|
</pre></div><p>
|
|
|
|
Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You
|
|
need to implement the required functions in <span style="font-family:monospace;">ma_resampling_backend_vtable</span>. Note that not all
|
|
functions in the vtable need to be implemented, but if it's possible to implement, they should be.
|
|
</p>
|
|
<p>
|
|
|
|
You can use the <span style="font-family:monospace;">ma_linear_resampler</span> object for an example on how to implement the vtable. The
|
|
<span style="font-family:monospace;">onGetHeapSize</span> callback is used to calculate the size of any internal heap allocation the custom
|
|
resampler will need to make given the supplied config. When you initialize the resampler via the
|
|
<span style="font-family:monospace;">onInit</span> callback, you'll be given a pointer to a heap allocation which is where you should store
|
|
the heap allocated data. You should not free this data in <span style="font-family:monospace;">onUninit</span> because miniaudio will manage
|
|
it for you.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">onProcess</span> callback is where the actual resampling takes place. On input, <span style="font-family:monospace;">pFrameCountIn</span>
|
|
points to a variable containing the number of frames in the <span style="font-family:monospace;">pFramesIn</span> buffer and
|
|
<span style="font-family:monospace;">pFrameCountOut</span> points to a variable containing the capacity in frames of the <span style="font-family:monospace;">pFramesOut</span> buffer.
|
|
On output, <span style="font-family:monospace;">pFrameCountIn</span> should be set to the number of input frames that were fully consumed,
|
|
whereas <span style="font-family:monospace;">pFrameCountOut</span> should be set to the number of frames that were written to <span style="font-family:monospace;">pFramesOut</span>.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">onSetRate</span> callback is optional and is used for dynamically changing the sample rate. If
|
|
dynamic rate changes are not supported, you can set this callback to NULL.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">onGetInputLatency</span> and <span style="font-family:monospace;">onGetOutputLatency</span> functions are used for retrieving the latency in
|
|
input and output rates respectively. These can be NULL in which case latency calculations will be
|
|
assumed to be NULL.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">onGetRequiredInputFrameCount</span> callback is used to give miniaudio a hint as to how many input
|
|
frames are required to be available to produce the given number of output frames. Likewise, the
|
|
<span style="font-family:monospace;">onGetExpectedOutputFrameCount</span> callback is used to determine how many output frames will be
|
|
produced given the specified number of input frames. miniaudio will use these as a hint, but they
|
|
are optional and can be set to NULL if you're unable to implement them.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="GeneralDataConversion" class="man">10.4. General Data Conversion</h2>
|
|
<p>
|
|
The <span style="font-family:monospace;">ma_data_converter</span> API can be used to wrap sample format conversion, channel conversion and
|
|
resampling into one operation. This is what miniaudio uses internally to convert between the format
|
|
requested when the device was initialized and the format of the backend's native device. The API
|
|
for general data conversion is very similar to the resampling API. Create a <span style="font-family:monospace;">ma_data_converter</span>
|
|
object like this:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_data_converter_config</span> config = ma_data_converter_config_init(
|
|
inputFormat,
|
|
outputFormat,
|
|
inputChannels,
|
|
outputChannels,
|
|
inputSampleRate,
|
|
outputSampleRate
|
|
);
|
|
|
|
<span style="color:#0099cc">ma_data_converter</span> converter;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_data_converter_init(&config, NULL, &converter);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// An error occurred...</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
In the example above we use <span style="font-family:monospace;">ma_data_converter_config_init()</span> to initialize the config, however
|
|
there's many more properties that can be configured, such as channel maps and resampling quality.
|
|
Something like the following may be more suitable depending on your requirements:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_data_converter_config</span> config = ma_data_converter_config_init_default();
|
|
config.formatIn = inputFormat;
|
|
config.formatOut = outputFormat;
|
|
config.channelsIn = inputChannels;
|
|
config.channelsOut = outputChannels;
|
|
config.sampleRateIn = inputSampleRate;
|
|
config.sampleRateOut = outputSampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, <span style="color:#0033ff">sizeof</span>(config.channelMapIn)/<span style="color:#0033ff">sizeof</span>(config.channelMapIn[0]), config.channelCountIn);
|
|
config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER;
|
|
</pre></div><p>
|
|
|
|
Do the following to uninitialize the data converter:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_data_converter_uninit(&converter, NULL);
|
|
</pre></div><p>
|
|
|
|
The following example shows how data can be processed
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_uint64</span> frameCountIn = 1000;
|
|
<span style="color:#0099cc">ma_uint64</span> frameCountOut = 2000;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// An error occurred...</span>
|
|
}
|
|
|
|
<span style="color:#009900">// At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number</span>
|
|
<span style="color:#009900">// of output frames written.</span>
|
|
</pre></div><p>
|
|
|
|
The data converter supports multiple channels and is always interleaved (both input and output).
|
|
The channel count cannot be changed after initialization.
|
|
</p>
|
|
<p>
|
|
|
|
Sample rates can be anything other than zero, and are always specified in hertz. They should be set
|
|
to something like 44100, etc. The sample rate is the only configuration property that can be
|
|
changed after initialization, but only if the <span style="font-family:monospace;">resampling.allowDynamicSampleRate</span> member of
|
|
<span style="font-family:monospace;">ma_data_converter_config</span> is set to <span style="font-family:monospace;">MA_TRUE</span>. To change the sample rate, use
|
|
<span style="font-family:monospace;">ma_data_converter_set_rate()</span> or <span style="font-family:monospace;">ma_data_converter_set_rate_ratio()</span>. The ratio must be in/out.
|
|
The resampling algorithm cannot be changed after initialization.
|
|
</p>
|
|
<p>
|
|
|
|
Processing always happens on a per PCM frame basis and always assumes interleaved input and output.
|
|
De-interleaved processing is not supported. To process frames, use
|
|
<span style="font-family:monospace;">ma_data_converter_process_pcm_frames()</span>. On input, this function takes the number of output frames
|
|
you can fit in the output buffer and the number of input frames contained in the input buffer. On
|
|
output these variables contain the number of output frames that were written to the output buffer
|
|
and the number of input frames that were consumed in the process. You can pass in NULL for the
|
|
input buffer in which case it will be treated as an infinitely large
|
|
buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated
|
|
as seek.
|
|
</p>
|
|
<p>
|
|
|
|
Sometimes it's useful to know exactly how many input frames will be required to output a specific
|
|
number of frames. You can calculate this with <span style="font-family:monospace;">ma_data_converter_get_required_input_frame_count()</span>.
|
|
Likewise, it's sometimes useful to know exactly how many frames would be output given a certain
|
|
number of input frames. You can do this with <span style="font-family:monospace;">ma_data_converter_get_expected_output_frame_count()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Due to the nature of how resampling works, the data converter introduces some latency if resampling
|
|
is required. This can be retrieved in terms of both the input rate and the output rate with
|
|
<span style="font-family:monospace;">ma_data_converter_get_input_latency()</span> and <span style="font-family:monospace;">ma_data_converter_get_output_latency()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="Filtering" class="man">11. Filtering</h1>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="BiquadFiltering" class="man">11.1. Biquad Filtering</h2>
|
|
<p>
|
|
Biquad filtering is achieved with the <span style="font-family:monospace;">ma_biquad</span> API. Example:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_biquad_config</span> config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
|
|
<span style="color:#0099cc">ma_result</span> result = ma_biquad_init(&config, &biquad);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
|
|
...
|
|
|
|
ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount);
|
|
</pre></div><p>
|
|
|
|
Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0,
|
|
b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and
|
|
coefficients must not be pre-normalized.
|
|
</p>
|
|
<p>
|
|
|
|
Supported formats are <span style="font-family:monospace;">ma_format_s16</span> and <span style="font-family:monospace;">ma_format_f32</span>. If you need to use a different format
|
|
you need to convert it yourself beforehand. When using <span style="font-family:monospace;">ma_format_s16</span> the biquad filter will use
|
|
fixed point arithmetic. When using <span style="font-family:monospace;">ma_format_f32</span>, floating point arithmetic will be used.
|
|
</p>
|
|
<p>
|
|
|
|
Input and output frames are always interleaved.
|
|
</p>
|
|
<p>
|
|
|
|
Filtering can be applied in-place by passing in the same pointer for both the input and output
|
|
buffers, like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount);
|
|
</pre></div><p>
|
|
|
|
If you need to change the values of the coefficients, but maintain the values in the registers you
|
|
can do so with <span style="font-family:monospace;">ma_biquad_reinit()</span>. This is useful if you need to change the properties of the
|
|
filter while keeping the values of registers valid to avoid glitching. Do not use
|
|
<span style="font-family:monospace;">ma_biquad_init()</span> for this as it will do a full initialization which involves clearing the
|
|
registers to 0. Note that changing the format or channel count after initialization is invalid and
|
|
will result in an error.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Low-PassFiltering" class="man">11.2. Low-Pass Filtering</h2>
|
|
<p>
|
|
Low-pass filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
API</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_lpf1</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
First order low-pass filter</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_lpf2</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Second order low-pass filter</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_lpf</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
High order low-pass filter (Butterworth)</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
Low-pass filter example:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_lpf_config</span> config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
|
|
<span style="color:#0099cc">ma_result</span> result = ma_lpf_init(&config, &lpf);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
|
|
...
|
|
|
|
ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount);
|
|
</pre></div><p>
|
|
|
|
Supported formats are <span style="font-family:monospace;">ma_format_s16</span> and<span style="font-family:monospace;"> ma_format_f32</span>. If you need to use a different format
|
|
you need to convert it yourself beforehand. Input and output frames are always interleaved.
|
|
</p>
|
|
<p>
|
|
|
|
Filtering can be applied in-place by passing in the same pointer for both the input and output
|
|
buffers, like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
|
|
</pre></div><p>
|
|
|
|
The maximum filter order is limited to <span style="font-family:monospace;">MA_MAX_FILTER_ORDER</span> which is set to 8. If you need more,
|
|
you can chain first and second order filters together.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">for</span> (iFilter = 0; iFilter < filterCount; iFilter += 1) {
|
|
ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount);
|
|
}
|
|
</pre></div><p>
|
|
|
|
If you need to change the configuration of the filter, but need to maintain the state of internal
|
|
registers you can do so with <span style="font-family:monospace;">ma_lpf_reinit()</span>. 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
|
|
format or channel count after initialization is invalid and will result in an error.
|
|
</p>
|
|
<p>
|
|
|
|
The <span style="font-family:monospace;">ma_lpf</span> object supports a configurable order, but if you only need a first order filter you
|
|
may want to consider using <span style="font-family:monospace;">ma_lpf1</span>. Likewise, if you only need a second order filter you can use
|
|
<span style="font-family:monospace;">ma_lpf2</span>. The advantage of this is that they're lighter weight and a bit more efficient.
|
|
</p>
|
|
<p>
|
|
|
|
If an even filter order is specified, a series of second order filters will be processed in a
|
|
chain. If an odd filter order is specified, a first order filter will be applied, followed by a
|
|
series of second order filters in a chain.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="High-PassFiltering" class="man">11.3. High-Pass Filtering</h2>
|
|
<p>
|
|
High-pass filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
API</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_hpf1</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
First order high-pass filter</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_hpf2</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Second order high-pass filter</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_hpf</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
High order high-pass filter (Butterworth)</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
High-pass filters work exactly the same as low-pass filters, only the APIs are called <span style="font-family:monospace;">ma_hpf1</span>,
|
|
<span style="font-family:monospace;">ma_hpf2</span> and <span style="font-family:monospace;">ma_hpf</span>. See example code for low-pass filters for example usage.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Band-PassFiltering" class="man">11.4. Band-Pass Filtering</h2>
|
|
<p>
|
|
Band-pass filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
API</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_bpf2</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Second order band-pass filter</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_bpf</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
High order band-pass filter</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
Band-pass filters work exactly the same as low-pass filters, only the APIs are called <span style="font-family:monospace;">ma_bpf2</span> and
|
|
<span style="font-family:monospace;">ma_hpf</span>. See example code for low-pass filters for example usage. Note that the order for
|
|
band-pass filters must be an even number which means there is no first order band-pass filter,
|
|
unlike low-pass and high-pass filters.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="NotchFiltering" class="man">11.5. Notch Filtering</h2>
|
|
<p>
|
|
Notch filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
API</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_notch2</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Second order notching filter</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
|
|
</p>
|
|
<h2 id="PeakingEQFiltering" class="man">11.6. Peaking EQ Filtering</h2>
|
|
<p>
|
|
Peaking filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
API</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_peak2</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Second order peaking filter</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
|
|
</p>
|
|
<h2 id="LowShelfFiltering" class="man">11.7. Low Shelf Filtering</h2>
|
|
<p>
|
|
Low shelf filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
API</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_loshelf2</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Second order low shelf filter</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to
|
|
just turn them down rather than eliminate them entirely.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="HighShelfFiltering" class="man">11.8. High Shelf Filtering</h2>
|
|
<p>
|
|
High shelf filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
API</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Description</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_hishelf2</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Second order high shelf filter</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
The high shelf filter has the same API as the low shelf filter, only you would use <span style="font-family:monospace;">ma_hishelf</span>
|
|
instead of <span style="font-family:monospace;">ma_loshelf</span>. Where a low shelf filter is used to adjust the volume of low frequencies,
|
|
the high shelf filter does the same thing for high frequencies.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="WaveformandNoiseGeneration" class="man">12. Waveform and Noise Generation</h1>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Waveforms" class="man">12.1. Waveforms</h2>
|
|
<p>
|
|
miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved
|
|
with the <span style="font-family:monospace;">ma_waveform</span> API. Example:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_waveform_config</span> config = ma_waveform_config_init(
|
|
FORMAT,
|
|
CHANNELS,
|
|
SAMPLE_RATE,
|
|
ma_waveform_type_sine,
|
|
amplitude,
|
|
frequency);
|
|
|
|
<span style="color:#0099cc">ma_waveform</span> waveform;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_waveform_init(&config, &waveform);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
|
|
...
|
|
|
|
ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount);
|
|
</pre></div><p>
|
|
|
|
The amplitude, frequency, type, and sample rate can be changed dynamically with
|
|
<span style="font-family:monospace;">ma_waveform_set_amplitude()</span>, <span style="font-family:monospace;">ma_waveform_set_frequency()</span>, <span style="font-family:monospace;">ma_waveform_set_type()</span>, and
|
|
<span style="font-family:monospace;">ma_waveform_set_sample_rate()</span> respectively.
|
|
</p>
|
|
<p>
|
|
|
|
You can invert the waveform by setting the amplitude to a negative value. You can use this to
|
|
control whether or not a sawtooth has a positive or negative ramp, for example.
|
|
</p>
|
|
<p>
|
|
|
|
Below are the supported waveform types:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Enum Name</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_waveform_type_sine</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_waveform_type_square</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_waveform_type_triangle</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_waveform_type_sawtooth</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Noise" class="man">12.2. Noise</h2>
|
|
<p>
|
|
miniaudio supports generation of white, pink and Brownian noise via the <span style="font-family:monospace;">ma_noise</span> API. Example:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_noise_config</span> config = ma_noise_config_init(
|
|
FORMAT,
|
|
CHANNELS,
|
|
ma_noise_type_white,
|
|
SEED,
|
|
amplitude);
|
|
|
|
<span style="color:#0099cc">ma_noise</span> noise;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_noise_init(&config, &noise);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
|
|
...
|
|
|
|
ma_noise_read_pcm_frames(&noise, pOutput, frameCount);
|
|
</pre></div><p>
|
|
|
|
The noise API uses simple LCG random number generation. It supports a custom seed which is useful
|
|
for things like automated testing requiring reproducibility. Setting the seed to zero will default
|
|
to <span style="font-family:monospace;">MA_DEFAULT_LCG_SEED</span>.
|
|
</p>
|
|
<p>
|
|
|
|
The amplitude, seed, and type can be changed dynamically with <span style="font-family:monospace;">ma_noise_set_amplitude()</span>,
|
|
<span style="font-family:monospace;">ma_noise_set_seed()</span>, and <span style="font-family:monospace;">ma_noise_set_type()</span> respectively.
|
|
</p>
|
|
<p>
|
|
|
|
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
|
|
the same random value, set the <span style="font-family:monospace;">duplicateChannels</span> member of the noise config to true, like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
config.duplicateChannels = MA_TRUE;
|
|
</pre></div><p>
|
|
|
|
Below are the supported noise types.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Enum Name</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_noise_type_white</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_noise_type_pink</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ma_noise_type_brownian</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="AudioBuffers" class="man">13. Audio Buffers</h1>
|
|
<p>
|
|
miniaudio supports reading from a buffer of raw audio data via the <span style="font-family:monospace;">ma_audio_buffer</span> API. This can
|
|
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.
|
|
</p>
|
|
<p>
|
|
|
|
Audio buffers are initialised using the standard configuration system used everywhere in miniaudio:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_audio_buffer_config</span> config = ma_audio_buffer_config_init(
|
|
format,
|
|
channels,
|
|
sizeInFrames,
|
|
pExistingData,
|
|
&allocationCallbacks);
|
|
|
|
<span style="color:#0099cc">ma_audio_buffer</span> buffer;
|
|
result = ma_audio_buffer_init(&config, &buffer);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error.</span>
|
|
}
|
|
|
|
...
|
|
|
|
ma_audio_buffer_uninit(&buffer);
|
|
</pre></div><p>
|
|
|
|
In the example above, the memory pointed to by <span style="font-family:monospace;">pExistingData</span> will *not* be copied and is how an
|
|
application can do self-managed memory allocation. If you would rather make a copy of the data, use
|
|
<span style="font-family:monospace;">ma_audio_buffer_init_copy()</span>. To uninitialize the buffer, use <span style="font-family:monospace;">ma_audio_buffer_uninit()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Sometimes it can be convenient to allocate the memory for the <span style="font-family:monospace;">ma_audio_buffer</span> structure and the
|
|
raw audio data in a contiguous block of memory. That is, the raw audio data will be located
|
|
immediately after the <span style="font-family:monospace;">ma_audio_buffer</span> structure. To do this, use
|
|
<span style="font-family:monospace;">ma_audio_buffer_alloc_and_init()</span>:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_audio_buffer_config</span> config = ma_audio_buffer_config_init(
|
|
format,
|
|
channels,
|
|
sizeInFrames,
|
|
pExistingData,
|
|
&allocationCallbacks);
|
|
|
|
<span style="color:#0099cc">ma_audio_buffer</span>* pBuffer
|
|
result = ma_audio_buffer_alloc_and_init(&config, &pBuffer);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error</span>
|
|
}
|
|
|
|
...
|
|
|
|
ma_audio_buffer_uninit_and_free(&buffer);
|
|
</pre></div><p>
|
|
|
|
If you initialize the buffer with <span style="font-family:monospace;">ma_audio_buffer_alloc_and_init()</span> you should uninitialize it
|
|
with <span style="font-family:monospace;">ma_audio_buffer_uninit_and_free()</span>. In the example above, the memory pointed to by
|
|
<span style="font-family:monospace;">pExistingData</span> will be copied into the buffer, which is contrary to the behavior of
|
|
<span style="font-family:monospace;">ma_audio_buffer_init()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the
|
|
cursor moves forward. The last parameter (<span style="font-family:monospace;">loop</span>) can be used to determine if the buffer should
|
|
loop. The return value is the number of frames actually read. If this is less than the number of
|
|
frames requested it means the end has been reached. This should never happen if the <span style="font-family:monospace;">loop</span>
|
|
parameter is set to true. If you want to manually loop back to the start, you can do so with with
|
|
<span style="font-family:monospace;">ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)</span>. Below is an example for reading data from an
|
|
audio buffer.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_uint64</span> framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping);
|
|
<span style="color:#0033ff">if</span> (framesRead < desiredFrameCount) {
|
|
<span style="color:#009900">// If not looping, this means the end has been reached. This should never happen in looping mode with valid input.</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
Sometimes you may want to avoid the cost of data movement between the internal buffer and the
|
|
output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0033ff">void</span>* pMappedFrames;
|
|
<span style="color:#0099cc">ma_uint64</span> frameCount = frameCountToTryMapping;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount);
|
|
<span style="color:#0033ff">if</span> (result == MA_SUCCESS) {
|
|
<span style="color:#009900">// Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be</span>
|
|
<span style="color:#009900">// less due to the end of the buffer being reached.</span>
|
|
ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels);
|
|
|
|
<span style="color:#009900">// You must unmap the buffer.</span>
|
|
ma_audio_buffer_unmap(pAudioBuffer, frameCount);
|
|
}
|
|
</pre></div><p>
|
|
|
|
When you use memory mapping, the read cursor is increment by the frame count passed in to
|
|
<span style="font-family:monospace;">ma_audio_buffer_unmap()</span>. If you decide not to process every frame you can pass in a value smaller
|
|
than the value returned by <span style="font-family:monospace;">ma_audio_buffer_map()</span>. The disadvantage to using memory mapping is
|
|
that it does not handle looping for you. You can determine if the buffer is at the end for the
|
|
purpose of looping with <span style="font-family:monospace;">ma_audio_buffer_at_end()</span> or by inspecting the return value of
|
|
<span style="font-family:monospace;">ma_audio_buffer_unmap()</span> and checking if it equals <span style="font-family:monospace;">MA_AT_END</span>. You should not treat <span style="font-family:monospace;">MA_AT_END</span>
|
|
as an error when returned by <span style="font-family:monospace;">ma_audio_buffer_unmap()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="RingBuffers" class="man">14. Ring Buffers</h1>
|
|
<p>
|
|
miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via
|
|
the <span style="font-family:monospace;">ma_rb</span> and <span style="font-family:monospace;">ma_pcm_rb</span> APIs. The <span style="font-family:monospace;">ma_rb</span> API operates on bytes, whereas the <span style="font-family:monospace;">ma_pcm_rb</span>
|
|
operates on PCM frames. They are otherwise identical as <span style="font-family:monospace;">ma_pcm_rb</span> is just a wrapper around
|
|
<span style="font-family:monospace;">ma_rb</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved
|
|
streams. The caller can also allocate their own backing memory for the ring buffer to use
|
|
internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for
|
|
you.
|
|
</p>
|
|
<p>
|
|
|
|
The examples below use the PCM frame variant of the ring buffer since that's most likely the one
|
|
you will want to use. To initialize a ring buffer, do something like the following:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<span style="color:#0099cc">ma_pcm_rb</span> rb;
|
|
<span style="color:#0099cc">ma_result</span> result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#009900">// Error</span>
|
|
}
|
|
</pre></div><p>
|
|
|
|
The <span style="font-family:monospace;">ma_pcm_rb_init()</span> 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
|
|
would call <span style="font-family:monospace;">ma_rb_init()</span> 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
|
|
is a pointer to a <span style="font-family:monospace;">ma_allocation_callbacks</span> structure for custom memory allocation routines.
|
|
Passing in <span style="font-family:monospace;">NULL</span> for this results in <span style="font-family:monospace;">MA_MALLOC()</span> and <span style="font-family:monospace;">MA_FREE()</span> being used.
|
|
</p>
|
|
<p>
|
|
|
|
Use <span style="font-family:monospace;">ma_pcm_rb_init_ex()</span> if you need a deinterleaved buffer. The data for each sub-buffer is
|
|
offset from each other based on the stride. To manage your sub-buffers you can use
|
|
<span style="font-family:monospace;">ma_pcm_rb_get_subbuffer_stride()</span>, <span style="font-family:monospace;">ma_pcm_rb_get_subbuffer_offset()</span> and
|
|
<span style="font-family:monospace;">ma_pcm_rb_get_subbuffer_ptr()</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Use <span style="font-family:monospace;">ma_pcm_rb_acquire_read()</span> and <span style="font-family:monospace;">ma_pcm_rb_acquire_write()</span> to retrieve a pointer to a section
|
|
of the ring buffer. You specify the number of frames you need, and on output it will set to what
|
|
was actually acquired. If the read or write pointer is positioned such that the number of frames
|
|
requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number
|
|
of frames you're given may be less than the number you requested.
|
|
</p>
|
|
<p>
|
|
|
|
After calling <span style="font-family:monospace;">ma_pcm_rb_acquire_read()</span> or <span style="font-family:monospace;">ma_pcm_rb_acquire_write()</span>, you do your work on the
|
|
buffer and then "commit" it with <span style="font-family:monospace;">ma_pcm_rb_commit_read()</span> or <span style="font-family:monospace;">ma_pcm_rb_commit_write()</span>. This is
|
|
where the read/write pointers are updated. When you commit you need to pass in the buffer that was
|
|
returned by the earlier call to <span style="font-family:monospace;">ma_pcm_rb_acquire_read()</span> or <span style="font-family:monospace;">ma_pcm_rb_acquire_write()</span> and is
|
|
only used for validation. The number of frames passed to <span style="font-family:monospace;">ma_pcm_rb_commit_read()</span> and
|
|
<span style="font-family:monospace;">ma_pcm_rb_commit_write()</span> is what's used to increment the pointers, and can be less that what was
|
|
originally requested.
|
|
</p>
|
|
<p>
|
|
|
|
If you want to correct for drift between the write pointer and the read pointer you can use a
|
|
combination of <span style="font-family:monospace;">ma_pcm_rb_pointer_distance()</span>, <span style="font-family:monospace;">ma_pcm_rb_seek_read()</span> and
|
|
<span style="font-family:monospace;">ma_pcm_rb_seek_write()</span>. Note that you can only move the pointers forward, and you should only
|
|
move the read pointer forward via the consumer thread, and the write pointer forward by the
|
|
producer thread. If there is too much space between the pointers, move the read pointer forward. If
|
|
there is too little space between the pointers, move the write pointer forward.
|
|
</p>
|
|
<p>
|
|
|
|
You can use a ring buffer at the byte level instead of the PCM frame level by using the <span style="font-family:monospace;">ma_rb</span>
|
|
API. This is exactly the same, only you will use the <span style="font-family:monospace;">ma_rb</span> functions instead of <span style="font-family:monospace;">ma_pcm_rb</span> and
|
|
instead of frame counts you will pass around byte counts.
|
|
</p>
|
|
<p>
|
|
|
|
The maximum size of the buffer in bytes is <span style="font-family:monospace;">0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)</span> due to the most
|
|
significant bit being used to encode a loop flag and the internally managed buffers always being
|
|
aligned to <span style="font-family:monospace;">MA_SIMD_ALIGNMENT</span>.
|
|
</p>
|
|
<p>
|
|
|
|
Note that the ring buffer is only thread safe when used by a single consumer thread and single
|
|
producer thread.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="Backends" class="man">15. Backends</h1>
|
|
<p>
|
|
The following backends are supported by miniaudio.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="overflow:hidden;"><table class="doc"><tr>
|
|
<th class="doc" valign="top"><p>
|
|
Name</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Enum Name</p>
|
|
</th>
|
|
<th class="doc" valign="top"><p>
|
|
Supported Operating Systems</p>
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
WASAPI</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_wasapi</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Windows Vista+</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
DirectSound</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_dsound</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Windows XP+</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
WinMM</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_winmm</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Windows XP+ (may work on older versions, but untested)</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Core Audio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_coreaudio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
macOS, iOS</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
ALSA</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_alsa</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Linux</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
PulseAudio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_pulseaudio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Cross Platform (disabled on Windows, BSD and Android)</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
JACK</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_jack</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Cross Platform (disabled on BSD and Android)</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
sndio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_sndio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
OpenBSD</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
audio(4)</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_audio4</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
NetBSD, OpenBSD</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
OSS</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_oss</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
FreeBSD</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
AAudio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_aaudio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Android 8+</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
OpenSL ES</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_opensl</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Android (API level 16+)</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Web Audio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_webaudio</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Web (via Emscripten)</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Custom</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_custom</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Cross Platform</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
Null</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_backend_null</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Cross Platform (not used on Web)</p>
|
|
</td>
|
|
</tr>
|
|
</table></div><p>
|
|
Some backends have some nuance details you may want to be aware of.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="WASAPI" class="man">15.1. WASAPI</h2>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
Low-latency shared mode will be disabled when using an application-defined sample rate which is
|
|
different to the device's native sample rate. To work around this, set <span style="font-family:monospace;">wasapi.noAutoConvertSRC</span>
|
|
to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing
|
|
when the <span style="font-family:monospace;">AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM</span> flag is specified. Setting wasapi.noAutoConvertSRC
|
|
will result in miniaudio's internal resampler being used instead which will in turn enable the
|
|
use of low-latency shared mode.</li>
|
|
</ul>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="PulseAudio" class="man">15.2. PulseAudio</h2>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
|
|
<a href="https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling">https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling</a>.
|
|
Alternatively, consider using a different backend such as ALSA.</li>
|
|
</ul>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Android" class="man">15.3. Android</h2>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
|
|
<span style="font-family:monospace;"><uses-permission android:name="android.permission.RECORD_AUDIO" /></span></li>
|
|
<li>
|
|
With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a
|
|
limitation with OpenSL|ES.</li>
|
|
<li>
|
|
With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration
|
|
API (devices are enumerated through Java). You can however perform your own device enumeration
|
|
through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it
|
|
to ma_device_init().</li>
|
|
<li>
|
|
The backend API will perform resampling where possible. The reason for this as opposed to using
|
|
miniaudio's built-in resampler is to take advantage of any potential device-specific
|
|
optimizations the driver may implement.</li>
|
|
</ul>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="UWP" class="man">15.4. UWP</h2>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
UWP only supports default playback and capture devices.</li>
|
|
<li>
|
|
UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):</li>
|
|
</ul>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto;">
|
|
<Package ...>
|
|
...
|
|
<Capabilities>
|
|
<DeviceCapability Name=<span style="color:#cc3300">"microphone"</span> />
|
|
</Capabilities>
|
|
</Package>
|
|
</pre></div><p>
|
|
|
|
</p>
|
|
<h2 id="WebAudio/Emscripten" class="man">15.5. Web Audio / Emscripten</h2>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
You cannot use <span style="font-family:monospace;">-std=c*</span> compiler flags, nor <span style="font-family:monospace;">-ansi</span>. This only applies to the Emscripten build.</li>
|
|
<li>
|
|
The first time a context is initialized it will create a global object called "miniaudio" whose
|
|
primary purpose is to act as a factory for device objects.</li>
|
|
<li>
|
|
Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as
|
|
they've been deprecated.</li>
|
|
<li>
|
|
Google has implemented a policy in their browsers that prevent automatic media output without
|
|
first receiving some kind of user input. The following web page has additional details:
|
|
<a href="https://developers.google.com/web/updates/2017/09/autoplay-policy-changes">https://developers.google.com/web/updates/2017/09/autoplay-policy-changes</a>. Starting the device
|
|
may fail if you try to start playback without first handling some kind of user input.</li>
|
|
</ul>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="OptimizationTips" class="man">16. Optimization Tips</h1>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="HighLevelAPI" class="man">16.1. High Level API</h2>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
If a sound does not require doppler or pitch shifting, consider disabling pitching by
|
|
initializing the sound with the <span style="font-family:monospace;">MA_SOUND_FLAG_NO_PITCH</span> flag.</li>
|
|
<li>
|
|
If a sound does not require spatialization, disable it by initialzing the sound with the
|
|
<span style="font-family:monospace;">MA_SOUND_FLAG_NO_SPATIALIZATION</span> flag. It can be renabled again post-initialization with
|
|
<span style="font-family:monospace;">ma_sound_set_spatialization_enabled()</span>.</li>
|
|
</ul>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="MiscellaneousNotes" class="man">17. Miscellaneous Notes</h1>
|
|
<ul style="overflow:hidden;">
|
|
<li>
|
|
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
|
|
not all have been tested.</li>
|
|
<li>
|
|
The contents of the output buffer passed into the data callback will always be pre-initialized to
|
|
silence unless the <span style="font-family:monospace;">noPreSilencedOutputBuffer</span> config variable in <span style="font-family:monospace;">ma_device_config</span> is set to
|
|
true, in which case it'll be undefined which will require you to write something to the entire
|
|
buffer.</li>
|
|
<li>
|
|
By default miniaudio will automatically clip samples. This only applies when the playback sample
|
|
format is configured as <span style="font-family:monospace;">ma_format_f32</span>. If you are doing clipping yourself, you can disable this
|
|
overhead by setting <span style="font-family:monospace;">noClip</span> to true in the device config.</li>
|
|
<li>
|
|
Note that GCC and Clang requires <span style="font-family:monospace;">-msse2</span>, <span style="font-family:monospace;">-mavx2</span>, etc. for SIMD optimizations.</li>
|
|
<li>
|
|
The sndio backend is currently only enabled on OpenBSD builds.</li>
|
|
<li>
|
|
The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can
|
|
use it.</li>
|
|
<li>
|
|
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.</li>
|
|
</ul>
|
|
</td>
|
|
</tr></table>
|
|
</div>
|
|
<table style="margin:0 auto; padding:1em 0px; text-align:center;">
|
|
<tr>
|
|
<td style="vertical-align:center;"><a style="padding:0;" href="https://www.reddit.com/r/miniaudio"><img src="../../img/reddit_white.svg" style="margin:0; padding:0; height:40px; width:40px;"></a></td>
|
|
<td style="vertical-align:center;"><a style="padding:0;" href="https://discord.gg/9vpqbjU"><img src="../../img/Discord-Logo-White.svg" style="padding:0; height:32px; width:32px;"></a></td>
|
|
<td style="vertical-align:center;"><a style="padding:0;" href="https://twitter.com/mackron"><img src="../../img/twitter_white.png" style="padding:0; height:32px; width:32px;"></a></td>
|
|
<td style="vertical-align:center;"><a style="padding:0;" href="https://github.com/mackron/miniaudio"><img src="../../img/github_white.png" style="padding:0; height:24px; width:24px;"></a></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<div style="color:#e0d7cf; font-size:9pt; padding:2em 0px; text-align:center;">
|
|
Copyright © 2022 David Reid<br/>
|
|
Developed by David Reid - <a class="footer-link" href="mailto:mackron@gmail.com">mackron@gmail.com</a>
|
|
</div>
|
|
</body>
|
|
</html>
|