mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
112 lines
4.5 KiB
C
112 lines
4.5 KiB
C
/*
|
|
Demonstrates the use of single-threaded mode.
|
|
|
|
By default, miniaudio runs in multi-threaded mode where audio processing is done on a separate
|
|
thread that's managed internally by miniaudio. Sometimes this is undesireable, such as when trying
|
|
to get miniaudio working on low-end systems where an extra thread is too costly, or when trying to
|
|
get it working on extremely old platforms that don't support threading at all, or simply when you
|
|
want to have more control over threading in your application.
|
|
|
|
To enable single-threaded mode, set the `threadingMode` member of the `ma_device_config` structure
|
|
to `MA_THREADING_MODE_SINGLE_THREADED`. To process audio, you need to regularly call
|
|
`ma_device_step()`, usually from your main application loop. It is from this function that the
|
|
data callback will get fired. You should only call `ma_device_step()` when the device is started,
|
|
so typically you would do this between your `ma_device_start()` and `ma_device_stop()` calls.
|
|
|
|
The `ma_device_step()` function lets you control whether or not it should block while waiting for
|
|
audio to be processed. This is controlled via the `blockingMode` parameter. You would typically
|
|
use `MA_BLOCKING_MODE_BLOCKING` if you want to relax the CPU. For a game you would probably want to
|
|
use `MA_BLOCKING_MODE_NON_BLOCKING`.
|
|
|
|
You should only ever call `ma_device_step()` in single-threaded mode. In multi-threaded mode (the
|
|
default), you should never call this function manually. You can query whether or not the device is
|
|
in single-threaded mode via `ma_device_get_threading_mode()`.
|
|
|
|
When in single-threaded mode, you should ensure that all `ma_device` API calls are made from the same
|
|
thread. This is due to some backends that require it, such as WASAPI.
|
|
*/
|
|
#include "../miniaudio.c"
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#include <emscripten.h>
|
|
|
|
void main_loop__em(void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
ma_device_step(pDevice, MA_BLOCKING_MODE_NON_BLOCKING);
|
|
}
|
|
#endif
|
|
|
|
#define DEVICE_FORMAT ma_format_f32
|
|
#define DEVICE_CHANNELS 2
|
|
#define DEVICE_SAMPLE_RATE 48000
|
|
|
|
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
|
|
{
|
|
ma_waveform_read_pcm_frames((ma_waveform*)pDevice->pUserData, pOutput, frameCount, NULL);
|
|
|
|
(void)pInput; /* Unused. */
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
ma_waveform sineWave;
|
|
ma_device_config deviceConfig;
|
|
ma_device device;
|
|
ma_waveform_config sineWaveConfig;
|
|
|
|
deviceConfig = ma_device_config_init(ma_device_type_playback);
|
|
deviceConfig.threadingMode = MA_THREADING_MODE_SINGLE_THREADED; /* <-- This is what enables single-threaded mode. */
|
|
deviceConfig.playback.format = DEVICE_FORMAT;
|
|
deviceConfig.playback.channels = DEVICE_CHANNELS;
|
|
deviceConfig.sampleRate = DEVICE_SAMPLE_RATE;
|
|
deviceConfig.dataCallback = data_callback;
|
|
deviceConfig.pUserData = &sineWave;
|
|
|
|
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) {
|
|
printf("Failed to open playback device.\n");
|
|
return -4;
|
|
}
|
|
|
|
sineWaveConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.2, 220);
|
|
ma_waveform_init(&sineWaveConfig, &sineWave);
|
|
|
|
if (ma_device_start(&device) != MA_SUCCESS) {
|
|
printf("Failed to start playback device.\n");
|
|
ma_device_uninit(&device);
|
|
return -5;
|
|
}
|
|
|
|
printf("Running in single-threaded mode. Press Ctrl+C to quit.\n");
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
emscripten_set_main_loop_arg(main_loop__em, &device, 0, 1);
|
|
#else
|
|
/*
|
|
We're putting this in an infinite loop for the sake of this example, but in a real application you
|
|
would probably integrate this into your normal application loop.
|
|
|
|
Using blocking mode makes it so the CPU is relaxed. For a game you would probably want to use
|
|
non-blocking mode which you can do with `MA_BLOCKING_MODE_NON_BLOCKING`.
|
|
|
|
If the device is stopped, `ma_device_step()` will return `MA_DEVICE_NOT_STARTED` which means you
|
|
need not explicitly check if the device is started before calling this function.
|
|
*/
|
|
for (;;) {
|
|
ma_result result = ma_device_step(&device, MA_BLOCKING_MODE_BLOCKING);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ma_device_uninit(&device);
|
|
ma_waveform_uninit(&sineWave); /* Uninitialize the waveform after the device so we don't pull it from under the device while it's being reference in the data callback. */
|
|
|
|
(void)argc;
|
|
(void)argv;
|
|
return 0;
|
|
}
|