From c96405a091467550ab422174c940933f06c8dd1b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Aug 2015 12:54:13 -0700 Subject: [PATCH] rt prio warning behavior is overridable --- README.md | 8 ++------ soundio/soundio.h | 15 +++++++++++---- src/alsa.cpp | 8 +++++--- src/coreaudio.cpp | 2 +- src/dummy.cpp | 10 ++++++++-- src/os.cpp | 17 ++++------------- src/os.h | 3 ++- src/soundio.cpp | 11 +++++++++++ src/wasapi.cpp | 12 +++++++++--- test/unit_tests.cpp | 4 ++-- 10 files changed, 55 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 857d1c6..be8c5a5 100644 --- a/README.md +++ b/README.md @@ -278,13 +278,13 @@ Then look at `html/index.html` in a browser. block_until_have_devices 0. Integrate into libgroove and test with Groove Basin 0. clear buffer maybe could take an argument to say how many frames to not clear - 0. create a test for clear buffer; ensure pause/play semantics work 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 call the underflow callback. 0. Create a test for pausing and resuming input and output streams. - Should pause/resume be callable from outside the callbacks? - Ensure double pausing / double resuming works fine. + - test clearing the buffer 0. Create a test for the latency / synchronization API. - Input is an audio file and some events indexed at particular frame - when listening the events should line up exactly with a beat or visual @@ -297,17 +297,13 @@ Then look at `html/index.html` in a browser. call lock and then unlock when done. 0. add len arguments to APIs that have char * - replace strdup with `soundio_str_dupe` - 0. Support PulseAudio proplist properties for main context and streams - 0. Expose JACK options in `jack_client_open` - 0. mlock memory which is accessed in the real time path - 0. make rtprio warning a callback and have existing behavior be the default callback 0. write detailed docs on buffer underflows explaining when they occur, what state changes are related to them, and how to recover from them. 0. Consider testing on FreeBSD 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`. - 0. Add [sndio](http://www.sndio.org/) backend to support OpenBSD. 0. Custom allocator support + - default allocator mlock memory 0. Support for stream icon. - PulseAudio: XDG icon name - WASAPI: path to .exe, .dll, or .ico diff --git a/soundio/soundio.h b/soundio/soundio.h index 3f8cc95..8d31534 100644 --- a/soundio/soundio.h +++ b/soundio/soundio.h @@ -314,12 +314,23 @@ struct SoundIo { /// variable to wake up. Called when ::soundio_wait_events would be woken up. void (*on_events_signal)(struct SoundIo *); + /// Read-only. After calling ::soundio_connect or ::soundio_connect_backend, + /// this field tells which backend is currently connected. + enum SoundIoBackend current_backend; + /// Optional: Application name. /// PulseAudio uses this for "application name". /// JACK uses this for `client_name`. /// Must not contain a colon (":"). const char *app_name; + /// Optional: Real time priority warning. + /// This callback is fired when making thread real-time priority failed. By + /// default, it will print to stderr only the first time it is called + /// a message instructing the user how to configure their system to allow + /// real-time priority threads. + void (*emit_rtprio_warning)(void); + /// Optional: JACK info callback. /// By default, libsoundio sets this to an empty function in order to /// silence stdio messages from JACK. You may override the behavior by @@ -330,10 +341,6 @@ struct SoundIo { /// Optional: JACK error callback. /// See SoundIo::jack_info_callback void (*jack_error_callback)(const char *msg); - - /// Read-only. After calling ::soundio_connect or ::soundio_connect_backend, - /// this field tells which backend is currently connected. - enum SoundIoBackend current_backend; }; /// The size of this struct is not part of the API or ABI. diff --git a/src/alsa.cpp b/src/alsa.cpp index a0d0389..c1d31c9 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -1267,12 +1267,13 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { SoundIoOutStreamAlsa *osa = &os->backend_data.alsa; + SoundIo *soundio = &si->pub; assert(!osa->thread); int err; osa->thread_exit_flag.test_and_set(); - if ((err = soundio_os_thread_create(outstream_thread_run, os, true, &osa->thread))) + if ((err = soundio_os_thread_create(outstream_thread_run, os, soundio->emit_rtprio_warning, &osa->thread))) return err; return 0; @@ -1538,12 +1539,13 @@ static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { SoundIoInStreamAlsa *isa = &is->backend_data.alsa; + SoundIo *soundio = &si->pub; assert(!isa->thread); isa->thread_exit_flag.test_and_set(); int err; - if ((err = soundio_os_thread_create(instream_thread_run, is, true, &isa->thread))) { + if ((err = soundio_os_thread_create(instream_thread_run, is, soundio->emit_rtprio_warning, &isa->thread))) { instream_destroy_alsa(si, is); return err; } @@ -1706,7 +1708,7 @@ int soundio_alsa_init(SoundIoPrivate *si) { wakeup_device_poll(sia); - if ((err = soundio_os_thread_create(device_thread_run, si, false, &sia->thread))) { + if ((err = soundio_os_thread_create(device_thread_run, si, nullptr, &sia->thread))) { destroy_alsa(si); return err; } diff --git a/src/coreaudio.cpp b/src/coreaudio.cpp index 0285a7e..5f7abf7 100644 --- a/src/coreaudio.cpp +++ b/src/coreaudio.cpp @@ -1360,7 +1360,7 @@ int soundio_coreaudio_init(SoundIoPrivate *si) { return SoundIoErrorSystemResources; } - if ((err = soundio_os_thread_create(device_thread_run, si, false, &sica->thread))) { + if ((err = soundio_os_thread_create(device_thread_run, si, nullptr, &sica->thread))) { destroy_ca(si); return err; } diff --git a/src/dummy.cpp b/src/dummy.cpp index bb3c8e3..675ba69 100644 --- a/src/dummy.cpp +++ b/src/dummy.cpp @@ -175,6 +175,7 @@ static int outstream_open_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) { SoundIoOutStreamDummy *osd = &os->backend_data.dummy; + SoundIo *soundio = &si->pub; if (pause) { if (osd->thread) { osd->abort_flag.clear(); @@ -186,7 +187,9 @@ static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStr if (!osd->thread) { osd->abort_flag.test_and_set(); int err; - if ((err = soundio_os_thread_create(playback_thread_run, os, true, &osd->thread))) { + if ((err = soundio_os_thread_create(playback_thread_run, os, + soundio->emit_rtprio_warning, &osd->thread))) + { return err; } } @@ -281,6 +284,7 @@ static int instream_open_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static int instream_pause_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) { SoundIoInStreamDummy *isd = &is->backend_data.dummy; + SoundIo *soundio = &si->pub; if (pause) { if (isd->thread) { isd->abort_flag.clear(); @@ -292,7 +296,9 @@ static int instream_pause_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is, if (!isd->thread) { isd->abort_flag.test_and_set(); int err; - if ((err = soundio_os_thread_create(capture_thread_run, is, true, &isd->thread))) { + if ((err = soundio_os_thread_create(capture_thread_run, is, + soundio->emit_rtprio_warning, &isd->thread))) + { return err; } } diff --git a/src/os.cpp b/src/os.cpp index 2511bd5..a605dd1 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #if defined(_WIN32) @@ -180,18 +179,10 @@ static void *run_pthread(void *userdata) { } #endif -static atomic_flag rtprio_seen = ATOMIC_FLAG_INIT; -static void emit_rtprio_warning(void) { - if (!rtprio_seen.test_and_set()) { - fprintf(stderr, "warning: unable to set high priority thread: Operation not permitted\n"); - fprintf(stderr, "See https://github.com/andrewrk/genesis/wiki/" - "warning:-unable-to-set-high-priority-thread:-Operation-not-permitted\n"); - } -} - int soundio_os_thread_create( void (*run)(void *arg), void *arg, - bool high_priority, struct SoundIoOsThread ** out_thread) + void (*emit_rtprio_warning)(void), + struct SoundIoOsThread ** out_thread) { *out_thread = NULL; @@ -210,7 +201,7 @@ int soundio_os_thread_create( soundio_os_thread_destroy(thread); return SoundIoErrorSystemResources; } - if (high_priority) { + if (emit_rtprio_warning) { if (!SetThreadPriority(thread->handle, THREAD_PRIORITY_TIME_CRITICAL)) { emit_rtprio_warning(); } @@ -223,7 +214,7 @@ int soundio_os_thread_create( } thread->attr_init = true; - if (high_priority) { + if (emit_rtprio_warning) { int max_priority = sched_get_priority_max(SCHED_FIFO); if (max_priority == -1) { soundio_os_thread_destroy(thread); diff --git a/src/os.h b/src/os.h index 444a050..c5e43a9 100644 --- a/src/os.h +++ b/src/os.h @@ -21,7 +21,8 @@ double soundio_os_get_time(void); struct SoundIoOsThread; int soundio_os_thread_create( void (*run)(void *arg), void *arg, - bool high_priority, struct SoundIoOsThread ** out_thread); + void (*emit_rtprio_warning)(void), + struct SoundIoOsThread ** out_thread); void soundio_os_thread_destroy(struct SoundIoOsThread *thread); diff --git a/src/soundio.cpp b/src/soundio.cpp index d55f89d..e08362b 100644 --- a/src/soundio.cpp +++ b/src/soundio.cpp @@ -12,6 +12,7 @@ #include #include +#include static const SoundIoBackend available_backends[] = { #ifdef SOUNDIO_HAVE_JACK @@ -168,6 +169,15 @@ static void default_backend_disconnect_cb(struct SoundIo *, int err) { soundio_panic("libsoundio: backend disconnected: %s", soundio_strerror(err)); } +static atomic_flag rtprio_seen = ATOMIC_FLAG_INIT; +static void default_emit_rtprio_warning(void) { + if (!rtprio_seen.test_and_set()) { + fprintf(stderr, "warning: unable to set high priority thread: Operation not permitted\n"); + fprintf(stderr, "See " + "https://github.com/andrewrk/genesis/wiki/warning:-unable-to-set-high-priority-thread:-Operation-not-permitted\n"); + } +} + struct SoundIo *soundio_create(void) { int err; if ((err = soundio_os_init())) @@ -180,6 +190,7 @@ struct SoundIo *soundio_create(void) { soundio->on_backend_disconnect = default_backend_disconnect_cb; soundio->on_events_signal = do_nothing_cb; soundio->app_name = "SoundIo"; + soundio->emit_rtprio_warning = default_emit_rtprio_warning; soundio->jack_info_callback = default_msg_callback; soundio->jack_error_callback = default_msg_callback; return soundio; diff --git a/src/wasapi.cpp b/src/wasapi.cpp index bcf0ce3..97ad9a7 100644 --- a/src/wasapi.cpp +++ b/src/wasapi.cpp @@ -1333,6 +1333,7 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi; SoundIoOutStream *outstream = &os->pub; SoundIoDevice *device = outstream->device; + SoundIo *soundio = &si->pub; // All the COM functions are supposed to be called from the same thread. libsoundio API does not // restrict the calling thread context in this way. Furthermore, the user might have called @@ -1368,7 +1369,9 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr osw->thread_exit_flag.test_and_set(); int err; - if ((err = soundio_os_thread_create(outstream_thread_run, os, true, &osw->thread))) { + if ((err = soundio_os_thread_create(outstream_thread_run, os, + soundio->emit_rtprio_warning, &osw->thread))) + { outstream_destroy_wasapi(si, os); return err; } @@ -1726,6 +1729,7 @@ static int instream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoInStrea SoundIoInStreamWasapi *isw = &is->backend_data.wasapi; SoundIoInStream *instream = &is->pub; SoundIoDevice *device = instream->device; + SoundIo *soundio = &si->pub; // All the COM functions are supposed to be called from the same thread. libsoundio API does not // restrict the calling thread context in this way. Furthermore, the user might have called @@ -1761,7 +1765,9 @@ static int instream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoInStrea isw->thread_exit_flag.test_and_set(); int err; - if ((err = soundio_os_thread_create(instream_thread_run, is, true, &isw->thread))) { + if ((err = soundio_os_thread_create(instream_thread_run, is, + soundio->emit_rtprio_warning, &isw->thread))) + { instream_destroy_wasapi(si, is); return err; } @@ -1982,7 +1988,7 @@ int soundio_wasapi_init(SoundIoPrivate *si) { siw->device_events.lpVtbl = &soundio_MMNotificationClient; siw->device_events_refs = 1; - if ((err = soundio_os_thread_create(device_thread_run, si, false, &siw->thread))) { + if ((err = soundio_os_thread_create(device_thread_run, si, nullptr, &siw->thread))) { destroy_wasapi(si); return err; } diff --git a/test/unit_tests.cpp b/test/unit_tests.cpp index 45b1cb6..b9135ab 100644 --- a/test/unit_tests.cpp +++ b/test/unit_tests.cpp @@ -145,10 +145,10 @@ static void test_ring_buffer_threaded(void) { rb_done = false; SoundIoOsThread *reader_thread; - ok_or_panic(soundio_os_thread_create(reader_thread_run, nullptr, false, &reader_thread)); + ok_or_panic(soundio_os_thread_create(reader_thread_run, nullptr, nullptr, &reader_thread)); SoundIoOsThread *writer_thread; - ok_or_panic(soundio_os_thread_create(writer_thread_run, nullptr, false, &writer_thread)); + ok_or_panic(soundio_os_thread_create(writer_thread_run, nullptr, nullptr, &writer_thread)); while (rb_read_it < 100000 || rb_write_it < 100000) {} rb_done = true;