mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
424 lines
19 KiB
HTML
424 lines
19 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="../manual/index.html">Documentation</a></td>
|
|
<td><a href="index.html">Examples</a></td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
|
|
<td style="padding:0.1em; width:25%; text-align:right; vertical-align:center;">
|
|
<a href="https://www.reddit.com/r/miniaudio"><img src="../../img/reddit_white.svg" style="margin:0; padding:0; height:40px; width:40px;"></a>
|
|
</td>
|
|
<td style="padding:0.1em; width:25%; text-align:right; vertical-align:center;">
|
|
<a href="https://discord.gg/9vpqbjU"><img src="../../img/Discord-Logo-White.svg" style="margin:0; padding:0; height:32px; width:32px;"></a>
|
|
</td>
|
|
<td style="padding:0.1em; width:25%; text-align:right; vertical-align:center;">
|
|
<a rel="me" href="https://fosstodon.org/@mackron"><img src="../../img/mastodon_white.svg" style="margin:0; padding:0; height:24px; width:32px;"></a>
|
|
</td>
|
|
<td style="padding:0.1em; padding-right:1em; width:25%; text-align:right; vertical-align:center;">
|
|
<a href="https://github.com/mackron/miniaudio"><img src="../../img/github_white.png" style="margin:0; padding:0; height:24px; width:24px;"></a>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div style="background-color:#fff; padding-bottom:0em; border-top:solid 1px #003800; background-color:#eee;">
|
|
<table border="0" style="margin:0 auto; width:100%; border-collapse:collapse; border:solid 0px #000; table-layout:fixed;"><tr>
|
|
<td valign="top" style="width:20em; padding:0; margin:0; border-right:solid 0px #000;"><div style="position:relative; height:100%; width:100%; border:solid 0px #000; padding:0; margin:0;">
|
|
<a href="../index.html" class="doc-navigation">Documentation Home</a><a href="../manual/index.html" class="doc-navigation">Programming Manual</a><a href="index.html" class="doc-navigation ">Examples</a><a href="custom_backend.html" class="doc-navigation doc-navigation-l1 ">Custom Backend</a><a href="custom_decoder.html" class="doc-navigation doc-navigation-l1 ">Custom Decoder</a><a href="custom_decoder_engine.html" class="doc-navigation doc-navigation-l1 ">Custom Decoder Engine</a><a href="data_source_chaining.html" class="doc-navigation doc-navigation-l1 ">Data Source Chaining</a><a href="duplex_effect.html" class="doc-navigation doc-navigation-l1 doc-navigation-active">Duplex Effect</a><a href="engine_advanced.html" class="doc-navigation doc-navigation-l1 ">Engine Advanced</a><a href="engine_effects.html" class="doc-navigation doc-navigation-l1 ">Engine Effects</a><a href="engine_hello_world.html" class="doc-navigation doc-navigation-l1 ">Engine Hello World</a><a href="engine_sdl.html" class="doc-navigation doc-navigation-l1 ">Engine Sdl</a><a href="engine_steamaudio.html" class="doc-navigation doc-navigation-l1 ">Engine Steamaudio</a><a href="hilo_interop.html" class="doc-navigation doc-navigation-l1 ">Hilo Interop</a><a href="node_graph.html" class="doc-navigation doc-navigation-l1 ">Node Graph</a><a href="resource_manager.html" class="doc-navigation doc-navigation-l1 ">Resource Manager</a><a href="resource_manager_advanced.html" class="doc-navigation doc-navigation-l1 ">Resource Manager Advanced</a><a href="simple_capture.html" class="doc-navigation doc-navigation-l1 ">Simple Capture</a><a href="simple_duplex.html" class="doc-navigation doc-navigation-l1 ">Simple Duplex</a><a href="simple_enumeration.html" class="doc-navigation doc-navigation-l1 ">Simple Enumeration</a><a href="simple_loopback.html" class="doc-navigation doc-navigation-l1 ">Simple Loopback</a><a href="simple_looping.html" class="doc-navigation doc-navigation-l1 ">Simple Looping</a><a href="simple_mixing.html" class="doc-navigation doc-navigation-l1 ">Simple Mixing</a><a href="simple_playback.html" class="doc-navigation doc-navigation-l1 ">Simple Playback</a><a href="simple_playback_sine.html" class="doc-navigation doc-navigation-l1 ">Simple Playback Sine</a><a href="simple_playback_sine.html" class="doc-navigation doc-navigation-l1 ">Simple Playback Sine</a><a href="simple_spatialization.html" class="doc-navigation doc-navigation-l1 ">Simple Spatialization</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;">
|
|
<h1>Duplex Effect</h1><p>
|
|
Demonstrates how to apply an effect to a duplex stream using the node graph system.
|
|
</p>
|
|
<p>
|
|
|
|
This example applies a vocoder effect to the input stream before outputting it. A custom node
|
|
called <span style="font-family:monospace;">ma_vocoder_node</span> is used to achieve the effect which can be found in the extras folder in
|
|
the miniaudio repository. The vocoder node uses <a href="https://github.com/blastbay/voclib">https://github.com/blastbay/voclib</a> to achieve the
|
|
effect.</p>
|
|
<div style="font-family:monospace; border:solid 1px #003800; border-left:solid 0.5em #003800; margin:1em 0em; width:100%;"><pre style="margin:0.5em 1em; padding:0; line-height:125%; overflow-x:auto; overflow-y:hidden;">
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300">"../miniaudio.c"</span>
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300">"../extras/nodes/ma_vocoder_node/ma_vocoder_node.c"</span>
|
|
|
|
<span style="color:#666666">#include</span> <span style="color:#cc3300"><stdio.h></span>
|
|
|
|
<span style="color:#666666">#define</span> DEVICE_FORMAT ma_format_f32 <span style="color:#009900">/* Must always be f32 for this example because the node graph system only works with this. */</span>
|
|
<span style="color:#666666">#define</span> DEVICE_CHANNELS 1 <span style="color:#009900">/* For this example, always set to 1. */</span>
|
|
|
|
<span style="color:#0033ff">static</span> <span style="color:#0099cc">ma_waveform</span> g_sourceData; <span style="color:#009900">/* The underlying data source of the source node. */</span>
|
|
<span style="color:#0033ff">static</span> ma_audio_buffer_ref g_exciteData; <span style="color:#009900">/* The underlying data source of the excite node. */</span>
|
|
<span style="color:#0033ff">static</span> ma_data_source_node g_sourceNode; <span style="color:#009900">/* A data source node containing the source data we'll be sending through to the vocoder. This will be routed into the first bus of the vocoder node. */</span>
|
|
<span style="color:#0033ff">static</span> ma_data_source_node g_exciteNode; <span style="color:#009900">/* A data source node containing the excite data we'll be sending through to the vocoder. This will be routed into the second bus of the vocoder node. */</span>
|
|
<span style="color:#0033ff">static</span> ma_vocoder_node g_vocoderNode; <span style="color:#009900">/* The vocoder node. */</span>
|
|
<span style="color:#0033ff">static</span> ma_node_graph g_nodeGraph;
|
|
|
|
<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">/*
|
|
This example assumes the playback and capture sides use the same format and channel count. The
|
|
format must be f32.
|
|
*/</span>
|
|
<span style="color:#0033ff">if</span> (pDevice->capture.format != DEVICE_FORMAT || pDevice->playback.format != DEVICE_FORMAT || pDevice->capture.channels != pDevice->playback.channels) {
|
|
<span style="color:#0033ff">return</span>;
|
|
}
|
|
|
|
<span style="color:#009900">/*
|
|
The node graph system is a pulling style of API. At the lowest level of the chain will be a
|
|
node acting as a data source for the purpose of delivering the initial audio data. In our case,
|
|
the data source is our <span style="font-family:monospace;">pInput</span> buffer. We need to update the underlying data source so that it
|
|
read data from <span style="font-family:monospace;">pInput</span>.
|
|
*/</span>
|
|
ma_audio_buffer_ref_set_data(&g_exciteData, pInput, frameCount);
|
|
|
|
<span style="color:#009900">/* With the source buffer configured we can now read directly from the node graph. */</span>
|
|
ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL);
|
|
}
|
|
|
|
<span style="color:#0033ff">int</span> main(<span style="color:#0033ff">int</span> argc, <span style="color:#0033ff">char</span>** argv)
|
|
{
|
|
<span style="color:#0099cc">ma_result</span> result;
|
|
<span style="color:#0099cc">ma_device_config</span> deviceConfig;
|
|
<span style="color:#0099cc">ma_device</span> device;
|
|
ma_node_graph_config nodeGraphConfig;
|
|
ma_vocoder_node_config vocoderNodeConfig;
|
|
ma_data_source_node_config sourceNodeConfig;
|
|
ma_data_source_node_config exciteNodeConfig;
|
|
<span style="color:#0099cc">ma_waveform_config</span> waveformConfig;
|
|
|
|
deviceConfig = ma_device_config_init(ma_device_type_duplex);
|
|
deviceConfig.capture.pDeviceID = NULL;
|
|
deviceConfig.capture.format = DEVICE_FORMAT;
|
|
deviceConfig.capture.channels = DEVICE_CHANNELS;
|
|
deviceConfig.capture.shareMode = ma_share_mode_shared;
|
|
deviceConfig.playback.pDeviceID = NULL;
|
|
deviceConfig.playback.format = DEVICE_FORMAT;
|
|
deviceConfig.playback.channels = DEVICE_CHANNELS;
|
|
deviceConfig.dataCallback = data_callback;
|
|
result = ma_device_init(NULL, &deviceConfig, &device);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
<span style="color:#0033ff">return</span> result;
|
|
}
|
|
|
|
|
|
<span style="color:#009900">/* Now we can setup our node graph. */</span>
|
|
nodeGraphConfig = ma_node_graph_config_init(device.capture.channels);
|
|
|
|
result = ma_node_graph_init(&nodeGraphConfig, NULL, &g_nodeGraph);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
printf(<span style="color:#cc3300">"Failed to initialize node graph."</span>);
|
|
<span style="color:#0033ff">goto</span> done0;
|
|
}
|
|
|
|
|
|
<span style="color:#009900">/* Vocoder. Attached straight to the endpoint. */</span>
|
|
vocoderNodeConfig = ma_vocoder_node_config_init(device.capture.channels, device.sampleRate);
|
|
|
|
result = ma_vocoder_node_init(&g_nodeGraph, &vocoderNodeConfig, NULL, &g_vocoderNode);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
printf(<span style="color:#cc3300">"Failed to initialize vocoder node."</span>);
|
|
<span style="color:#0033ff">goto</span> done1;
|
|
}
|
|
|
|
ma_node_attach_output_bus(&g_vocoderNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0);
|
|
|
|
<span style="color:#009900">/* Amplify the volume of the vocoder output because in my testing it is a bit quiet. */</span>
|
|
ma_node_set_output_bus_volume(&g_vocoderNode, 0, 4);
|
|
|
|
|
|
<span style="color:#009900">/* Source/carrier. Attached to input bus 0 of the vocoder node. */</span>
|
|
waveformConfig = ma_waveform_config_init(device.capture.format, device.capture.channels, device.sampleRate, ma_waveform_type_sawtooth, 1.0, 50);
|
|
|
|
result = ma_waveform_init(&waveformConfig, &g_sourceData);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
printf(<span style="color:#cc3300">"Failed to initialize waveform for excite node."</span>);
|
|
<span style="color:#0033ff">goto</span> done3;
|
|
}
|
|
|
|
sourceNodeConfig = ma_data_source_node_config_init(&g_sourceData);
|
|
|
|
result = ma_data_source_node_init(&g_nodeGraph, &sourceNodeConfig, NULL, &g_sourceNode);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
printf(<span style="color:#cc3300">"Failed to initialize excite node."</span>);
|
|
<span style="color:#0033ff">goto</span> done3;
|
|
}
|
|
|
|
ma_node_attach_output_bus(&g_sourceNode, 0, &g_vocoderNode, 0);
|
|
|
|
|
|
<span style="color:#009900">/* Excite/modulator. Attached to input bus 1 of the vocoder node. */</span>
|
|
result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, NULL, 0, &g_exciteData);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
printf(<span style="color:#cc3300">"Failed to initialize audio buffer for source."</span>);
|
|
<span style="color:#0033ff">goto</span> done2;
|
|
}
|
|
|
|
exciteNodeConfig = ma_data_source_node_config_init(&g_exciteData);
|
|
|
|
result = ma_data_source_node_init(&g_nodeGraph, &exciteNodeConfig, NULL, &g_exciteNode);
|
|
<span style="color:#0033ff">if</span> (result != MA_SUCCESS) {
|
|
printf(<span style="color:#cc3300">"Failed to initialize source node."</span>);
|
|
<span style="color:#0033ff">goto</span> done2;
|
|
}
|
|
|
|
ma_node_attach_output_bus(&g_exciteNode, 0, &g_vocoderNode, 1);
|
|
|
|
|
|
ma_device_start(&device);
|
|
|
|
printf(<span style="color:#cc3300">"Press Enter to quit...\n"</span>);
|
|
getchar();
|
|
|
|
<span style="color:#009900">/* It's important that we stop the device first or else we'll uninitialize the graph from under the device. */</span>
|
|
ma_device_stop(&device);
|
|
|
|
<span style="color:#009900">/*done4:*/</span> ma_data_source_node_uninit(&g_exciteNode, NULL);
|
|
done3: ma_data_source_node_uninit(&g_sourceNode, NULL);
|
|
done2: ma_vocoder_node_uninit(&g_vocoderNode, NULL);
|
|
done1: ma_node_graph_uninit(&g_nodeGraph, NULL);
|
|
done0: ma_device_uninit(&device);
|
|
|
|
(<span style="color:#0033ff">void</span>)argc;
|
|
(<span style="color:#0033ff">void</span>)argv;
|
|
<span style="color:#0033ff">return</span> 0;
|
|
}
|
|
</pre></div></td>
|
|
</tr></table>
|
|
</div>
|
|
<table style="margin:0 auto; padding:1em 0px; text-align:center;">
|
|
<tr>
|
|
<td style="vertical-align:center;"><a style="padding:0;" href="https://www.reddit.com/r/miniaudio"><img src="../../img/reddit_white.svg" style="margin:0; padding:0; height:40px; width:40px;"></a></td>
|
|
<td style="vertical-align:center;"><a style="padding:0;" href="https://discord.gg/9vpqbjU"><img src="../../img/Discord-Logo-White.svg" style="padding:0; height:32px; width:32px;"></a></td>
|
|
<td rel="me" style="vertical-align:center;"><a style="padding:0;" href="https://fosstodon.org/@mackron"><img src="../../img/mastodon_white.svg" style="padding:0; height:24px; 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 © 2025 David Reid<br/>
|
|
Developed by David Reid - <a class="footer-link" href="mailto:mackron@gmail.com">mackron@gmail.com</a>
|
|
</div>
|
|
</body>
|
|
</html>
|