diff --git a/CMakeLists.txt b/CMakeLists.txt index 54e73b7..c7e341b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,11 +54,6 @@ set(LIBSOUNDIO_SOURCES "${CMAKE_SOURCE_DIR}/src/dummy_ring_buffer.cpp" "${CMAKE_SOURCE_DIR}/src/channel_layout.cpp" ) -if(SOUNDIO_HAVE_PULSEAUDIO) - set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES} - "${CMAKE_SOURCE_DIR}/src/pulseaudio.cpp" - ) -endif() set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h") set(LIBSOUNDIO_HEADERS @@ -68,8 +63,23 @@ set(LIBSOUNDIO_HEADERS set(TEST_SOURCES "${CMAKE_SOURCE_DIR}/test/unit_tests.cpp" + "${CMAKE_SOURCE_DIR}/src/util.cpp" + "${CMAKE_SOURCE_DIR}/src/os.cpp" + "${CMAKE_SOURCE_DIR}/src/soundio.cpp" + "${CMAKE_SOURCE_DIR}/src/dummy.cpp" + "${CMAKE_SOURCE_DIR}/src/dummy_ring_buffer.cpp" + "${CMAKE_SOURCE_DIR}/src/channel_layout.cpp" ) +if(SOUNDIO_HAVE_PULSEAUDIO) + set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES} + "${CMAKE_SOURCE_DIR}/src/pulseaudio.cpp" + ) + set(TEST_SOURCES ${TEST_SOURCES} + "${CMAKE_SOURCE_DIR}/src/pulseaudio.cpp" + ) +endif() + # GTFO, -lstdc++ !! set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "") diff --git a/example/sine.c b/example/sine.c index 9801709..935c2d2 100644 --- a/example/sine.c +++ b/example/sine.c @@ -5,8 +5,60 @@ * See http://opensource.org/licenses/MIT */ -#include "soundio.h" +#include + +#include +#include +#include + +__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 void write_callback(struct SoundIoOutputDevice *device, int frame_count) { + fprintf(stderr, "write_callback\n"); +} + +static void underrun_callback(struct SoundIoOutputDevice *device) { + static int count = 0; + fprintf(stderr, "underrun %d\n", count++); +} int main(int argc, char **argv) { + struct SoundIo *soundio = soundio_create(); + if (!soundio) + panic("out of memory"); + + int err; + if ((err = soundio_connect(soundio))) + panic("error connecting: %s", soundio_error_string(err)); + + int default_out_device_index = soundio_get_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("could not get output device: out of memory"); + + fprintf(stderr, "Output device: %s: %s\n", + soundio_device_name(device), + soundio_device_description(device)); + + struct SoundIoOutputDevice *output_device; + soundio_output_device_create(device, SoundIoSampleFormatFloat, 0.1, NULL, + write_callback, underrun_callback, &output_device); + + soundio_output_device_destroy(output_device); + soundio_device_unref(device); + soundio_destroy(soundio); return 0; } diff --git a/src/dummy.cpp b/src/dummy.cpp index 3c16b5c..276787e 100644 --- a/src/dummy.cpp +++ b/src/dummy.cpp @@ -114,6 +114,9 @@ static void output_device_destroy_dummy(SoundIo *soundio, SoundIoOutputDevice *output_device) { SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data; + if (!opd) + return; + if (opd->thread) { if (opd->thread) { opd->abort_flag.clear(); @@ -124,12 +127,24 @@ static void output_device_destroy_dummy(SoundIo *soundio, } soundio_os_cond_destroy(opd->cond); opd->cond = nullptr; + + soundio_dummy_ring_buffer_deinit(&opd->ring_buffer); + + destroy(opd); + output_device->backend_data = nullptr; } static int output_device_init_dummy(SoundIo *soundio, SoundIoOutputDevice *output_device) { - SoundIoOutputDeviceDummy *opd = (SoundIoOutputDeviceDummy *)output_device->backend_data; + + SoundIoOutputDeviceDummy *opd = create(); + if (!opd) { + output_device_destroy_dummy(soundio, output_device); + return SoundIoErrorNoMem; + } + output_device->backend_data = opd; + SoundIoDevice *device = output_device->device; int buffer_frame_count = output_device->latency * device->default_sample_rate; opd->buffer_size = output_device->bytes_per_frame * buffer_frame_count; diff --git a/src/dummy_ring_buffer.cpp b/src/dummy_ring_buffer.cpp index 4f34d90..f7e5511 100644 --- a/src/dummy_ring_buffer.cpp +++ b/src/dummy_ring_buffer.cpp @@ -44,7 +44,7 @@ void soundio_dummy_ring_buffer_destroy(struct SoundIoDummyRingBuffer *rb) { } void soundio_dummy_ring_buffer_deinit(struct SoundIoDummyRingBuffer *rb) { - deallocate(rb, rb->capacity); + deallocate(rb->address, rb->capacity); rb->address = nullptr; } diff --git a/src/pulseaudio.cpp b/src/pulseaudio.cpp index b34f2e8..3e95745 100644 --- a/src/pulseaudio.cpp +++ b/src/pulseaudio.cpp @@ -543,10 +543,44 @@ static void playback_stream_write_callback(pa_stream *stream, size_t nbytes, voi output_device->write_callback(output_device, frame_count); } -static int output_device_init_pa(SoundIo *soundio, +static void output_device_destroy_pa(SoundIo *soundio, SoundIoOutputDevice *output_device) { SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data; + if (!opd) + return; + + SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data; + pa_stream *stream = opd->stream; + if (stream) { + pa_threaded_mainloop_lock(ah->main_loop); + + pa_stream_set_write_callback(stream, nullptr, nullptr); + pa_stream_set_state_callback(stream, nullptr, nullptr); + pa_stream_set_underflow_callback(stream, nullptr, nullptr); + pa_stream_disconnect(stream); + + pa_stream_unref(stream); + + pa_threaded_mainloop_unlock(ah->main_loop); + + opd->stream = nullptr; + } + + destroy(opd); + output_device->backend_data = nullptr; +} + +static int output_device_init_pa(SoundIo *soundio, + SoundIoOutputDevice *output_device) +{ + SoundIoOutputDevicePulseAudio *opd = create(); + if (!opd) { + output_device_destroy_pa(soundio, output_device); + return SoundIoErrorNoMem; + } + output_device->backend_data = opd; + SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data; SoundIoDevice *device = output_device->device; opd->stream_ready = false; @@ -564,6 +598,7 @@ static int output_device_init_pa(SoundIo *soundio, opd->stream = pa_stream_new(ah->pulse_context, "SoundIo", &sample_spec, &channel_map); if (!opd->stream) { pa_threaded_mainloop_unlock(ah->main_loop); + output_device_destroy_pa(soundio, output_device); return SoundIoErrorNoMem; } pa_stream_set_state_callback(opd->stream, playback_stream_state_callback, output_device); @@ -585,28 +620,6 @@ static int output_device_init_pa(SoundIo *soundio, return 0; } -static void output_device_destroy_pa(SoundIo *soundio, - SoundIoOutputDevice *output_device) -{ - SoundIoOutputDevicePulseAudio *opd = (SoundIoOutputDevicePulseAudio *)output_device->backend_data; - SoundIoPulseAudio *ah = (SoundIoPulseAudio *)soundio->backend_data; - pa_stream *stream = opd->stream; - if (stream) { - pa_threaded_mainloop_lock(ah->main_loop); - - pa_stream_set_write_callback(stream, nullptr, nullptr); - pa_stream_set_state_callback(stream, nullptr, nullptr); - pa_stream_set_underflow_callback(stream, nullptr, nullptr); - pa_stream_disconnect(stream); - - pa_stream_unref(stream); - - pa_threaded_mainloop_unlock(ah->main_loop); - - opd->stream = nullptr; - } -} - static int output_device_start_pa(SoundIo *soundio, SoundIoOutputDevice *output_device) { diff --git a/src/soundio.cpp b/src/soundio.cpp index a11dab5..93547ba 100644 --- a/src/soundio.cpp +++ b/src/soundio.cpp @@ -269,3 +269,53 @@ void soundio_output_device_write(struct SoundIoOutputDevice *output_device, SoundIo *soundio = output_device->device->soundio; soundio->output_device_write(soundio, output_device, data, frame_count); } + + +int soundio_output_device_create(struct SoundIoDevice *device, + enum SoundIoSampleFormat sample_format, + double latency, void *userdata, + void (*write_callback)(struct SoundIoOutputDevice *, int frame_count), + void (*underrun_callback)(struct SoundIoOutputDevice *), + struct SoundIoOutputDevice **out_output_device) +{ + *out_output_device = nullptr; + + SoundIoOutputDevice *output_device = create(); + if (!output_device) { + soundio_output_device_destroy(output_device); + return SoundIoErrorNoMem; + } + + soundio_device_ref(device); + output_device->device = device; + output_device->userdata = userdata; + output_device->write_callback = write_callback; + output_device->underrun_callback = underrun_callback; + output_device->sample_format = sample_format; + output_device->latency = latency; + output_device->bytes_per_frame = soundio_get_bytes_per_frame(sample_format, + device->channel_layout.channel_count); + + SoundIo *soundio = device->soundio; + int err = soundio->output_device_init(soundio, output_device); + if (err) { + soundio_output_device_destroy(output_device); + return err; + } + + *out_output_device = output_device; + return 0; +} + +void soundio_output_device_destroy(SoundIoOutputDevice *output_device) { + if (!output_device) + return; + + SoundIo *soundio = output_device->device->soundio; + + if (soundio->output_device_destroy) + soundio->output_device_destroy(soundio, output_device); + + soundio_device_unref(output_device->device); + destroy(output_device); +} diff --git a/src/soundio.h b/src/soundio.h index 38fa1c5..fe3c0c9 100644 --- a/src/soundio.h +++ b/src/soundio.h @@ -284,7 +284,7 @@ enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *dev int soundio_output_device_create(struct SoundIoDevice *device, enum SoundIoSampleFormat sample_format, double latency, void *userdata, - void (*write_callback)(struct SoundIoOutputDevice *, int), + void (*write_callback)(struct SoundIoOutputDevice *, int frame_count), void (*underrun_callback)(struct SoundIoOutputDevice *), struct SoundIoOutputDevice **out_output_device); void soundio_output_device_destroy(struct SoundIoOutputDevice *device); diff --git a/test/unit_tests.cpp b/test/unit_tests.cpp index 4adace6..d0656bb 100644 --- a/test/unit_tests.cpp +++ b/test/unit_tests.cpp @@ -1,5 +1,78 @@ -#include "soundio.h" +#undef NDEBUG + +#include "soundio.h" +#include "os.hpp" +#include "util.hpp" + +#include +#include +#include + +static inline void ok_or_panic(int err) { + if (err) + soundio_panic("%s", soundio_error_string(err)); +} + +static void test_os_get_time(void) { + double prev_time = soundio_os_get_time(); + for (int i = 0; i < 1000; i += 1) { + double time = soundio_os_get_time(); + assert(time >= prev_time); + prev_time = time; + } +} + +static void write_callback(struct SoundIoOutputDevice *device, int frame_count) { } +static void underrun_callback(struct SoundIoOutputDevice *device) { } + +static void test_create_output_device(void) { + struct SoundIo *soundio = soundio_create(); + assert(soundio); + ok_or_panic(soundio_connect(soundio)); + int default_out_device_index = soundio_get_default_output_device_index(soundio); + assert(default_out_device_index >= 0); + struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index); + assert(device); + soundio_device_name(device); + soundio_device_description(device); + struct SoundIoOutputDevice *output_device; + soundio_output_device_create(device, SoundIoSampleFormatFloat, 0.1, NULL, + write_callback, underrun_callback, &output_device); + soundio_output_device_destroy(output_device); + soundio_device_unref(device); + soundio_destroy(soundio); +} + +struct Test { + const char *name; + void (*fn)(void); +}; + +static struct Test tests[] = { + {"os_get_time", test_os_get_time}, + {"create output device", test_create_output_device}, + {NULL, NULL}, +}; + +static void exec_test(struct Test *test) { + fprintf(stderr, "testing %s...", test->name); + test->fn(); + fprintf(stderr, "OK\n"); +} + +int main(int argc, char *argv[]) { + const char *match = nullptr; + + if (argc == 2) + match = argv[1]; + + struct Test *test = &tests[0]; + + while (test->name) { + if (!match || strstr(test->name, match)) + exec_test(test); + test += 1; + } -int main(int argc, char **argv) { return 0; }