mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-21 15:56:58 +02:00
3133 lines
110 KiB
HTML
3133 lines
110 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://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;"><tr>
|
|
<td valign="top" style="width:20em; padding:0; margin:0; border-right:solid 0px #000;"><div style="position:relative; height:100%; width:300px; 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="#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="#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;"><img src="../../img/logo1_large.png" style="width:auto; height:auto; min-height:70px;"></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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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 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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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>
|
|
<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><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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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>
|
|
<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><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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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>
|
|
<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>
|
|
|
|
</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 (sorry) and will need to link the relevant frameworks but should Just Work 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>
|
|
|
|
</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.
|
|
</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.
|
|
</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>
|
|
|
|
</p>
|
|
<h2 id="Emscripten" class="man">2.6. Emscripten</h2>
|
|
<p>
|
|
The Emscripten build emits Web Audio JavaScript directly and should Just Work without any configuration. You cannot use -std=c* compiler flags, nor -ansi.
|
|
</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>
|
|
<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_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 ma_context and ma_device 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>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables the ma_thread, ma_mutex, ma_semaphore and ma_event APIs. This option is useful if you only need to use miniaudio for
|
|
data conversion, decoding and/or encoding. Some families of APIs require 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%">
|
|
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 ma_waveform and ma_noise.</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_AVX512</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Disables AVX-512 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_LOG_LEVEL [level]
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Sets the logging level. Set level to one of the following:
|
|
|
|
</p>
|
|
<div style="font-family:monospace; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
MA_LOG_LEVEL_VERBOSE
|
|
MA_LOG_LEVEL_INFO
|
|
MA_LOG_LEVEL_WARNING
|
|
MA_LOG_LEVEL_ERROR
|
|
</pre></div></td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_DEBUG_OUTPUT</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
Enable printf() debug output.</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. Defaults to <span style="font-family:monospace;">extern</span>.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="doc" valign="top"><p>
|
|
MA_DLL
|
|
|
|
</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
If set, configures MA_API to either import or export APIs depending on whether or not the implementation is being defined. If
|
|
defining the implementation, MA_API will be configured to export. Otherwise it will be configured to import. This has no effect
|
|
if MA_API is defined externally.</p>
|
|
</td>
|
|
</tr>
|
|
</table><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>
|
|
<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><p>
|
|
All formats are native-endian.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="Decoding" class="man">4. Decoding</h1>
|
|
<p>
|
|
The <span style="font-family:monospace;">ma_decoder</span> API is used for reading audio files. The following formats are supported:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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 ma_decoder_config object (the NULL 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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<span style="color:#0099cc">ma_decoder_config</span> config = ma_decoder_config_init(ma_format_f32, 2, 48000);
|
|
</pre></div><p>
|
|
|
|
When passing in NULL 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 return the number of PCM frames actually read. If the return value is less than the requested number of
|
|
PCM frames it means you've reached the end:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<span style="color:#0099cc">ma_uint64</span> framesRead = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead);
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
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 the <span style="font-family:monospace;">_wav</span>, <span style="font-family:monospace;">_mp3</span>, etc. varients of the aforementioned initialization APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
ma_decoder_init_wav()
|
|
ma_decoder_init_mp3()
|
|
ma_decoder_init_memory_wav()
|
|
ma_decoder_init_memory_mp3()
|
|
ma_decoder_init_file_wav()
|
|
ma_decoder_init_file_mp3()
|
|
etc.
|
|
</pre></div><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>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="Encoding" class="man">5. 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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<span style="color:#0099cc">ma_encoder_config</span> config = ma_encoder_config_init(ma_resource_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>
|
|
<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_resource_format_wav</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
WAV</p>
|
|
</td>
|
|
</tr>
|
|
</table><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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
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>
|
|
<h1 id="DataConversion" class="man">6. 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">6.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">6.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>
|
|
<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><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%">
|
|
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">6.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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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, &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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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">6.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 MA_CHANNEL_FRONT_LEFT 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_get_standard_channel_map()</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>
|
|
<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><p>
|
|
Below are the channel maps used by default in miniaudio (ma_standard_channel_map_default):
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Resampling" class="man">6.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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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 supports multiple algorithms:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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>
|
|
Speex</p>
|
|
</td>
|
|
<td class="doc" valign="top"><p>
|
|
ma_resample_algorithm_speex</p>
|
|
</td>
|
|
</tr>
|
|
</table><p>
|
|
Because Speex is not public domain it is strictly opt-in and the code is stored in separate files. if you opt-in to the Speex backend you will need to consider
|
|
it's license, the text of which can be found in it's source files in "extras/speex_resampler". Details on how to opt-in to the Speex resampler is explained in
|
|
the Speex Resampler section below.
|
|
</p>
|
|
<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">6.3.1. Resampling Algorithms</h2>
|
|
<p>
|
|
The choice of resampling algorithm depends on your situation and requirements. The linear resampler is the most efficient and has the least amount of latency,
|
|
but at the expense of poorer quality. The Speex resampler is higher quality, but slower with more latency. It also performs several heap allocations internally
|
|
for memory management.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="LinearResampling" class="man">6.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). This
|
|
can be controlled with the <span style="font-family:monospace;">lpfNyquistFactor</span> config variable. This defaults to 1, and should be in the range of 0..1, although a value of 0 does not make
|
|
sense and should be avoided. A value of 1 will use the Nyquist Frequency as the cutoff. A value of 0.5 will use half the Nyquist Frequency as the cutoff, etc.
|
|
Values less than 1 will result in more washed out sound due to more of the higher frequencies being removed. This config variable has no impact on performance
|
|
and is a purely perceptual configuration.
|
|
</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="SpeexResampling" class="man">6.3.1.2. Speex Resampling</h2>
|
|
<p>
|
|
The Speex resampler is made up of third party code which is released under the BSD license. Because it is licensed differently to miniaudio, which is public
|
|
domain, it is strictly opt-in and all of it's code is stored in separate files. If you opt-in to the Speex resampler you must consider the license text in it's
|
|
source files. To opt-in, you must first #include the following file before the implementation of miniaudio.h:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300">"extras/speex_resampler/ma_speex_resampler.h"</span>
|
|
</pre></div><p>
|
|
|
|
Both the header and implementation is contained within the same file. The implementation can be included in your program like so:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<span style="color:#666666">#define</span> MINIAUDIO_SPEEX_RESAMPLER_IMPLEMENTATION
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300">"extras/speex_resampler/ma_speex_resampler.h"</span>
|
|
</pre></div><p>
|
|
|
|
Note that even if you opt-in to the Speex backend, miniaudio won't use it unless you explicitly ask for it in the respective config of the object you are
|
|
initializing. If you try to use the Speex resampler without opting in, initialization of the <span style="font-family:monospace;">ma_resampler</span> object will fail with <span style="font-family:monospace;">MA_NO_BACKEND</span>.
|
|
</p>
|
|
<p>
|
|
|
|
The only configuration option to consider with the Speex resampler is the <span style="font-family:monospace;">speex.quality</span> config variable. This is a value between 0 and 10, with 0 being
|
|
the fastest with the poorest quality and 10 being the slowest with the highest quality. The default value is 3.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="GeneralDataConversion" class="man">6.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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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, &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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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_get_standard_channel_map(ma_standard_channel_map_flac, config.channelCountIn, config.channelMapIn);
|
|
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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
ma_data_converter_uninit(&converter);
|
|
</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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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 MA_TRUE. 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">7. Filtering</h1>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="BiquadFiltering" class="man">7.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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
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">7.2. Low-Pass Filtering</h2>
|
|
<p>
|
|
Low-pass filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
|
|
</pre></div><p>
|
|
|
|
The maximum filter order is limited to MA_MAX_FILTER_ORDER 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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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">7.3. High-Pass Filtering</h2>
|
|
<p>
|
|
High-pass filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><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">7.4. Band-Pass Filtering</h2>
|
|
<p>
|
|
Band-pass filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><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">7.5. Notch Filtering</h2>
|
|
<p>
|
|
Notch filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><p>
|
|
|
|
</p>
|
|
<h2 id="PeakingEQFiltering" class="man">7.6. Peaking EQ Filtering</h2>
|
|
<p>
|
|
Peaking filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><p>
|
|
|
|
</p>
|
|
<h2 id="LowShelfFiltering" class="man">7.7. Low Shelf Filtering</h2>
|
|
<p>
|
|
Low shelf filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><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">7.8. High Shelf Filtering</h2>
|
|
<p>
|
|
High shelf filtering is achieved with the following APIs:
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><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">8. Waveform and Noise Generation</h1>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Waveforms" class="man">8.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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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 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> 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>
|
|
<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><p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="Noise" class="man">8.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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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 MA_DEFAULT_LCG_SEED.
|
|
</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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
config.duplicateChannels = MA_TRUE;
|
|
</pre></div><p>
|
|
|
|
Below are the supported noise types.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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><p>
|
|
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h1 id="AudioBuffers" class="man">9. 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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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">10. 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;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<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 'ma_pcm_rb_acquire_read()<span style="font-family:monospace;"> and </span>ma_pcm_rb_acquire_write()` 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.
|
|
</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 MA_SIMD_ALIGNMENT.
|
|
</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">11. Backends</h1>
|
|
<p>
|
|
The following backends are supported by miniaudio.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<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>
|
|
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><p>
|
|
Some backends have some nuance details you may want to be aware of.
|
|
</p>
|
|
<p>
|
|
|
|
</p>
|
|
<h2 id="WASAPI" class="man">11.1. WASAPI</h2>
|
|
<ul>
|
|
<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>
|
|
<h2 id="PulseAudio" class="man">11.2. PulseAudio</h2>
|
|
<ul>
|
|
<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>
|
|
<h2 id="Android" class="man">11.3. Android</h2>
|
|
<ul>
|
|
<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>
|
|
<h2 id="UWP" class="man">11.4. UWP</h2>
|
|
<ul>
|
|
<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>
|
|
<div style="font-family:monospace; margin:1em 0em;"><pre style="margin:0.5em 1em; padding:0; line-height:125%">
|
|
<Package ...>
|
|
...
|
|
<Capabilities>
|
|
<DeviceCapability Name=<span style="color:#cc3300">"microphone"</span> />
|
|
</Capabilities>
|
|
</Package>
|
|
</pre></div><p>
|
|
|
|
</p>
|
|
<h2 id="WebAudio/Emscripten" class="man">11.5. Web Audio / Emscripten</h2>
|
|
<ul>
|
|
<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>
|
|
<h1 id="MiscellaneousNotes" class="man">12. Miscellaneous Notes</h1>
|
|
<ul>
|
|
<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 zero unless the <span style="font-family:monospace;">noPreZeroedOutputBuffer</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>
|
|
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>
|
|
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>
|
|
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://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 © 2020 David Reid<br/>
|
|
Developed by David Reid - <a class="footer-link" href="mailto:mackron@gmail.com">mackron@gmail.com</a>
|
|
</div>
|
|
</body>
|
|
</html>
|