add a test for underflow handling and expose os API

This commit is contained in:
Andrew Kelley 2015-07-30 12:07:58 -07:00
parent 6a439dffb9
commit 2a70bdb745
12 changed files with 192 additions and 14 deletions

View file

@ -68,6 +68,7 @@ set(LIBSOUNDIO_SOURCES
set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h") set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")
set(LIBSOUNDIO_HEADERS set(LIBSOUNDIO_HEADERS
"${CMAKE_SOURCE_DIR}/src/soundio.h" "${CMAKE_SOURCE_DIR}/src/soundio.h"
"${CMAKE_SOURCE_DIR}/src/os.h"
${CONFIGURE_OUT_FILE} ${CONFIGURE_OUT_FILE}
) )
@ -214,6 +215,14 @@ set_target_properties(unit_tests PROPERTIES
include_directories(${TEST_INCLUDES}) include_directories(${TEST_INCLUDES})
add_test(UnitTests unit_tests) add_test(UnitTests unit_tests)
add_executable(underflow test/underflow.c)
set_target_properties(underflow PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(underflow libsoundio_shared)
add_custom_target(coverage add_custom_target(coverage
DEPENDS unit_tests DEPENDS unit_tests

View file

@ -249,10 +249,6 @@ view `coverage/index.html` in a browser.
0. Verify that JACK xrun callback context is the same as process callback. 0. Verify that JACK xrun callback context is the same as process callback.
If not, might need to hav xrun callback set a flag and have process callback If not, might need to hav xrun callback set a flag and have process callback
call the underflow callback. call the underflow callback.
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
0. Instead fo open(), start(), pause(), open() starts it and it starts paused.
0. Create a test for underflow handling. It just makes a sine wave for 5
seconds, then on next audio callback, sleep for 2 seconds.
0. Create a test for pausing and resuming input and output streams. 0. Create a test for pausing and resuming input and output streams.
0. Create a test for the latency / synchronization API. 0. Create a test for the latency / synchronization API.
- Input is an audio file and some events indexed at particular frame - when - Input is an audio file and some events indexed at particular frame - when
@ -292,6 +288,7 @@ view `coverage/index.html` in a browser.
`soundio_outstream_start` is called, switch to the real callback, then call `soundio_outstream_start` is called, switch to the real callback, then call
`pa_stream_flush`, then uncork the stream. `pa_stream_flush`, then uncork the stream.
0. In ALSA do we need to wake up the poll when destroying the in or out stream? 0. In ALSA do we need to wake up the poll when destroying the in or out stream?
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
## Planned Uses for libsoundio ## Planned Uses for libsoundio

View file

@ -9,7 +9,7 @@
#define SOUNDIO_ALSA_HPP #define SOUNDIO_ALSA_HPP
#include "soundio.h" #include "soundio.h"
#include "os.hpp" #include "os.h"
#include "atomics.hpp" #include "atomics.hpp"
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>

View file

@ -9,7 +9,7 @@
#define SOUNDIO_DUMMY_HPP #define SOUNDIO_DUMMY_HPP
#include "soundio.h" #include "soundio.h"
#include "os.hpp" #include "os.h"
#include "atomics.hpp" #include "atomics.hpp"
#include "ring_buffer.hpp" #include "ring_buffer.hpp"

View file

@ -9,7 +9,7 @@
#define SOUNDIO_JACK_HPP #define SOUNDIO_JACK_HPP
#include "soundio.h" #include "soundio.h"
#include "os.hpp" #include "os.h"
#include "atomics.hpp" #include "atomics.hpp"
#include <jack/jack.h> #include <jack/jack.h>

View file

@ -5,7 +5,7 @@
* See http://opensource.org/licenses/MIT * See http://opensource.org/licenses/MIT
*/ */
#include "os.hpp" #include "os.h"
#include "soundio.h" #include "soundio.h"
#include "util.hpp" #include "util.hpp"
#include "atomics.hpp" #include "atomics.hpp"

View file

@ -5,12 +5,18 @@
* See http://opensource.org/licenses/MIT * See http://opensource.org/licenses/MIT
*/ */
#ifndef SOUNDIO_OS_HPP #ifndef SOUNDIO_OS_H
#define SOUNDIO_OS_HPP #define SOUNDIO_OS_H
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
// safe to call from any thread(s) multiple times, but // safe to call from any thread(s) multiple times, but
// must be called at least once before calling any other os functions // must be called at least once before calling any other os functions
// soundio_create calls this function. // soundio_create calls this function.
@ -63,4 +69,8 @@ struct SoundIoOsMirroredMemory {
int soundio_os_init_mirrored_memory(struct SoundIoOsMirroredMemory *mem, size_t capacity); int soundio_os_init_mirrored_memory(struct SoundIoOsMirroredMemory *mem, size_t capacity);
void soundio_os_deinit_mirrored_memory(struct SoundIoOsMirroredMemory *mem); void soundio_os_deinit_mirrored_memory(struct SoundIoOsMirroredMemory *mem);
#ifdef __cplusplus
}
#endif
#endif #endif

View file

@ -8,7 +8,6 @@
#include "ring_buffer.hpp" #include "ring_buffer.hpp"
#include "soundio.hpp" #include "soundio.hpp"
#include "util.hpp" #include "util.hpp"
#include "os.hpp"
#include <stdlib.h> #include <stdlib.h>

View file

@ -9,7 +9,7 @@
#define SOUNDIO_RING_BUFFER_HPP #define SOUNDIO_RING_BUFFER_HPP
#include "atomics.hpp" #include "atomics.hpp"
#include "os.hpp" #include "os.h"
struct SoundIoRingBuffer { struct SoundIoRingBuffer {
SoundIoOsMirroredMemory mem; SoundIoOsMirroredMemory mem;

View file

@ -7,7 +7,7 @@
#include "soundio.hpp" #include "soundio.hpp"
#include "util.hpp" #include "util.hpp"
#include "os.hpp" #include "os.h"
#include "config.h" #include "config.h"
#include <string.h> #include <string.h>

163
test/underflow.c Normal file
View file

@ -0,0 +1,163 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include <soundio.h>
#include <os.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
__attribute__ ((cold))
__attribute__ ((noreturn))
__attribute__ ((format (printf, 1, 2)))
static void panic(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
abort();
}
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [--dummy] [--alsa] [--pulseaudio] [--jack]\n", exe);
return 1;
}
static const float PI = 3.1415926535f;
static float seconds_offset = 0.0f;
static bool caused_underflow = false;
static struct SoundIoOsCond *cond = NULL;
static struct SoundIo *soundio = NULL;
static float seconds_end = 9.0f;
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
float float_sample_rate = outstream->sample_rate;
float seconds_per_frame = 1.0f / float_sample_rate;
int err;
if (!caused_underflow && seconds_offset >= 3.0f) {
caused_underflow = true;
soundio_os_cond_timed_wait(cond, NULL, 3.0);
}
if (seconds_offset >= seconds_end) {
soundio_wakeup(soundio);
return;
}
for (;;) {
int frame_count = requested_frame_count;
struct SoundIoChannelArea *areas;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
panic("%s", soundio_strerror(err));
if (!frame_count)
break;
const struct SoundIoChannelLayout *layout = &outstream->layout;
float pitch = 440.0f;
float radians_per_second = pitch * 2.0f * PI;
for (int frame = 0; frame < frame_count; frame += 1) {
float sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
for (int channel = 0; channel < layout->channel_count; channel += 1) {
float *ptr = (float*)(areas[channel].ptr + areas[channel].step * frame);
*ptr = sample;
}
}
seconds_offset += seconds_per_frame * frame_count;
if ((err = soundio_outstream_end_write(outstream, frame_count))) {
if (err == SoundIoErrorUnderflow)
return;
panic("%s", soundio_strerror(err));
}
requested_frame_count -= frame_count;
if (requested_frame_count <= 0)
break;
}
}
static void underflow_callback(struct SoundIoOutStream *outstream) {
static int count = 0;
fprintf(stderr, "underflow %d\n", count++);
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (strcmp("--dummy", arg) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("--alsa", arg) == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp("--pulseaudio", arg) == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp("--jack", arg) == 0) {
backend = SoundIoBackendJack;
} else {
return usage(exe);
}
}
fprintf(stderr, "You should hear a sine wave for 3 seconds, then some period of silence or glitches,\n"
"then you should see at least one buffer underflow message, then hear a sine\n"
"wave for 3 seconds, then the program should exit successfully.\n");
if (!(soundio = soundio_create()))
panic("out of memory");
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err)
panic("error connecting: %s", soundio_strerror(err));
int default_out_device_index = soundio_default_output_device_index(soundio);
if (default_out_device_index < 0)
panic("no output device found");
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
if (!device)
panic("out of memory");
fprintf(stderr, "Output device: %s\n", device->name);
cond = soundio_os_cond_create();
if (!cond)
panic("out of memory");
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->format = SoundIoFormatFloat32NE;
outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback;
if ((err = soundio_outstream_open(outstream)))
panic("unable to open device: %s", soundio_strerror(err));
if (outstream->layout_error)
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
if ((err = soundio_outstream_start(outstream)))
panic("unable to start device: %s", soundio_strerror(err));
while (seconds_offset < seconds_end)
soundio_wait_events(soundio);
soundio_outstream_destroy(outstream);
soundio_device_unref(device);
soundio_os_cond_destroy(cond);
soundio_destroy(soundio);
return 0;
}

View file

@ -1,7 +1,7 @@
#undef NDEBUG #undef NDEBUG
#include "soundio.hpp" #include "soundio.hpp"
#include "os.hpp" #include "os.h"
#include "util.hpp" #include "util.hpp"
#include "atomics.hpp" #include "atomics.hpp"