Add a basic single-threaded test to deviceio test.

This commit is contained in:
David Reid
2026-01-01 14:52:01 +10:00
parent db6bc371eb
commit 1d308f69bc
+48 -2
View File
@@ -1,5 +1,5 @@
/* /*
USAGE: deviceio [input/output file] [mode] [backend] [waveform] [noise] [--auto] USAGE: deviceio [input/output file] [mode] [backend] [waveform] [noise] [threading mode] [--auto]
In playback mode the input file is optional, in which case a waveform or noise source will be used instead. For capture and loopback modes In playback mode the input file is optional, in which case a waveform or noise source will be used instead. For capture and loopback modes
it must specify an output parameter, and must be specified. In duplex mode it is optional, but if specified will be an output file that it must specify an output parameter, and must be specified. In duplex mode it is optional, but if specified will be an output file that
@@ -19,6 +19,7 @@ will receive the captured audio.
sndio sndio
audio4 audio4
oss oss
pipewire
pulseaudio or pulse pulseaudio or pulse
alsa alsa
jack jack
@@ -27,7 +28,6 @@ will receive the captured audio.
webaudio webaudio
null null
sdl2 sdl2
pipewire
"waveform" can be one of the following: "waveform" can be one of the following:
sine sine
@@ -40,6 +40,11 @@ will receive the captured audio.
pink pink
brownian or brown brownian or brown
"threading mode" can be one of the following:
multi-threaded or multithreaded (default)
single-threaded or singlethreaded
If multiple backends are specified, the priority will be based on the order in which you specify them. If multiple waveform or noise types If multiple backends are specified, the priority will be based on the order in which you specify them. If multiple waveform or noise types
are specified the last one on the command line will have priority. are specified the last one on the command line will have priority.
*/ */
@@ -256,6 +261,23 @@ ma_bool32 try_parse_noise(const char* arg, ma_noise_type* pNoiseType)
return MA_FALSE; return MA_FALSE;
} }
ma_bool32 try_parse_threading_mode(const char* arg, ma_threading_mode* pThreadingMode)
{
MA_ASSERT(arg != NULL);
MA_ASSERT(pThreadingMode != NULL);
if (strcmp(arg, "multi-threaded") == 0 || strcmp(arg, "multithreaded") == 0) {
*pThreadingMode = MA_THREADING_MODE_MULTI_THREADED;
return MA_TRUE;
}
if (strcmp(arg, "single-threaded") == 0 || strcmp(arg, "singlethreaded") == 0) {
*pThreadingMode = MA_THREADING_MODE_SINGLE_THREADED;
return MA_TRUE;
}
return MA_FALSE;
}
void print_enabled_backends(void) void print_enabled_backends(void)
{ {
ma_device_backend_config pStockBackends[MA_MAX_STOCK_DEVICE_BACKENDS]; ma_device_backend_config pStockBackends[MA_MAX_STOCK_DEVICE_BACKENDS];
@@ -449,6 +471,7 @@ int main(int argc, char** argv)
ma_device_config deviceConfig; ma_device_config deviceConfig;
ma_waveform_type waveformType = ma_waveform_type_sine; ma_waveform_type waveformType = ma_waveform_type_sine;
ma_noise_type noiseType = ma_noise_type_white; ma_noise_type noiseType = ma_noise_type_white;
ma_threading_mode threadingMode = MA_THREADING_MODE_MULTI_THREADED;
const char* pFilePath = NULL; /* Input or output file path, depending on the mode. */ const char* pFilePath = NULL; /* Input or output file path, depending on the mode. */
ma_bool32 enumerate = MA_TRUE; ma_bool32 enumerate = MA_TRUE;
ma_bool32 interactive = MA_TRUE; ma_bool32 interactive = MA_TRUE;
@@ -487,6 +510,11 @@ int main(int argc, char** argv)
continue; continue;
} }
/* threading mode */
if (try_parse_threading_mode(argv[iarg], &threadingMode)) {
continue;
}
/* Getting here means the argument should be considered the input or output file. */ /* Getting here means the argument should be considered the input or output file. */
pFilePath = argv[iarg]; pFilePath = argv[iarg];
g_State.sourceType = source_type_decoder; g_State.sourceType = source_type_decoder;
@@ -534,6 +562,7 @@ int main(int argc, char** argv)
} }
deviceConfig = ma_device_config_init(deviceType); deviceConfig = ma_device_config_init(deviceType);
deviceConfig.threadingMode = threadingMode;
deviceConfig.playback.format = deviceFormat; deviceConfig.playback.format = deviceFormat;
deviceConfig.playback.channels = deviceChannels; deviceConfig.playback.channels = deviceChannels;
deviceConfig.capture.format = deviceFormat; deviceConfig.capture.format = deviceFormat;
@@ -622,9 +651,14 @@ int main(int argc, char** argv)
goto done; goto done;
} }
if (ma_device_get_threading_mode(&g_State.device) == MA_THREADING_MODE_SINGLE_THREADED) {
printf("Running in single-threaded mode. Press Ctrl+C to quit.\n");
}
/* Now we just keep looping and wait for user input. */ /* Now we just keep looping and wait for user input. */
for (;;) { for (;;) {
if (interactive) { if (interactive) {
if (ma_device_get_threading_mode(&g_State.device) == MA_THREADING_MODE_MULTI_THREADED) {
int c; int c;
if (ma_device_is_started(&g_State.device)) { if (ma_device_is_started(&g_State.device)) {
@@ -657,12 +691,24 @@ int main(int argc, char** argv)
} }
} }
} }
} else {
/* Single-threaded mode. Just sleep for a bit and check if we want to close. */
ma_device_step(&g_State.device, MA_BLOCKING_MODE_BLOCKING);
if (g_State.wantsToClose) {
break;
}
}
} else { } else {
/* Running in auto-close mode. Just sleep for a bit. The data callback will control when this loop aborts. */ /* Running in auto-close mode. Just sleep for a bit. The data callback will control when this loop aborts. */
if (g_State.wantsToClose) { if (g_State.wantsToClose) {
break; break;
} }
if (ma_device_get_threading_mode(&g_State.device) == MA_THREADING_MODE_SINGLE_THREADED) {
ma_device_step(&g_State.device, MA_BLOCKING_MODE_BLOCKING);
}
/* /*
Can't sleep with Emscripten. Just skip the sleeping part in this case. I don't run this test for Emscripten Can't sleep with Emscripten. Just skip the sleeping part in this case. I don't run this test for Emscripten
so it doesn't matter. Just fixing this for the sake of automated build tools. so it doesn't matter. Just fixing this for the sake of automated build tools.