Commit 4e91c638 authored by David Reid's avatar David Reid

Delete old tests.

parent 673dce19
#define MA_LOG_LEVEL MA_LOG_LEVEL_VERBOSE
#define MA_DEBUG_OUTPUT
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
int print_context_info(ma_context* pContext)
{
ma_result result = MA_SUCCESS;
ma_device_info* pPlaybackDeviceInfos;
ma_uint32 playbackDeviceCount;
ma_device_info* pCaptureDeviceInfos;
ma_uint32 captureDeviceCount;
printf("BACKEND: %s\n", ma_get_backend_name(pContext->backend));
// Enumeration.
printf(" Enumerating Devices... ");
{
result = ma_context_get_devices(pContext, &pPlaybackDeviceInfos, &playbackDeviceCount, &pCaptureDeviceInfos, &captureDeviceCount);
if (result == MA_SUCCESS) {
printf("Done\n");
} else {
printf("Failed\n");
goto done;
}
printf(" Playback Devices (%d)\n", playbackDeviceCount);
for (ma_uint32 iDevice = 0; iDevice < playbackDeviceCount; ++iDevice) {
printf(" %d: %s\n", iDevice, pPlaybackDeviceInfos[iDevice].name);
}
printf(" Capture Devices (%d)\n", captureDeviceCount);
for (ma_uint32 iDevice = 0; iDevice < captureDeviceCount; ++iDevice) {
printf(" %d: %s\n", iDevice, pCaptureDeviceInfos[iDevice].name);
}
}
// Device Information.
printf(" Getting Device Information...\n");
{
printf(" Playback Devices (%d)\n", playbackDeviceCount);
for (ma_uint32 iDevice = 0; iDevice < playbackDeviceCount; ++iDevice) {
printf(" %d: %s\n", iDevice, pPlaybackDeviceInfos[iDevice].name);
result = ma_context_get_device_info(pContext, ma_device_type_playback, &pPlaybackDeviceInfos[iDevice].id, ma_share_mode_shared, &pPlaybackDeviceInfos[iDevice]);
if (result == MA_SUCCESS) {
printf(" Name: %s\n", pPlaybackDeviceInfos[iDevice].name);
printf(" Min Channels: %d\n", pPlaybackDeviceInfos[iDevice].minChannels);
printf(" Max Channels: %d\n", pPlaybackDeviceInfos[iDevice].maxChannels);
printf(" Min Sample Rate: %d\n", pPlaybackDeviceInfos[iDevice].minSampleRate);
printf(" Max Sample Rate: %d\n", pPlaybackDeviceInfos[iDevice].maxSampleRate);
printf(" Format Count: %d\n", pPlaybackDeviceInfos[iDevice].formatCount);
for (ma_uint32 iFormat = 0; iFormat < pPlaybackDeviceInfos[iDevice].formatCount; ++iFormat) {
printf(" %s\n", ma_get_format_name(pPlaybackDeviceInfos[iDevice].formats[iFormat]));
}
} else {
printf(" ERROR\n");
}
}
printf(" Capture Devices (%d)\n", captureDeviceCount);
for (ma_uint32 iDevice = 0; iDevice < captureDeviceCount; ++iDevice) {
printf(" %d: %s\n", iDevice, pCaptureDeviceInfos[iDevice].name);
result = ma_context_get_device_info(pContext, ma_device_type_capture, &pCaptureDeviceInfos[iDevice].id, ma_share_mode_shared, &pCaptureDeviceInfos[iDevice]);
if (result == MA_SUCCESS) {
printf(" Name: %s\n", pCaptureDeviceInfos[iDevice].name);
printf(" Min Channels: %d\n", pCaptureDeviceInfos[iDevice].minChannels);
printf(" Max Channels: %d\n", pCaptureDeviceInfos[iDevice].maxChannels);
printf(" Min Sample Rate: %d\n", pCaptureDeviceInfos[iDevice].minSampleRate);
printf(" Max Sample Rate: %d\n", pCaptureDeviceInfos[iDevice].maxSampleRate);
printf(" Format Count: %d\n", pCaptureDeviceInfos[iDevice].formatCount);
for (ma_uint32 iFormat = 0; iFormat < pCaptureDeviceInfos[iDevice].formatCount; ++iFormat) {
printf(" %s\n", ma_get_format_name(pCaptureDeviceInfos[iDevice].formats[iFormat]));
}
} else {
printf(" ERROR\n");
}
}
}
done:
printf("\n");
return (result == MA_SUCCESS) ? 0 : -1;
}
int print_device_info(ma_device* pDevice)
{
printf("DEVICE NAME: %s\n", pDevice->name);
printf(" Format: %s -> %s\n", ma_get_format_name(pDevice->format), ma_get_format_name(pDevice->internalFormat));
printf(" Channels: %d -> %d\n", pDevice->channels, pDevice->internalChannels);
printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->internalSampleRate);
printf(" Buffer Size: %d\n", pDevice->bufferSizeInFrames);
printf(" Periods: %d\n", pDevice->periods);
return 0;
}
ma_uint32 on_send(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
{
ma_sine_wave* pSineWave = (ma_sine_wave*)pDevice->pUserData;
ma_assert(pSineWave != NULL);
float* pFramesOutF32 = (float*)pFramesOut;
for (ma_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) {
float sample;
ma_sine_wave_read(pSineWave, 1, &sample);
for (ma_uint32 iChannel = 0; iChannel < pDevice->channels; ++iChannel) {
pFramesOutF32[iChannel] = sample;
}
pFramesOutF32 += pDevice->channels;
}
return frameCount;
}
int main(int argc, char** argv)
{
ma_result result;
ma_sine_wave sineWave;
result = ma_sine_wave_init(0.2, 400, 44100, &sineWave);
if (result != MA_SUCCESS) {
printf("Failed to initialize sine wave.\n");
return -1;
}
// Separate context for this test.
ma_context_config contextConfig = ma_context_config_init(NULL); // <-- Don't need a log callback because we're using debug output instead.
ma_context context;
result = ma_context_init(NULL, 0, &contextConfig, &context);
if (result != MA_SUCCESS) {
printf("Failed to initialize context.\n");
return -1;
}
print_context_info(&context);
// Device.
ma_device_config deviceConfig = ma_device_config_init_playback(ma_format_f32, 2, 44100, on_send);
deviceConfig.bufferSizeInFrames = 32768;
ma_device device;
result = ma_device_init(&context, ma_device_type_playback, NULL, &deviceConfig, &sineWave, &device);
if (result != MA_SUCCESS) {
ma_context_uninit(&context);
printf("Failed to initialize device.\n");
return -1;
}
print_device_info(&device);
// Start playback.
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
ma_device_uninit(&device);
ma_context_uninit(&context);
printf("Failed to start device.\n");
return -1;
}
printf("Press Enter to quit...\n");
getchar();
ma_device_uninit(&device);
ma_context_uninit(&context);
return 0;
}
#define MA_DEBUG_OUTPUT
#define MA_USE_REFERENCE_CONVERSION_APIS
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
// Two converters are needed here. One for converting f32 samples from the sine wave generator to the input format,
// and another for converting the input format to the output format for device output.
ma_sine_wave sineWave;
ma_format_converter converterIn;
ma_format_converter converterOut;
ma_uint32 on_convert_samples_in(ma_format_converter* pConverter, ma_uint32 frameCount, void* pFrames, void* pUserData)
{
(void)pUserData;
ma_assert(pConverter->config.formatIn == ma_format_f32);
ma_sine_wave* pSineWave = (ma_sine_wave*)pConverter->config.pUserData;
ma_assert(pSineWave);
return (ma_uint32)ma_sine_wave_read_f32(pSineWave, frameCount, (float*)pFrames);
}
ma_uint32 on_convert_samples_out(ma_format_converter* pConverter, ma_uint32 frameCount, void* pFrames, void* pUserData)
{
(void)pUserData;
ma_format_converter* pConverterIn = (ma_format_converter*)pConverter->config.pUserData;
ma_assert(pConverterIn != NULL);
return (ma_uint32)ma_format_converter_read(pConverterIn, frameCount, pFrames, NULL);
}
void on_send_to_device__original(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_assert(pDevice->playback.format == ma_format_f32);
ma_assert(pDevice->playback.channels == 1);
ma_sine_wave_read_f32(&sineWave, frameCount, (float*)pOutput);
(void)pDevice;
(void)pInput;
}
void on_send_to_device__dithered(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
ma_assert(pDevice->playback.channels == 1);
ma_format_converter* pConverter = (ma_format_converter*)pDevice->pUserData;
ma_assert(pConverter != NULL);
ma_assert(pDevice->playback.format == pConverter->config.formatOut);
ma_format_converter_read(pConverter, frameCount, pOutput, NULL);
(void)pInput;
}
int do_dithering_test()
{
ma_device_config config;
ma_device device;
ma_result result;
config = ma_device_config_init(ma_device_type_playback);
config.playback.format = ma_format_f32;
config.playback.channels = 1;
config.sampleRate = 0;
config.dataCallback = on_send_to_device__original;
// We first play the sound the way it's meant to be played.
result = ma_device_init(NULL, &config, &device);
if (result != MA_SUCCESS) {
return -1;
}
ma_sine_wave_init(0.5, 400, device.sampleRate, &sineWave);
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
return -2;
}
printf("Press Enter to play enable dithering.\n");
getchar();
ma_device_uninit(&device);
ma_format srcFormat = ma_format_s24;
ma_format dstFormat = ma_format_u8;
ma_dither_mode ditherMode = ma_dither_mode_triangle;
ma_format_converter_config converterInConfig = ma_format_converter_config_init_new();
converterInConfig.formatIn = ma_format_f32; // <-- From the sine wave generator.
converterInConfig.formatOut = srcFormat;
converterInConfig.channels = config.playback.channels;
converterInConfig.ditherMode = ma_dither_mode_none;
converterInConfig.onRead = on_convert_samples_in;
converterInConfig.pUserData = &sineWave;
result = ma_format_converter_init(&converterInConfig, &converterIn);
if (result != MA_SUCCESS) {
return -3;
}
ma_format_converter_config converterOutConfig = ma_format_converter_config_init_new();
converterOutConfig.formatIn = srcFormat;
converterOutConfig.formatOut = dstFormat;
converterOutConfig.channels = config.playback.channels;
converterOutConfig.ditherMode = ditherMode;
converterOutConfig.onRead = on_convert_samples_out;
converterOutConfig.pUserData = &converterIn;
result = ma_format_converter_init(&converterOutConfig, &converterOut);
if (result != MA_SUCCESS) {
return -3;
}
config.playback.format = dstFormat;
config.dataCallback = on_send_to_device__dithered;
config.pUserData = &converterOut;
result = ma_device_init(NULL, &config, &device);
if (result != MA_SUCCESS) {
return -1;
}
// Now we play the sound after it's run through a dithered format converter.
ma_sine_wave_init(0.5, 400, device.sampleRate, &sineWave);
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
return -2;
}
printf("Press Enter to stop.\n");
getchar();
return 0;
}
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
do_dithering_test();
return 0;
}
\ No newline at end of file
// Just a simple test to check that MA_NO_DEVICE_IO compiles.
#include "../extras/dr_flac.h"
#include "../extras/dr_mp3.h"
#include "../extras/dr_wav.h"
#define MA_NO_DEVICE_IO
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
ma_result result = MA_ERROR;
ma_pcm_converter_config dspConfig = ma_pcm_converter_config_init_new();
ma_pcm_converter converter;
result = ma_pcm_converter_init(&dspConfig, &converter);
ma_decoder_config decoderConfig = ma_decoder_config_init(ma_format_unknown, 0, 0);
ma_decoder decoder;
result = ma_decoder_init_file("res/sine_s16_mono_48000.wav", &decoderConfig, &decoder);
return result;
}
#define DR_FLAC_IMPLEMENTATION
#include "../extras/dr_flac.h"
#define DR_MP3_IMPLEMENTATION
#include "../extras/dr_mp3.h"
#define DR_WAV_IMPLEMENTATION
#include "../extras/dr_wav.h"
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
typedef enum
{
simd_mode_scalar = 0,
simd_mode_sse2,
simd_mode_avx2,
simd_mode_avx512,
simd_mode_neon
} simd_mode;
const char* simd_mode_to_string(simd_mode mode)
{
switch (mode) {
case simd_mode_scalar: return "Reference";
case simd_mode_sse2: return "SSE2";
case simd_mode_avx2: return "AVX2";
case simd_mode_avx512: return "AVX-512";
case simd_mode_neon: return "NEON";
}
return "Unknown";
}
const char* ma_src_algorithm_to_string(ma_src_algorithm algorithm)
{
switch (algorithm) {
case ma_src_algorithm_none: return "Passthrough";
case ma_src_algorithm_linear: return "Linear";
case ma_src_algorithm_sinc: return "Sinc";
}
return "Unknown";
}
const char* ma_dither_mode_to_string(ma_dither_mode ditherMode)
{
switch (ditherMode) {
case ma_dither_mode_none: return "None";
case ma_dither_mode_rectangle: return "Rectangle";
case ma_dither_mode_triangle: return "Triangle";
}
return "Unkown";
}
///////////////////////////////////////////////////////////////////////////////
//
// Format Conversion
//
///////////////////////////////////////////////////////////////////////////////
typedef struct
{
void* pBaseData;
ma_uint64 sampleCount;
ma_uint64 iNextSample;
} format_conversion_data;
void pcm_convert__reference(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
{
switch (formatIn)
{
case ma_format_u8:
{
switch (formatOut)
{
case ma_format_s16: ma_pcm_u8_to_s16__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_u8_to_s24__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_u8_to_s32__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_u8_to_f32__reference(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s16:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s16_to_u8__reference( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s16_to_s24__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s16_to_s32__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s16_to_f32__reference(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s24:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s24_to_u8__reference( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s24_to_s16__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s24_to_s32__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s24_to_f32__reference(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s32_to_u8__reference( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s32_to_s16__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s32_to_s24__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s32_to_f32__reference(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_f32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_f32_to_u8__reference( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_f32_to_s16__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_f32_to_s24__reference(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_f32_to_s32__reference(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
default: break;
}
}
void pcm_convert__optimized(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
{
switch (formatIn)
{
case ma_format_u8:
{
switch (formatOut)
{
case ma_format_s16: ma_pcm_u8_to_s16__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_u8_to_s24__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_u8_to_s32__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_u8_to_f32__optimized(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s16:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s16_to_u8__optimized( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s16_to_s24__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s16_to_s32__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s16_to_f32__optimized(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s24:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s24_to_u8__optimized( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s24_to_s16__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s24_to_s32__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s24_to_f32__optimized(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s32_to_u8__optimized( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s32_to_s16__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s32_to_s24__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s32_to_f32__optimized(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_f32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_f32_to_u8__optimized( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_f32_to_s16__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_f32_to_s24__optimized(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_f32_to_s32__optimized(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
default: break;
}
}
#if defined(MA_SUPPORT_SSE2)
void pcm_convert__sse2(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
{
switch (formatIn)
{
case ma_format_u8:
{
switch (formatOut)
{
case ma_format_s16: ma_pcm_u8_to_s16__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_u8_to_s24__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_u8_to_s32__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_u8_to_f32__sse2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s16:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s16_to_u8__sse2( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s16_to_s24__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s16_to_s32__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s16_to_f32__sse2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s24:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s24_to_u8__sse2( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s24_to_s16__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s24_to_s32__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s24_to_f32__sse2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s32_to_u8__sse2( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s32_to_s16__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s32_to_s24__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s32_to_f32__sse2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_f32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_f32_to_u8__sse2( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_f32_to_s16__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_f32_to_s24__sse2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_f32_to_s32__sse2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
default: break;
}
}
#endif
#if defined(MA_SUPPORT_AVX2)
void pcm_convert__avx(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
{
switch (formatIn)
{
case ma_format_u8:
{
switch (formatOut)
{
case ma_format_s16: ma_pcm_u8_to_s16__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_u8_to_s24__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_u8_to_s32__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_u8_to_f32__avx2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s16:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s16_to_u8__avx2( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s16_to_s24__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s16_to_s32__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s16_to_f32__avx2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s24:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s24_to_u8__avx2( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s24_to_s16__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s24_to_s32__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s24_to_f32__avx2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s32_to_u8__avx2( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s32_to_s16__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s32_to_s24__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s32_to_f32__avx2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_f32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_f32_to_u8__avx2( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_f32_to_s16__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_f32_to_s24__avx2(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_f32_to_s32__avx2(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
default: break;
}
}
#endif
#if defined(MA_SUPPORT_AVX512)
void pcm_convert__avx512(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
{
switch (formatIn)
{
case ma_format_u8:
{
switch (formatOut)
{
case ma_format_s16: ma_pcm_u8_to_s16__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_u8_to_s24__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_u8_to_s32__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_u8_to_f32__avx512(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s16:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s16_to_u8__avx512( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s16_to_s24__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s16_to_s32__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s16_to_f32__avx512(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s24:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s24_to_u8__avx512( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s24_to_s16__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s24_to_s32__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s24_to_f32__avx512(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s32_to_u8__avx512( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s32_to_s16__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s32_to_s24__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s32_to_f32__avx512(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_f32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_f32_to_u8__avx512( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_f32_to_s16__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_f32_to_s24__avx512(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_f32_to_s32__avx512(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
default: break;
}
}
#endif
#if defined(MA_SUPPORT_NEON)
void pcm_convert__neon(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
{
switch (formatIn)
{
case ma_format_u8:
{
switch (formatOut)
{
case ma_format_s16: ma_pcm_u8_to_s16__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_u8_to_s24__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_u8_to_s32__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_u8_to_f32__neon(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s16:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s16_to_u8__neon( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s16_to_s24__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s16_to_s32__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s16_to_f32__neon(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s24:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s24_to_u8__neon( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s24_to_s16__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_s24_to_s32__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s24_to_f32__neon(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_s32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_s32_to_u8__neon( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_s32_to_s16__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_s32_to_s24__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_f32: ma_pcm_s32_to_f32__neon(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
case ma_format_f32:
{
switch (formatOut)
{
case ma_format_u8: ma_pcm_f32_to_u8__neon( pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s16: ma_pcm_f32_to_s16__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s24: ma_pcm_f32_to_s24__neon(pOut, pIn, sampleCount, ditherMode); return;
case ma_format_s32: ma_pcm_f32_to_s32__neon(pOut, pIn, sampleCount, ditherMode); return;
default: break;
}
} break;
default: break;
}
}
#endif
void pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode, simd_mode mode)
{
// For testing, we always reset the seed for dithering so we can get consistent results for comparisons.
ma_seed(1234);
switch (mode)
{
case simd_mode_scalar:
{
pcm_convert__optimized(pOut, formatOut, pIn, formatIn, sampleCount, ditherMode);
} break;
#if defined(MA_SUPPORT_SSE2)
case simd_mode_sse2:
{
pcm_convert__sse2(pOut, formatOut, pIn, formatIn, sampleCount, ditherMode);
} break;
#endif
#if defined(MA_SUPPORT_AVX2)
case simd_mode_avx2:
{
pcm_convert__avx(pOut, formatOut, pIn, formatIn, sampleCount, ditherMode);
} break;
#endif
#if defined(MA_SUPPORT_AVX512)
case simd_mode_avx512:
{
pcm_convert__avx512(pOut, formatOut, pIn, formatIn, sampleCount, ditherMode);
} break;
#endif
#if defined(MA_SUPPORT_NEON)
case simd_mode_neon:
{
pcm_convert__neon(pOut, formatOut, pIn, formatIn, sampleCount, ditherMode);
} break;
#endif
default: break;
}
}
int do_profiling__format_conversion__profile_individual(ma_format formatIn, ma_format formatOut, ma_dither_mode ditherMode, const void* pBaseData, ma_uint64 sampleCount, simd_mode mode, const void* pReferenceData, double referenceTime)
{
void* pTestData = ma_aligned_malloc((size_t)(sampleCount * ma_get_bytes_per_sample(formatOut)), MA_SIMD_ALIGNMENT);
if (pTestData == NULL) {
printf("Out of memory.\n");
return -1;
}
ma_timer timer;
ma_timer_init(&timer);
double timeTaken = ma_timer_get_time_in_seconds(&timer);
{
pcm_convert(pTestData, formatOut, pBaseData, formatIn, sampleCount, ditherMode, mode);
}
timeTaken = ma_timer_get_time_in_seconds(&timer) - timeTaken;
// Compare with the reference for correctness.
ma_bool32 passed = MA_TRUE;
for (ma_uint64 iSample = 0; iSample < sampleCount; ++iSample) {
ma_uint32 bps = ma_get_bytes_per_sample(formatOut);
// We need to compare on a format by format basis because we allow for very slight deviations in results depending on the output format.
switch (formatOut)
{
case ma_format_s16:
{
ma_int16 a = ((const ma_int16*)pReferenceData)[iSample];
ma_int16 b = ((const ma_int16*)pTestData)[iSample];
if (abs(a-b) > 0) {
printf("Incorrect Sample: (%d) %d != %d\n", (int)iSample, a, b);
passed = MA_FALSE;
}
} break;
default:
{
if (memcmp(ma_offset_ptr(pReferenceData, iSample*bps), ma_offset_ptr(pTestData, iSample*bps), bps) != 0) {
printf("Incorrect Sample: (%d)\n", (int)iSample);
passed = MA_FALSE;
}
} break;
}
}
if (passed) {
printf(" [PASSED] ");
} else {
printf(" [FAILED] ");
}
printf("(Dither = %s) %s -> %s (%s): %.4fms (%.2f%%)\n", ma_dither_mode_to_string(ditherMode), ma_get_format_name(formatIn), ma_get_format_name(formatOut), simd_mode_to_string(mode), timeTaken*1000, referenceTime/timeTaken*100);
ma_aligned_free(pTestData);
return 0;
}
int do_profiling__format_conversion__profile_set(ma_format formatIn, ma_format formatOut, ma_dither_mode ditherMode)
{
// Generate our base data to begin with. This is generated from an f32 sine wave which is converted to formatIn. That then becomes our base data.
ma_uint32 sampleCount = 10000000;
float* pSourceData = (float*)ma_aligned_malloc(sampleCount*sizeof(*pSourceData), MA_SIMD_ALIGNMENT);
if (pSourceData == NULL) {
printf("Out of memory.\n");
return -1;
}
ma_sine_wave sineWave;
ma_sine_wave_init(1.0, 400, 48000, &sineWave);
ma_sine_wave_read_f32(&sineWave, sampleCount, pSourceData);
void* pBaseData = ma_aligned_malloc(sampleCount * ma_get_bytes_per_sample(formatIn), MA_SIMD_ALIGNMENT);
ma_pcm_convert(pBaseData, formatIn, pSourceData, ma_format_f32, sampleCount, ma_dither_mode_none);
// Reference first so we can get a benchmark.
void* pReferenceData = ma_aligned_malloc(sampleCount * ma_get_bytes_per_sample(formatOut), MA_SIMD_ALIGNMENT);
ma_timer timer;
ma_timer_init(&timer);
double referenceTime = ma_timer_get_time_in_seconds(&timer);
{
pcm_convert__reference(pReferenceData, formatOut, pBaseData, formatIn, sampleCount, ditherMode);
}
referenceTime = ma_timer_get_time_in_seconds(&timer) - referenceTime;
// Here is where each optimized implementation is profiled.
do_profiling__format_conversion__profile_individual(formatIn, formatOut, ditherMode, pBaseData, sampleCount, simd_mode_scalar, pReferenceData, referenceTime);
if (ma_has_sse2()) {
do_profiling__format_conversion__profile_individual(formatIn, formatOut, ditherMode, pBaseData, sampleCount, simd_mode_sse2, pReferenceData, referenceTime);
}
if (ma_has_avx2()) {
do_profiling__format_conversion__profile_individual(formatIn, formatOut, ditherMode, pBaseData, sampleCount, simd_mode_avx2, pReferenceData, referenceTime);
}
if (ma_has_avx512f()) {
do_profiling__format_conversion__profile_individual(formatIn, formatOut, ditherMode, pBaseData, sampleCount, simd_mode_avx512, pReferenceData, referenceTime);
}
if (ma_has_neon()) {
do_profiling__format_conversion__profile_individual(formatIn, formatOut, ditherMode, pBaseData, sampleCount, simd_mode_neon, pReferenceData, referenceTime);
}
ma_aligned_free(pReferenceData);
ma_aligned_free(pBaseData);
ma_aligned_free(pSourceData);
return 0;
}
int do_profiling__format_conversion()
{
// First we need to generate our base data.
do_profiling__format_conversion__profile_set(ma_format_f32, ma_format_s16, ma_dither_mode_none);
return 0;
}
///////////////////////////////////////////////////////////////////////////////
//
// Channel Routing
//
///////////////////////////////////////////////////////////////////////////////
float g_ChannelRouterProfilingOutputBenchmark[8][48000];
float g_ChannelRouterProfilingOutput[8][48000];
double g_ChannelRouterTime_Reference = 0;
double g_ChannelRouterTime_SSE2 = 0;
double g_ChannelRouterTime_AVX2 = 0;
double g_ChannelRouterTime_AVX512 = 0;
double g_ChannelRouterTime_NEON = 0;
ma_sine_wave g_sineWave;
ma_bool32 channel_router_test(ma_uint32 channels, ma_uint64 frameCount, float** ppFramesA, float** ppFramesB)
{
for (ma_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
for (ma_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) {
if (ppFramesA[iChannel][iFrame] != ppFramesB[iChannel][iFrame]) {
return MA_FALSE;
}
}
}
return MA_TRUE;
}
ma_uint32 channel_router_on_read(ma_channel_router* pRouter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
{
(void)pUserData;
(void)pRouter;
float** ppSamplesOutF = (float**)ppSamplesOut;
for (ma_uint32 iChannel = 0; iChannel < pRouter->config.channelsIn; ++iChannel) {
ma_sine_wave_init(1/(iChannel+1), 400, 48000, &g_sineWave);
ma_sine_wave_read_f32(&g_sineWave, frameCount, ppSamplesOutF[iChannel]);
}
return frameCount;
}
int do_profiling__channel_routing()
{
ma_result result;
// When profiling we need to compare against a benchmark to ensure the optimization is implemented correctly. We always
// use the reference implementation for our benchmark.
ma_uint32 channels = ma_countof(g_ChannelRouterProfilingOutputBenchmark);
ma_channel channelMapIn[MA_MAX_CHANNELS];
ma_get_standard_channel_map(ma_standard_channel_map_default, channels, channelMapIn);
ma_channel channelMapOut[MA_MAX_CHANNELS];
ma_get_standard_channel_map(ma_standard_channel_map_default, channels, channelMapOut);
ma_channel_router_config routerConfig = ma_channel_router_config_init(channels, channelMapIn, channels, channelMapOut, ma_channel_mix_mode_planar_blend, channel_router_on_read, NULL);
ma_channel_router router;
result = ma_channel_router_init(&routerConfig, &router);
if (result != MA_SUCCESS) {
return -1;
}
// Disable optimizations for our tests.
router.isPassthrough = MA_FALSE;
router.isSimpleShuffle = MA_FALSE;
router.useSSE2 = MA_FALSE;
router.useAVX2 = MA_FALSE;
router.useAVX512 = MA_FALSE;
router.useNEON = MA_FALSE;
ma_uint64 framesToRead = ma_countof(g_ChannelRouterProfilingOutputBenchmark[0]);
// Benchmark
void* ppOutBenchmark[8];
for (int i = 0; i < 8; ++i) {
ppOutBenchmark[i] = (void*)g_ChannelRouterProfilingOutputBenchmark[i];
}
ma_sine_wave_init(1, 400, 48000, &g_sineWave);
ma_uint64 framesRead = ma_channel_router_read_deinterleaved(&router, framesToRead, ppOutBenchmark, NULL);
if (framesRead != framesToRead) {
printf("Channel Router: An error occurred while reading benchmark data.\n");
}
void* ppOut[8];
for (int i = 0; i < 8; ++i) {
ppOut[i] = (void*)g_ChannelRouterProfilingOutput[i];
}
printf("Channel Routing\n");
printf("===============\n");
// Reference
{
ma_timer timer;
ma_timer_init(&timer);
double startTime = ma_timer_get_time_in_seconds(&timer);
framesRead = ma_channel_router_read_deinterleaved(&router, framesToRead, ppOut, NULL);
if (framesRead != framesToRead) {
printf("Channel Router: An error occurred while reading reference data.\n");
}
if (!channel_router_test(channels, framesRead, (float**)ppOutBenchmark, (float**)ppOut)) {
printf(" [ERROR] ");
} else {
printf(" [PASSED] ");
}
g_ChannelRouterTime_Reference = ma_timer_get_time_in_seconds(&timer) - startTime;
printf("Reference: %.4fms (%.2f%%)\n", g_ChannelRouterTime_Reference*1000, g_ChannelRouterTime_Reference/g_ChannelRouterTime_Reference*100);
}
// SSE2
if (ma_has_sse2()) {
router.useSSE2 = MA_TRUE;
ma_timer timer;
ma_timer_init(&timer);
double startTime = ma_timer_get_time_in_seconds(&timer);
framesRead = ma_channel_router_read_deinterleaved(&router, framesToRead, ppOut, NULL);
if (framesRead != framesToRead) {
printf("Channel Router: An error occurred while reading SSE2 data.\n");
}
g_ChannelRouterTime_SSE2 = ma_timer_get_time_in_seconds(&timer) - startTime;
router.useSSE2 = MA_FALSE;
if (!channel_router_test(channels, framesRead, (float**)ppOutBenchmark, (float**)ppOut)) {
printf(" [ERROR] ");
} else {
printf(" [PASSED] ");
}
printf("SSE2: %.4fms (%.2f%%)\n", g_ChannelRouterTime_SSE2*1000, g_ChannelRouterTime_Reference/g_ChannelRouterTime_SSE2*100);
}
// AVX2
if (ma_has_avx2()) {
router.useAVX2 = MA_TRUE;
ma_timer timer;
ma_timer_init(&timer);
double startTime = ma_timer_get_time_in_seconds(&timer);
framesRead = ma_channel_router_read_deinterleaved(&router, framesToRead, ppOut, NULL);
if (framesRead != framesToRead) {
printf("Channel Router: An error occurred while reading AVX2 data.\n");
}
g_ChannelRouterTime_AVX2 = ma_timer_get_time_in_seconds(&timer) - startTime;
router.useAVX2 = MA_FALSE;
if (!channel_router_test(channels, framesRead, (float**)ppOutBenchmark, (float**)ppOut)) {
printf(" [ERROR] ");
} else {
printf(" [PASSED] ");
}
printf("AVX2: %.4fms (%.2f%%)\n", g_ChannelRouterTime_AVX2*1000, g_ChannelRouterTime_Reference/g_ChannelRouterTime_AVX2*100);
}
// NEON
if (ma_has_neon()) {
router.useNEON = MA_TRUE;
ma_timer timer;
ma_timer_init(&timer);
double startTime = ma_timer_get_time_in_seconds(&timer);
framesRead = ma_channel_router_read_deinterleaved(&router, framesToRead, ppOut, NULL);
if (framesRead != framesToRead) {
printf("Channel Router: An error occurred while reading NEON data.\n");
}
g_ChannelRouterTime_NEON = ma_timer_get_time_in_seconds(&timer) - startTime;
router.useNEON = MA_FALSE;
if (!channel_router_test(channels, framesRead, (float**)ppOutBenchmark, (float**)ppOut)) {
printf(" [ERROR] ");
} else {
printf(" [PASSED] ");
}
printf("NEON: %.4fms (%.2f%%)\n", g_ChannelRouterTime_NEON*1000, g_ChannelRouterTime_Reference/g_ChannelRouterTime_NEON*100);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
//
// SRC
//
///////////////////////////////////////////////////////////////////////////////
typedef struct
{
float* pFrameData[MA_MAX_CHANNELS];
ma_uint64 frameCount;
ma_uint32 channels;
double timeTaken;
} src_reference_data;
typedef struct
{
float* pFrameData[MA_MAX_CHANNELS];
ma_uint64 frameCount;
ma_uint64 iNextFrame;
ma_uint32 channels;
} src_data;
ma_uint32 do_profiling__src__on_read(ma_src* pSRC, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
{
src_data* pBaseData = (src_data*)pUserData;
ma_assert(pBaseData != NULL);
ma_assert(pBaseData->iNextFrame <= pBaseData->frameCount);
ma_uint64 framesToRead = frameCount;
ma_uint64 framesAvailable = pBaseData->frameCount - pBaseData->iNextFrame;
if (framesToRead > framesAvailable) {
framesToRead = framesAvailable;
}
if (framesToRead > 0) {
for (ma_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
ma_copy_memory(ppSamplesOut[iChannel], pBaseData->pFrameData[iChannel], (size_t)(framesToRead * sizeof(float)));
}
}
pBaseData->iNextFrame += framesToRead;
return (ma_uint32)framesToRead;
}
ma_result init_src(src_data* pBaseData, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_src_algorithm algorithm, simd_mode mode, ma_src* pSRC)
{
ma_assert(pBaseData != NULL);
ma_assert(pSRC != NULL);
ma_src_config srcConfig = ma_src_config_init(sampleRateIn, sampleRateOut, pBaseData->channels, do_profiling__src__on_read, pBaseData);
srcConfig.sinc.windowWidth = 17; // <-- Make this an odd number to test unaligned section in the SIMD implementations.
srcConfig.algorithm = algorithm;
srcConfig.noSSE2 = MA_TRUE;
srcConfig.noAVX2 = MA_TRUE;
srcConfig.noAVX512 = MA_TRUE;
srcConfig.noNEON = MA_TRUE;
switch (mode) {
case simd_mode_sse2: srcConfig.noSSE2 = MA_FALSE; break;
case simd_mode_avx2: srcConfig.noAVX2 = MA_FALSE; break;
case simd_mode_avx512: srcConfig.noAVX512 = MA_FALSE; break;
case simd_mode_neon: srcConfig.noNEON = MA_FALSE; break;
case simd_mode_scalar:
default: break;
}
ma_result result = ma_src_init(&srcConfig, pSRC);
if (result != MA_SUCCESS) {
printf("Failed to initialize sample rate converter.\n");
return (int)result;
}
return result;
}
int do_profiling__src__profile_individual(src_data* pBaseData, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_src_algorithm algorithm, simd_mode mode, src_reference_data* pReferenceData)
{
ma_assert(pBaseData != NULL);
ma_assert(pReferenceData != NULL);
ma_result result = MA_ERROR;
// Make sure the base data is moved back to the start.
pBaseData->iNextFrame = 0;
ma_src src;
result = init_src(pBaseData, sampleRateIn, sampleRateOut, algorithm, mode, &src);
if (result != MA_SUCCESS) {
return (int)result;
}
// Profiling.
ma_uint64 sz = pReferenceData->frameCount * sizeof(float);
ma_assert(sz <= SIZE_MAX);
float* pFrameData[MA_MAX_CHANNELS];
for (ma_uint32 iChannel = 0; iChannel < pBaseData->channels; iChannel += 1) {
pFrameData[iChannel] = (float*)ma_aligned_malloc((size_t)sz, MA_SIMD_ALIGNMENT);
if (pFrameData[iChannel] == NULL) {
printf("Out of memory.\n");
return -2;
}
ma_zero_memory(pFrameData[iChannel], (size_t)sz);
}
ma_timer timer;
ma_timer_init(&timer);
double startTime = ma_timer_get_time_in_seconds(&timer);
{
ma_src_read_deinterleaved(&src, pReferenceData->frameCount, (void**)pFrameData, pBaseData);
}
double timeTaken = ma_timer_get_time_in_seconds(&timer) - startTime;
// Correctness test.
ma_bool32 passed = MA_TRUE;
for (ma_uint32 iChannel = 0; iChannel < pReferenceData->channels; iChannel += 1) {
for (ma_uint32 iFrame = 0; iFrame < pReferenceData->frameCount; iFrame += 1) {
float s0 = pReferenceData->pFrameData[iChannel][iFrame];
float s1 = pFrameData[iChannel][iFrame];
//if (s0 != s1) {
if (fabs(s0 - s1) > 0.000001) {
printf("(Channel %d, Sample %d) %f != %f\n", iChannel, iFrame, s0, s1);
passed = MA_FALSE;
}
}
}
// Print results.
if (passed) {
printf(" [PASSED] ");
} else {
printf(" [FAILED] ");
}
printf("%s %d -> %d (%s): %.4fms (%.2f%%)\n", ma_src_algorithm_to_string(algorithm), sampleRateIn, sampleRateOut, simd_mode_to_string(mode), timeTaken*1000, pReferenceData->timeTaken/timeTaken*100);
for (ma_uint32 iChannel = 0; iChannel < pBaseData->channels; iChannel += 1) {
ma_aligned_free(pFrameData[iChannel]);
}
return (int)result;
}
int do_profiling__src__profile_set(src_data* pBaseData, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_src_algorithm algorithm)
{
ma_assert(pBaseData != NULL);
// Make sure the base data is back at the start.
pBaseData->iNextFrame = 0;
src_reference_data referenceData;
ma_zero_object(&referenceData);
referenceData.channels = pBaseData->channels;
// The first thing to do is to perform a sample rate conversion using the scalar/reference implementation. This reference is used to compare
// the results of the optimized implementation.
referenceData.frameCount = ma_calculate_frame_count_after_src(sampleRateOut, sampleRateIn, pBaseData->frameCount);
if (referenceData.frameCount == 0) {
printf("Failed to calculate output frame count.\n");
return -1;
}
ma_uint64 sz = referenceData.frameCount * sizeof(float);
ma_assert(sz <= SIZE_MAX);
for (ma_uint32 iChannel = 0; iChannel < referenceData.channels; iChannel += 1) {
referenceData.pFrameData[iChannel] = (float*)ma_aligned_malloc((size_t)sz, MA_SIMD_ALIGNMENT);
if (referenceData.pFrameData[iChannel] == NULL) {
printf("Out of memory.\n");
return -2;
}
ma_zero_memory(referenceData.pFrameData[iChannel], (size_t)sz);
}
// Generate the reference data.
ma_src src;
ma_result result = init_src(pBaseData, sampleRateIn, sampleRateOut, algorithm, simd_mode_scalar, &src);
if (result != MA_SUCCESS) {
return (int)result;
}
ma_timer timer;
ma_timer_init(&timer);
double startTime = ma_timer_get_time_in_seconds(&timer);
{
ma_src_read_deinterleaved(&src, referenceData.frameCount, (void**)referenceData.pFrameData, pBaseData);
}
referenceData.timeTaken = ma_timer_get_time_in_seconds(&timer) - startTime;
// Now that we have the reference data to compare against we can go ahead and measure the SIMD optimizations.
do_profiling__src__profile_individual(pBaseData, sampleRateIn, sampleRateOut, algorithm, simd_mode_scalar, &referenceData);
if (ma_has_sse2()) {
do_profiling__src__profile_individual(pBaseData, sampleRateIn, sampleRateOut, algorithm, simd_mode_sse2, &referenceData);
}
if (ma_has_avx2()) {
do_profiling__src__profile_individual(pBaseData, sampleRateIn, sampleRateOut, algorithm, simd_mode_avx2, &referenceData);
}
if (ma_has_avx512f()) {
do_profiling__src__profile_individual(pBaseData, sampleRateIn, sampleRateOut, algorithm, simd_mode_avx512, &referenceData);
}
if (ma_has_neon()) {
do_profiling__src__profile_individual(pBaseData, sampleRateIn, sampleRateOut, algorithm, simd_mode_neon, &referenceData);
}
for (ma_uint32 iChannel = 0; iChannel < referenceData.channels; iChannel += 1) {
ma_aligned_free(referenceData.pFrameData[iChannel]);
}
return 0;
}
int do_profiling__src()
{
printf("Sample Rate Conversion\n");
printf("======================\n");
// Set up base data.
src_data baseData;
ma_zero_object(&baseData);
baseData.channels = 8;
baseData.frameCount = 100000;
for (ma_uint32 iChannel = 0; iChannel < baseData.channels; ++iChannel) {
baseData.pFrameData[iChannel] = (float*)ma_aligned_malloc((size_t)(baseData.frameCount * sizeof(float)), MA_SIMD_ALIGNMENT);
if (baseData.pFrameData[iChannel] == NULL) {
printf("Out of memory.\n");
return -1;
}
ma_sine_wave sineWave;
ma_sine_wave_init(1.0f, 400 + (iChannel*50), 48000, &sineWave);
ma_sine_wave_read_f32(&sineWave, baseData.frameCount, baseData.pFrameData[iChannel]);
}
// Upsampling.
do_profiling__src__profile_set(&baseData, 44100, 48000, ma_src_algorithm_sinc);
// Downsampling.
do_profiling__src__profile_set(&baseData, 48000, 44100, ma_src_algorithm_sinc);
for (ma_uint32 iChannel = 0; iChannel < baseData.channels; iChannel += 1) {
ma_aligned_free(baseData.pFrameData[iChannel]);
}
return 0;
}
#if 0
// Converts two 4xf32 vectors to one 8xi16 vector with signed saturation.
__m128i drmath_vf32_to_vi16__sse2(__m128 f32_0, __m128 f32_1)
{
return _mm_packs_epi32(_mm_cvttps_epi32(f32_0), _mm_cvttps_epi32(f32_1));
}
__m256i drmath_vf32_to_vi16__avx(__m256 f32_0, __m256 f32_1)
{
__m256i i0 = _mm256_cvttps_epi32(f32_0);
__m256i i1 = _mm256_cvttps_epi32(f32_1);
__m256i p0 = _mm256_permute2x128_si256(i0, i1, 32);
__m256i p1 = _mm256_permute2x128_si256(i0, i1, 49);
__m256i r = _mm256_packs_epi32(p0, p1);
return r;
}
#endif
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
{
//__m128 f0 = _mm_set_ps(32780, 2, 1, 0);
//__m128 f1 = _mm_set_ps(-32780, 6, 5, 4);
//__m128i r = drmath_vf32_to_vi16__sse2(f0, f1);
//__m256 f0 = _mm256_set_ps(7, 6, 5, 4, 3, 2, 1, 0);
//__m256 f1 = _mm256_set_ps(15, 14, 13, 12, 11, 10, 9, 8);
//__m256i r = drmath_vf32_to_vi16__avx(f0, f1);
//
//int a = 5;
}
// Summary.
if (ma_has_sse2()) {
printf("Has SSE2: YES\n");
} else {
printf("Has SSE2: NO\n");
}
if (ma_has_avx2()) {
printf("Has AVX2: YES\n");
} else {
printf("Has AVX2: NO\n");
}
if (ma_has_avx512f()) {
printf("Has AVX-512F: YES\n");
} else {
printf("Has AVX-512F: NO\n");
}
if (ma_has_neon()) {
printf("Has NEON: YES\n");
} else {
printf("Has NEON: NO\n");
}
printf("\n");
// Format conversion.
do_profiling__format_conversion();
printf("\n\n");
// Channel routing.
do_profiling__channel_routing();
printf("\n\n");
// Sample rate conversion.
do_profiling__src();
printf("\n\n");
printf("Press any key to quit...\n");
getchar();
return 0;
}
\ No newline at end of file
#define MA_NO_SSE2
#define MA_NO_AVX2
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
// There is a usage pattern for resampling that miniaudio does not properly support which is where the client continuously
// reads samples until ma_src_read() returns 0. The problem with this pattern is that is consumes the samples sitting
// in the window which are needed to compute the next samples in future calls to ma_src_read() (assuming the client
// has re-filled the resampler's input data).
/*
for (;;) {
fill_src_input_data(&src, someData);
float buffer[4096]
while ((framesRead = ma_src_read(&src, ...) != 0) {
do_something_with_resampled_data(buffer);
}
}
*/
// In the use case above, the very last samples that are read from ma_src_read() will not have future samples to draw
// from in order to calculate the correct interpolation factor which in turn results in crackling.
ma_uint32 sampleRateIn = 0;
ma_uint32 sampleRateOut = 0;
ma_sine_wave sineWave; // <-- This is the source data.
ma_src src;
float srcInput[1024];
ma_uint32 srcNextSampleIndex = ma_countof(srcInput);
void reload_src_input()
{
ma_sine_wave_read_f32(&sineWave, ma_countof(srcInput), srcInput);
srcNextSampleIndex = 0;
}
ma_uint32 on_src(ma_src* pSRC, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
{
ma_assert(pSRC != NULL);
ma_assert(pSRC->config.channels == 1);
(void)pUserData;
// Only read as much as is available in the input buffer. Do not reload the buffer here.
ma_uint32 framesAvailable = ma_countof(srcInput) - srcNextSampleIndex;
ma_uint32 framesToRead = frameCount;
if (framesToRead > framesAvailable) {
framesToRead = framesAvailable;
}
ma_copy_memory(ppSamplesOut[0], srcInput + srcNextSampleIndex, sizeof(float)*framesToRead);
srcNextSampleIndex += framesToRead;
return framesToRead;
}
void on_send_to_device(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
(void)pDevice;
(void)pInput;
ma_assert(pDevice->playback.format == ma_format_f32);
ma_assert(pDevice->playback.channels == 1);
float* pFramesF32 = (float*)pOutput;
// To reproduce the case we are needing to test, we need to read from the SRC in a very specific way. We keep looping
// until we've read the requested frame count, however we have an inner loop that keeps running until ma_src_read()
// returns 0, in which case we need to reload the SRC's input data and keep going.
ma_uint32 totalFramesRead = 0;
while (totalFramesRead < frameCount) {
ma_uint32 framesRemaining = frameCount - totalFramesRead;
ma_uint32 maxFramesToRead = 128;
ma_uint32 framesToRead = framesRemaining;
if (framesToRead > maxFramesToRead) {
framesToRead = maxFramesToRead;
}
ma_uint32 framesRead = (ma_uint32)ma_src_read_deinterleaved(&src, framesToRead, (void**)&pFramesF32, NULL);
if (framesRead == 0) {
reload_src_input();
}
totalFramesRead += framesRead;
pFramesF32 += framesRead;
}
ma_assert(totalFramesRead == frameCount);
}
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
ma_device_config config = ma_device_config_init(ma_device_type_playback);
config.playback.format = ma_format_f32;
config.playback.channels = 1;
config.dataCallback = on_send_to_device;
ma_device device;
ma_result result;
config.bufferSizeInFrames = 8192*1;
// We first play the sound the way it's meant to be played.
result = ma_device_init(NULL, &config, &device);
if (result != MA_SUCCESS) {
return -1;
}
// For this test, we need the sine wave to be a different format to the device.
sampleRateOut = device.sampleRate;
sampleRateIn = (sampleRateOut == 44100) ? 48000 : 44100;
ma_sine_wave_init(0.2, 400, sampleRateIn, &sineWave);
ma_src_config srcConfig = ma_src_config_init(sampleRateIn, sampleRateOut, 1, on_src, NULL);
srcConfig.algorithm = ma_src_algorithm_sinc;
srcConfig.neverConsumeEndOfInput = MA_TRUE;
result = ma_src_init(&srcConfig, &src);
if (result != MA_SUCCESS) {
printf("Failed to create SRC.\n");
return -1;
}
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
return -2;
}
printf("Press Enter to quit...\n");
getchar();
ma_device_uninit(&device);
return 0;
}
#include <stdio.h>
#define MINIAUDIO_IMPLEMENTATION
#include "../miniaudio.h"
ma_sine_wave sineWave;
ma_uint32 framesWritten;
ma_event stopEvent;
ma_bool32 isInitialRun = MA_TRUE;
void on_stop(ma_device* pDevice)
{
(void)pDevice;
printf("STOPPED\n");
}
void on_data(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{
(void)pInput; /* Not used yet. */
/* Output exactly one second of data. Pad the end with silence. */
ma_uint32 framesRemaining = pDevice->sampleRate - framesWritten;
ma_uint32 framesToProcess = frameCount;
if (framesToProcess > framesRemaining && isInitialRun) {
framesToProcess = framesRemaining;
}
ma_sine_wave_read_f32_ex(&sineWave, framesToProcess, pDevice->playback.channels, ma_stream_layout_interleaved, (float**)&pOutput);
if (isInitialRun) {
framesWritten += framesToProcess;
}
ma_assert(framesWritten <= pDevice->sampleRate);
if (framesWritten >= pDevice->sampleRate) {
if (isInitialRun) {
printf("STOPPING [AUDIO THREAD]...\n");
ma_event_signal(&stopEvent);
isInitialRun = MA_FALSE;
}
}
}
int main(int argc, char** argv)
{
ma_result result;
(void)argc;
(void)argv;
ma_backend backend = ma_backend_wasapi;
ma_sine_wave_init(0.25, 400, 44100, &sineWave);
ma_device_config config = ma_device_config_init(ma_device_type_playback);
config.playback.format = ma_format_f32;
config.playback.channels = 2;
config.sampleRate = 44100;
config.dataCallback = on_data;
config.stopCallback = on_stop;
config.bufferSizeInFrames = 16384;
ma_device device;
result = ma_device_init_ex(&backend, 1, NULL, &config, &device);
if (result != MA_SUCCESS) {
printf("Failed to initialize device.\n");
return result;
}
result = ma_event_init(device.pContext, &stopEvent);
if (result != MA_SUCCESS) {
printf("Failed to initialize stop event.\n");
return result;
}
ma_device_start(&device);
/* We wait for the stop event, stop the device, then ask the user to press any key to restart. This checks that the device can restart after stopping. */
ma_event_wait(&stopEvent);
printf("STOPPING [MAIN THREAD]...\n");
ma_device_stop(&device);
printf("Press Enter to restart...\n");
getchar();
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
printf("Failed to restart the device.\n");
ma_device_uninit(&device);
return -1;
}
printf("Press Enter to quit...\n");
getchar();
ma_device_uninit(&device);
return 0;
}
\ No newline at end of file
<html>
<head>
</head>
<body>
This is a test of the Web Audio API
<div id="Error" style="color:red">
</div>
<div><b>Playback Devices</b></div>
<div id="PlaybackDevices">
[Playback Devices]
</div>
<div><b>Capture Devices</b></div>
<div id="CaptureDevices">
[Capture Devices]
</div>
<button id="btnStartPlayback">
Start Playback
</button>
<button id="btnStopPlayback">
Stop Playback
</button>
<button id="btnClosePlayback">
Close Playback
</button>
<br/>
<button id="btnStartCapture">
Start Capture
</button>
<button id="btnStopCapture">
Stop Capture
</button>
<button id="btnCloseCapture">
Close Capture
</button>
<script>
var runningTime = 0.0;
function ma_enum_devices(deviceType) {
if (deviceType !== 'audiooutput' && deviceType !== 'audioinput') {
alert("Invalid device type: " + deviceType);
return null;
}
// Unfortunately the navigator.mediaDevices.enumerateDevices() API doesn't seem to be very well supported.
// 1) On Chrome and Opera the label is always an empty string
// 2) No devices are returned in Firefox.
//
// This is not a production quality solution right now. It's better to instead just assume default
// devices and output/input silence if they fail to open (if a microphone is not connected, for example).
var promise = new Promise(function(resolve, reject) {
var devices = [];
navigator.mediaDevices.enumerateDevices().then(function(deviceInfos) {
for (var i = 0; i < deviceInfos.length; ++i) {
if (deviceInfos[i].kind === deviceType) {
devices.push(deviceInfos[i]);
}
}
resolve(devices);
}).catch(function(error) {
reject("Failed to enumerate devices: " + error);
});
});
return promise;
}
function ma_device_new(deviceType, deviceID) {
if (typeof(mal) === 'undefined') {
return null; // Context not initialized.
}
// For now only default devices are being used. The device ID needs to be null.
if (deviceID != null) {
return null; // Only default devices are currently supported.
}
if (deviceID == null) {
deviceID = "";
}
var bufferSizeInFrames = 512;
var sampleRate = 44100;
var channelCount = 2;
var device = {};
device.webaudioContext = new (window.AudioContext || window.webkitAudioContext)({
latencyHint: 'interactive',
sampleRate: sampleRate,
});
device.webaudioContext.suspend(); // miniaudio always starts it's devices in a stopped state.
console.log("Sample Rate: " + device.webaudioContext.sampleRate);
device.intermediaryBufferSizeInBytes = channelCount * bufferSizeInFrames * 4;
//device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes);
device.intermediaryBuffer = new Float32Array(channelCount * bufferSizeInFrames);
if (deviceType == 'audiooutput') {
device.playback = {};
device.playback.scriptNode = device.webaudioContext.createScriptProcessor(
bufferSizeInFrames,
channelCount,
channelCount
);
device.playback.scriptNode.onaudioprocess = function(e) {
// TODO: Don't do anything if we don't have an intermediary buffer. This means the device
// was uninitialized.
// The buffer we give to the client needs to be interleaved. After the client callback has returned
// we deinterleave it.
var requiredBufferLength = channelCount * e.outputBuffer.length;
if (device.intermediaryBuffer.length < requiredBufferLength) {
device.intermediaryBuffer = new Float32Array(requiredBufferLength);
}
// Here is where we get the client to fill the buffer with audio data.
// TESTING: Output a sine wave to the speakers.
for (var iFrame = 0; iFrame < e.outputBuffer.length; ++iFrame) {
var value = Math.sin((runningTime+(iFrame*6.28318530717958647693/44100.0)) * 400.0) * 0.25;
for (var iChannel = 0; iChannel < channelCount; ++iChannel) {
device.intermediaryBuffer[iFrame*channelCount + iChannel] = value;
}
}
runningTime += (6.28318530717958647693*e.outputBuffer.length) / 44100.0;
// At this point the intermediary buffer should be filled with data. We now need to deinterleave
// it and write it to the output buffer.
for (var iChannel = 0; iChannel < channelCount; ++iChannel) {
for (var iFrame = 0; iFrame < e.outputBuffer.length; ++iFrame) {
e.outputBuffer.getChannelData(iChannel)[iFrame] = device.intermediaryBuffer[iFrame*channelCount + iChannel];
}
}
};
device.playback.scriptNode.connect(device.webaudioContext.destination);
} else if (deviceType == 'audioinput') {
device.capture = {};
navigator.mediaDevices.getUserMedia({audio:true, video:false})
.then(function(stream) {
// We need to use ScriptProcessorNode instead of MediaRecorder because we need raw PCM data
// rather than compressed data. Why is this not supported? Seriously...
//
// This way this works is that we connect the output of a MediaStreamSourceNode to the input
// of a ScriptProcessorNode. The ScriptProcessorNode is connected to the AudioContext
// destination, but instead of connecting the input to the output we just output silence.
device.capture.streamNode = device.webaudioContext.createMediaStreamSource(stream);
device.capture.scriptNode = device.webaudioContext.createScriptProcessor(
bufferSizeInFrames,
channelCount,
channelCount
);
device.capture.scriptNode.onaudioprocess = function(e) {
// The input buffer needs to be interleaved before sending to the client. We need to do
// this in an intermediary buffer.
var requiredBufferLength = e.inputBuffer.numberOfChannels * e.inputBuffer.length;
if (device.intermediaryBuffer.length < requiredBufferLength) {
device.intermediaryBuffer = new Float32Array(requiredBufferLength);
}
for (var iFrame = 0; iFrame < e.inputBuffer.length; ++iFrame) {
for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
device.intermediaryBuffer[iFrame*e.inputBuffer.numberOfChannels + iChannel] = e.inputBuffer.getChannelData(iChannel)[iFrame];
}
}
// At this point the input data has been interleaved and can be passed on to the client.
// Always output silence.
for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
e.outputBuffer.getChannelData(iChannel).fill(0.0);
}
/*
// TESTING: Write to the interleaved data to the output buffers.
for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
for (var iFrame = 0; iFrame < e.inputBuffer.length; ++iFrame) {
e.outputBuffer.getChannelData(iChannel)[iFrame] = device.intermediaryBuffer[iFrame*e.inputBuffer.numberOfChannels + iChannel];
}
}
*/
};
device.capture.streamNode.connect(device.capture.scriptNode);
device.capture.scriptNode.connect(device.webaudioContext.destination);
})
.catch(function(error) {
// For now just do nothing, but later on we may want to periodically fire the callback with silence.
console.log("No Stream.");
});
} else {
return null; // Unknown device type.
}
return device;
}
function ma_device_delete(device) {
Module._free(device.intermediaryBuffer);
}
function ma_context_init() {
if ((window.AudioContext || window.webkitAudioContext) === undefined) {
return 0; // Web Audio not supported.
}
if (typeof(Float32Array) === 'undefined') {
return 0; // Float32Array not supported.
}
if (typeof(mal) === 'undefined') {
mal = {};
miniaudio.devices = []; // Device cache for mapping devices to indexes for JavaScript/C interop.
// Returns the index of the device. Throws an exception on error.
miniaudio.track_device = function(device) {
// Try inserting into a free slot first.
for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
if (miniaudio.devices[iDevice] == null) {
miniaudio.devices[iDevice] = device;
return iDevice;
}
}
// Getting here means there is no empty slots in the array so we just push to the end.
miniaudio.devices.push(device);
return miniaudio.devices.length - 1;
};
miniaudio.untrack_device_by_index = function(deviceIndex) {
// We just set the device's slot to null. The slot will get reused in the next call to ma_track_device.
miniaudio.devices[iDevice] = null;
// Trim the array if possible.
while (miniaudio.devices.length > 0) {
if (miniaudio.devices[miniaudio.devices.length-1] == null) {
miniaudio.devices.pop();
} else {
break;
}
}
};
miniaudio.untrack_device = function(device) {
for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
if (miniaudio.devices[iDevice] == device) {
return miniaudio.untrack_device_by_index(iDevice);
}
}
};
miniaudio.get_device_by_index = function(deviceIndex) {
return miniaudio.devices[deviceIndex];
};
}
return 1;
}
window.onload = function() {
if (ma_context_init() != 1) {
alert("Failed to initialize context.");
return;
}
// Unfortunately this doesn't seem to work too well. See comment in ma_enum_devices().
ma_enum_devices('audiooutput').then(function(outputDevices) {
for (var iDevice = 0; iDevice < outputDevices.length; ++iDevice) {
console.log("Output Device: ", JSON.stringify(outputDevices[iDevice]));
}
}).catch(function(error) {
console.log("Failed to retrieve output devices: ", error);
});
ma_enum_devices('audioinput').then(function(inputDevices) {
for (var iDevice = 0; iDevice < inputDevices.length; ++iDevice) {
console.log("Input Device: ", JSON.stringify(inputDevices[iDevice]));
}
}).catch(function(error) {
console.log("Failed to retrieve input devices: ", error);
});
var outputDevice = ma_device_new('audiooutput', null);
var inputDevice = ma_device_new('audioinput', null);
var btnStartPlayback = document.getElementById("btnStartPlayback");
btnStartPlayback.addEventListener('click', function() {
outputDevice.webaudioContext.resume();
});
var btnStopPlayback = document.getElementById("btnStopPlayback");
btnStopPlayback.addEventListener('click', function() {
outputDevice.webaudioContext.suspend();
});
var btnClosePlayback = document.getElementById("btnClosePlayback");
btnClosePlayback.addEventListener('click', function() {
outputDevice.webaudioContext.close();
});
var btnStartCapture = document.getElementById("btnStartCapture");
btnStartCapture.addEventListener('click', function() {
inputDevice.webaudioContext.resume();
});
var btnStopCapture = document.getElementById("btnStopCapture");
btnStopCapture.addEventListener('click', function() {
inputDevice.webaudioContext.suspend();
});
var btnCloseCapture = document.getElementById("btnCloseCapture");
btnCloseCapture.addEventListener('click', function() {
inputDevice.webaudioContext.close();
});
}
</script>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment