mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Delete old tests.
This commit is contained in:
@@ -1,175 +0,0 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -1,148 +0,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;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// 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"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,143 +0,0 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -1,98 +0,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;
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user