JACK emits on_backend_shutdown event

This commit is contained in:
Andrew Kelley 2015-07-28 13:36:31 -07:00
parent 6df84096f3
commit eeae08e1a3
5 changed files with 135 additions and 50 deletions

View file

@ -237,6 +237,7 @@ view `coverage/index.html` in a browser.
0. Ability to parse PulseAudio's "front-left" "front-right" channel label strings
0. When two soundio clients are talking to each other, use port names to
negotiate channel maps.
0. JACK: implement prebuffering
0. why does pulseaudio microphone use up all the CPU?
0. merge in/out stream structures and functions?
0. implement CoreAudio (OSX) backend, get examples working
@ -248,7 +249,11 @@ view `coverage/index.html` in a browser.
the microphone example.
0. PulseAudio: when prebuf gets set to 0 need to pass `PA_STREAM_START_CORKED`.
0. In ALSA do we need to wake up the poll when destroying the in or out stream?
0. Create a test for clearing the playback buffer.
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. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
0. Create a test for clearing the playback buffer and prebuffering.
0. Create a test for pausing and resuming input and output streams.
0. Create a test for the latency / synchronization API.
- Input is an audio file and some events indexed at particular frame - when

View file

@ -35,7 +35,8 @@ static void flush_events_jack(struct SoundIoPrivate *si) {
SoundIo *soundio = &si->pub;
SoundIoJack *sij = &si->backend_data.jack;
bool change = false;
bool change_devices = false;
bool cb_shutdown = false;
SoundIoDevicesInfo *old_devices_info = nullptr;
soundio_os_mutex_lock(sij->mutex);
@ -44,15 +45,22 @@ static void flush_events_jack(struct SoundIoPrivate *si) {
old_devices_info = si->safe_devices_info;
si->safe_devices_info = sij->ready_devices_info;
sij->ready_devices_info = nullptr;
change = true;
change_devices = true;
}
if (sij->is_shutdown && !sij->emitted_shutdown_cb) {
sij->emitted_shutdown_cb = true;
cb_shutdown = true;
}
soundio_os_mutex_unlock(sij->mutex);
if (change)
if (change_devices)
soundio->on_devices_change(soundio);
soundio_destroy_devices_info(old_devices_info);
if (cb_shutdown)
soundio->on_backend_disconnect(soundio);
}
static void wait_events_jack(struct SoundIoPrivate *si) {
@ -96,17 +104,58 @@ static SoundIoDeviceJackPort *find_port_matching_channel(SoundIoDevice *device,
return nullptr;
}
static int outstream_xrun_callback(void *arg) {
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
SoundIoOutStream *outstream = &os->pub;
outstream->underflow_callback(outstream);
return 0;
}
static int outstream_buffer_size_callback(jack_nframes_t nframes, void *arg) {
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
SoundIoOutStream *outstream = &os->pub;
SoundIo *soundio = outstream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoJack *sij = &si->backend_data.jack;
if ((jack_nframes_t)sij->buffer_size == nframes) {
return 0;
} else {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return -1;
}
}
static int outstream_sample_rate_callback(jack_nframes_t nframes, void *arg) {
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
SoundIoOutStream *outstream = &os->pub;
if (nframes == (jack_nframes_t)outstream->sample_rate) {
return 0;
} else {
outstream->error_callback(outstream, SoundIoErrorStreaming);
return -1;
}
}
static void outstream_shutdown_callback(void *arg) {
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
SoundIoOutStream *outstream = &os->pub;
outstream->error_callback(outstream, SoundIoErrorStreaming);
}
static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
SoundIoJack *sij = &si->backend_data.jack;
SoundIoOutStreamJack *osj = &os->backend_data.jack;
SoundIoOutStream *outstream = &os->pub;
SoundIoDevice *device = outstream->device;
SoundIoDevicePrivate *dev = (SoundIoDevicePrivate *)device;
SoundIoDeviceJack *dj = &dev->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
outstream->buffer_duration = 0.0; // TODO
outstream->period_duration = 0.0; // TODO
outstream->prebuf_duration = 0.0; // TODO
outstream->buffer_duration = device->buffer_duration_current;
outstream->period_duration = 0.0;
outstream->prebuf_duration = 0.0;
jack_status_t status;
osj->client = jack_client_open(outstream->name, JackNoStartServer, &status);
@ -125,7 +174,19 @@ static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStrea
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
// TODO register the other callbacks and emit a stream error if they're called
if ((err = jack_set_buffer_size_callback(osj->client, outstream_buffer_size_callback, os))) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
if ((err = jack_set_sample_rate_callback(osj->client, outstream_sample_rate_callback, os))) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
if ((err = jack_set_xrun_callback(osj->client, outstream_xrun_callback, os))) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
jack_on_shutdown(osj->client, outstream_shutdown_callback, os);
// register ports and map channels
@ -171,9 +232,10 @@ static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStrea
static int outstream_pause_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) {
SoundIoOutStreamJack *osj = &os->backend_data.jack;
SoundIoOutStream *outstream = &os->pub;
// TODO SoundIoDevice *device = outstream->device;
// TODO SoundIoDevicePrivate *dev = (SoundIoDevicePrivate *)device;
// TODO SoundIoDeviceJack *dj = &dev->backend_data.jack;
SoundIoJack *sij = &si->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
int err;
if (pause) {
if ((err = jack_deactivate(osj->client)))
@ -227,35 +289,50 @@ static int outstream_end_write_jack(struct SoundIoPrivate *, struct SoundIoOutSt
}
static int outstream_clear_buffer_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *) {
soundio_panic("TODO clear buffer");
// JACK does not support `prebuf` which is the same as a `prebuf` value of 0,
// which means that clearing the buffer is always successful and does nothing.
return 0;
}
static int instream_open_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) {
soundio_panic("TODO open instream");
}
static void instream_destroy_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) {
soundio_panic("TODO destroy instream");
}
static int instream_start_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) {
static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
//SoundIoInStream *outstream = &is->pub;
//SoundIoInStreamJack *isj = &is->backend_data.jack;
SoundIoJack *sij = &si->backend_data.jack;
if (sij->is_shutdown) {
instream_destroy_jack(si, is);
return SoundIoErrorBackendDisconnected;
}
soundio_panic("TODO open instream");
}
static int instream_start_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
SoundIoJack *sij = &si->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
soundio_panic("TODO start instream");
}
static int instream_begin_read_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
static int instream_begin_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is,
SoundIoChannelArea **out_areas, int *frame_count)
{
soundio_panic("TODO begin read");
}
static int instream_end_read_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) {
static int instream_end_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
soundio_panic("TODO end read");
}
static int instream_pause_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause) {
static int instream_pause_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
SoundIoJack *sij = &si->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
soundio_panic("TODO pause");
}
@ -326,6 +403,10 @@ static int refresh_devices(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub;
SoundIoJack *sij = &si->backend_data.jack;
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>();
if (!devices_info)
return SoundIoErrorNoMem;
@ -513,12 +594,6 @@ static int refresh_devices(SoundIoPrivate *si) {
return 0;
}
static int process_callback(jack_nframes_t nframes, void *arg) {
////SoundIoPrivate *si = (SoundIoPrivate *)arg;
//soundio_panic("TODO process callback");
return 0;
}
static int buffer_size_callback(jack_nframes_t nframes, void *arg) {
SoundIoPrivate *si = (SoundIoPrivate *)arg;
SoundIoJack *sij = &si->backend_data.jack;
@ -537,12 +612,6 @@ static int sample_rate_callback(jack_nframes_t nframes, void *arg) {
return 0;
}
/* TODO
static int xrun_callback(void *arg) {
return 0;
}
*/
static void port_registration_callback(jack_port_id_t port_id, int reg, void *arg) {
SoundIoPrivate *si = (SoundIoPrivate *)arg;
SoundIoJack *sij = &si->backend_data.jack;
@ -561,8 +630,13 @@ static int port_rename_calllback(jack_port_id_t port_id,
}
static void shutdown_callback(void *arg) {
//SoundIoPrivate *si = (SoundIoPrivate *)arg;
soundio_panic("TODO shutdown callback");
SoundIoPrivate *si = (SoundIoPrivate *)arg;
SoundIo *soundio = &si->pub;
SoundIoJack *sij = &si->backend_data.jack;
soundio_os_mutex_lock(sij->mutex);
sij->is_shutdown = true;
soundio->on_events_signal(soundio);
soundio_os_mutex_unlock(sij->mutex);
}
static void destroy_jack(SoundIoPrivate *si) {
@ -620,10 +694,6 @@ int soundio_jack_init(struct SoundIoPrivate *si) {
}
int err;
if ((err = jack_set_process_callback(sij->client, process_callback, si))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
}
if ((err = jack_set_buffer_size_callback(sij->client, buffer_size_callback, si))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
@ -632,12 +702,6 @@ int soundio_jack_init(struct SoundIoPrivate *si) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
}
/* TODO
if ((err = jack_set_xrun_callback(sij->client, xrun_callback, si))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
}
*/
if ((err = jack_set_port_registration_callback(sij->client, port_registration_callback, si))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;
@ -648,6 +712,9 @@ int soundio_jack_init(struct SoundIoPrivate *si) {
}
jack_on_shutdown(sij->client, shutdown_callback, si);
sij->buffer_size = jack_get_buffer_size(sij->client);
sij->sample_rate = jack_get_sample_rate(sij->client);
if ((err = jack_activate(sij->client))) {
destroy_jack(si);
return SoundIoErrorInitAudioBackend;

View file

@ -35,6 +35,8 @@ struct SoundIoJack {
bool initialized;
int sample_rate;
int buffer_size;
bool is_shutdown;
bool emitted_shutdown_cb;
};
struct SoundIoOutStreamJackPort {

View file

@ -59,6 +59,7 @@ const char *soundio_strerror(int error) {
case SoundIoErrorIncompatibleDevice: return "incompatible device";
case SoundIoErrorNoSuchClient: return "no such client";
case SoundIoErrorIncompatibleBackend: return "incompatible backend";
case SoundIoErrorBackendDisconnected: return "backend disconnected";
}
soundio_panic("invalid error enum value: %d", error);
}
@ -139,8 +140,7 @@ void soundio_destroy(struct SoundIo *soundio) {
destroy(si);
}
static void default_on_devices_change(struct SoundIo *) { }
static void default_on_events_signal(struct SoundIo *) { }
static void do_nothing_cb(struct SoundIo *) { }
static void default_msg_callback(const char *msg) { }
struct SoundIo * soundio_create(void) {
@ -149,8 +149,9 @@ struct SoundIo * soundio_create(void) {
if (!si)
return NULL;
SoundIo *soundio = &si->pub;
soundio->on_devices_change = default_on_devices_change;
soundio->on_events_signal = default_on_events_signal;
soundio->on_devices_change = do_nothing_cb;
soundio->on_backend_disconnect = do_nothing_cb;
soundio->on_events_signal = do_nothing_cb;
soundio->app_name = "SoundIo";
soundio->jack_info_callback = default_msg_callback;
soundio->jack_error_callback = default_msg_callback;

View file

@ -28,6 +28,7 @@ enum SoundIoError {
SoundIoErrorIncompatibleDevice,
SoundIoErrorNoSuchClient,
SoundIoErrorIncompatibleBackend,
SoundIoErrorBackendDisconnected,
};
enum SoundIoChannelId {
@ -203,6 +204,12 @@ struct SoundIo {
// Optional callback. Called when the list of devices change. Only called
// during a call to soundio_flush_events or soundio_wait_events.
void (*on_devices_change)(struct SoundIo *);
// Optional callback. Called when the backend disconnects. For example,
// when the JACK server shuts down. When this happens, listing devices
// and opening streams will always fail with
// SoundIoErrorBackendDisconnected. This callback is only called during a
// call to soundio_flush_events or soundio_wait_events.
void (*on_backend_disconnect)(struct SoundIo *);
// Optional callback. Called from an unknown thread that you should not use
// to call any soundio functions. You may use this to signal a condition
// variable to wake up. Called when soundio_wait_events would be woken up.
@ -349,6 +356,9 @@ struct SoundIoOutStream {
// How many seconds need to be in the buffer before playback will commence.
// If a buffer underflow occurs, this prebuffering will be again enabled.
// This value defaults to being the same as `buffer_duration`.
// After you call `soundio_outstream_open` this value is replaced with the
// actual `prebuf_duration`, as near to this value as possible.
// JACK does not support prebuffering; `prebuf_duration` is effectively 0.
double prebuf_duration;
// Defaults to NULL. Put whatever you want here.