SoundIo, SoundIoDevice: unions instead of void *

for better cache locality
This commit is contained in:
Andrew Kelley 2015-07-27 16:37:45 -07:00
parent 85549b59b7
commit db1195877a
12 changed files with 326 additions and 210 deletions

View file

@ -233,12 +233,16 @@ view `coverage/index.html` in a browser.
## Roadmap ## Roadmap
0. implement JACK backend, get examples working 0. implement JACK backend, get examples working
0. Steal PulseAudio's default channel maps per channel count
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. why does pulseaudio microphone use up all the CPU? 0. why does pulseaudio microphone use up all the CPU?
0. merge in/out stream structures and functions? 0. merge in/out stream structures and functions?
0. implement CoreAudio (OSX) backend, get examples working 0. implement CoreAudio (OSX) backend, get examples working
0. implement WASAPI (Windows) backend, get examples working 0. implement WASAPI (Windows) backend, get examples working
0. implement ASIO (Windows) backend, get examples working 0. implement ASIO (Windows) backend, get examples working
0. Avoid calling `panic` in PulseAudio. 0. Avoid calling `soundio_panic` in PulseAudio.
0. Figure out a way to test prebuf. I suspect prebuf not working for ALSA 0. Figure out a way to test prebuf. I suspect prebuf not working for ALSA
which is why we have to pre-fill the ring buffer with silence for which is why we have to pre-fill the ring buffer with silence for
the microphone example. the microphone example.
@ -257,6 +261,9 @@ view `coverage/index.html` in a browser.
0. Allow calling functions from outside the callbacks as long as they first 0. Allow calling functions from outside the callbacks as long as they first
call lock and then unlock when done. call lock and then unlock when done.
0. Should pause/resume be callable from outside the callbacks? 0. Should pause/resume be callable from outside the callbacks?
0. device.name -> device.id, device.description -> device.name
0. PulseAudio: when opening a device start it corked that way we can get
accurate buffer readings
0. clean up API and improve documentation 0. clean up API and improve documentation
- make sure every function which can return an error documents which errors - make sure every function which can return an error documents which errors
it can return it can return

View file

@ -7,8 +7,6 @@
#include "alsa.hpp" #include "alsa.hpp"
#include "soundio.hpp" #include "soundio.hpp"
#include "os.hpp"
#include "atomics.hpp"
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <sys/inotify.h> #include <sys/inotify.h>
@ -25,21 +23,6 @@ static snd_pcm_access_t prioritized_access_types[] = {
}; };
struct SoundIoAlsa {
SoundIoOsMutex *mutex;
SoundIoOsCond *cond;
struct SoundIoOsThread *thread;
atomic_flag abort_flag;
int notify_fd;
int notify_wd;
atomic_bool have_devices_flag;
int notify_pipe_fd[2];
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
};
struct SoundIoOutStreamAlsa { struct SoundIoOutStreamAlsa {
snd_pcm_t *handle; snd_pcm_t *handle;
snd_pcm_chmap_t *chmap; snd_pcm_chmap_t *chmap;
@ -85,9 +68,7 @@ static void wakeup_device_poll(SoundIoAlsa *sia) {
} }
static void destroy_alsa(SoundIoPrivate *si) { static void destroy_alsa(SoundIoPrivate *si) {
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; SoundIoAlsa *sia = &si->backend_data.alsa;
if (!sia)
return;
if (sia->thread) { if (sia->thread) {
sia->abort_flag.clear(); sia->abort_flag.clear();
@ -108,9 +89,6 @@ static void destroy_alsa(SoundIoPrivate *si) {
close(sia->notify_pipe_fd[0]); close(sia->notify_pipe_fd[0]);
close(sia->notify_pipe_fd[1]); close(sia->notify_pipe_fd[1]);
close(sia->notify_fd); close(sia->notify_fd);
destroy(sia);
si->backend_data = nullptr;
} }
static char * str_partition_on_char(char *str, char c) { static char * str_partition_on_char(char *str, char c) {
@ -517,7 +495,7 @@ static inline bool str_has_prefix(const char *big_str, const char *prefix) {
static int refresh_devices(SoundIoPrivate *si) { static int refresh_devices(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; SoundIoAlsa *sia = &si->backend_data.alsa;
SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>(); SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>();
if (!devices_info) if (!devices_info)
@ -761,7 +739,7 @@ static int refresh_devices(SoundIoPrivate *si) {
static void device_thread_run(void *arg) { static void device_thread_run(void *arg) {
SoundIoPrivate *si = (SoundIoPrivate *)arg; SoundIoPrivate *si = (SoundIoPrivate *)arg;
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; SoundIoAlsa *sia = &si->backend_data.alsa;
// Some systems cannot read integer variables if they are not // Some systems cannot read integer variables if they are not
// properly aligned. On other systems, incorrect alignment may // properly aligned. On other systems, incorrect alignment may
@ -864,7 +842,7 @@ static void block_until_have_devices(SoundIoAlsa *sia) {
static void flush_events(SoundIoPrivate *si) { static void flush_events(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; SoundIoAlsa *sia = &si->backend_data.alsa;
block_until_have_devices(sia); block_until_have_devices(sia);
bool change = false; bool change = false;
@ -888,7 +866,7 @@ static void flush_events(SoundIoPrivate *si) {
} }
static void wait_events(SoundIoPrivate *si) { static void wait_events(SoundIoPrivate *si) {
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; SoundIoAlsa *sia = &si->backend_data.alsa;
flush_events(si); flush_events(si);
soundio_os_mutex_lock(sia->mutex); soundio_os_mutex_lock(sia->mutex);
soundio_os_cond_wait(sia->cond, sia->mutex); soundio_os_cond_wait(sia->cond, sia->mutex);
@ -896,7 +874,7 @@ static void wait_events(SoundIoPrivate *si) {
} }
static void wakeup(SoundIoPrivate *si) { static void wakeup(SoundIoPrivate *si) {
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data; SoundIoAlsa *sia = &si->backend_data.alsa;
soundio_os_mutex_lock(sia->mutex); soundio_os_mutex_lock(sia->mutex);
soundio_os_cond_signal(sia->cond, sia->mutex); soundio_os_cond_signal(sia->cond, sia->mutex);
soundio_os_mutex_unlock(sia->mutex); soundio_os_mutex_unlock(sia->mutex);
@ -1695,15 +1673,9 @@ static int instream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoInStream
} }
int soundio_alsa_init(SoundIoPrivate *si) { int soundio_alsa_init(SoundIoPrivate *si) {
SoundIoAlsa *sia = &si->backend_data.alsa;
int err; int err;
assert(!si->backend_data);
SoundIoAlsa *sia = create<SoundIoAlsa>();
if (!sia) {
destroy_alsa(si);
return SoundIoErrorNoMem;
}
si->backend_data = sia;
sia->notify_fd = -1; sia->notify_fd = -1;
sia->notify_wd = -1; sia->notify_wd = -1;
sia->have_devices_flag.store(false); sia->have_devices_flag.store(false);

View file

@ -8,7 +8,28 @@
#ifndef SOUNDIO_ALSA_HPP #ifndef SOUNDIO_ALSA_HPP
#define SOUNDIO_ALSA_HPP #define SOUNDIO_ALSA_HPP
#include "os.hpp"
#include "atomics.hpp"
int soundio_alsa_init(struct SoundIoPrivate *si); int soundio_alsa_init(struct SoundIoPrivate *si);
#endif struct SoundIoDeviceAlsa {
};
struct SoundIoAlsa {
SoundIoOsMutex *mutex;
SoundIoOsCond *cond;
struct SoundIoOsThread *thread;
atomic_flag abort_flag;
int notify_fd;
int notify_wd;
atomic_bool have_devices_flag;
int notify_pipe_fd[2];
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
};
#endif

View file

@ -7,7 +7,6 @@
#include "dummy.hpp" #include "dummy.hpp"
#include "soundio.hpp" #include "soundio.hpp"
#include "os.hpp"
#include "atomics.hpp" #include "atomics.hpp"
#include "ring_buffer.hpp" #include "ring_buffer.hpp"
@ -39,12 +38,6 @@ struct SoundIoInStreamDummy {
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };
struct SoundIoDummy {
SoundIoOsMutex *mutex;
SoundIoOsCond *cond;
bool devices_emitted;
};
static void playback_thread_run(void *arg) { static void playback_thread_run(void *arg) {
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg; SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
@ -127,23 +120,18 @@ static void capture_thread_run(void *arg) {
} }
static void destroy_dummy(SoundIoPrivate *si) { static void destroy_dummy(SoundIoPrivate *si) {
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data; SoundIoDummy *sid = &si->backend_data.dummy;
if (!sid)
return;
if (sid->cond) if (sid->cond)
soundio_os_cond_destroy(sid->cond); soundio_os_cond_destroy(sid->cond);
if (sid->mutex) if (sid->mutex)
soundio_os_mutex_destroy(sid->mutex); soundio_os_mutex_destroy(sid->mutex);
destroy(sid);
si->backend_data = nullptr;
} }
static void flush_events(SoundIoPrivate *si) { static void flush_events(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data; SoundIoDummy *sid = &si->backend_data.dummy;
if (sid->devices_emitted) if (sid->devices_emitted)
return; return;
sid->devices_emitted = true; sid->devices_emitted = true;
@ -151,13 +139,13 @@ static void flush_events(SoundIoPrivate *si) {
} }
static void wait_events(SoundIoPrivate *si) { static void wait_events(SoundIoPrivate *si) {
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data; SoundIoDummy *sid = &si->backend_data.dummy;
flush_events(si); flush_events(si);
soundio_os_cond_wait(sid->cond, nullptr); soundio_os_cond_wait(sid->cond, nullptr);
} }
static void wakeup(SoundIoPrivate *si) { static void wakeup(SoundIoPrivate *si) {
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data; SoundIoDummy *sid = &si->backend_data.dummy;
soundio_os_cond_signal(sid->cond, nullptr); soundio_os_cond_signal(sid->cond, nullptr);
} }
@ -468,13 +456,7 @@ static int set_all_device_channel_layouts(SoundIoDevice *device) {
int soundio_dummy_init(SoundIoPrivate *si) { int soundio_dummy_init(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
assert(!si->backend_data); SoundIoDummy *sid = &si->backend_data.dummy;
SoundIoDummy *sid = create<SoundIoDummy>();
if (!sid) {
destroy_dummy(si);
return SoundIoErrorNoMem;
}
si->backend_data = sid;
sid->mutex = soundio_os_mutex_create(); sid->mutex = soundio_os_mutex_create();
if (!sid->mutex) { if (!sid->mutex) {

View file

@ -8,6 +8,18 @@
#ifndef SOUNDIO_DUMMY_HPP #ifndef SOUNDIO_DUMMY_HPP
#define SOUNDIO_DUMMY_HPP #define SOUNDIO_DUMMY_HPP
#include "os.hpp"
int soundio_dummy_init(struct SoundIoPrivate *si); int soundio_dummy_init(struct SoundIoPrivate *si);
struct SoundIoDummy {
SoundIoOsMutex *mutex;
SoundIoOsCond *cond;
bool devices_emitted;
};
struct SoundIoDeviceDummy {
};
#endif #endif

View file

@ -8,23 +8,15 @@
#include "jack.hpp" #include "jack.hpp"
#include "soundio.hpp" #include "soundio.hpp"
#include "atomics.hpp" #include "atomics.hpp"
#include "os.hpp"
#include "list.hpp" #include "list.hpp"
#include <jack/jack.h>
#include <stdio.h> #include <stdio.h>
static atomic_flag global_msg_callback_flag = ATOMIC_FLAG_INIT; static atomic_flag global_msg_callback_flag = ATOMIC_FLAG_INIT;
struct SoundIoJack { struct SoundIoOutStreamJack {
jack_client_t *client; jack_client_t *client;
SoundIoOsMutex *mutex; jack_port_t *ports[SOUNDIO_MAX_CHANNELS];
SoundIoOsCond *cond;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
bool initialized;
int sample_rate;
int buffer_size;
}; };
struct SoundIoJackPort { struct SoundIoJackPort {
@ -37,12 +29,13 @@ struct SoundIoJackClient {
int name_len; int name_len;
bool is_physical; bool is_physical;
SoundIoDevicePurpose purpose; SoundIoDevicePurpose purpose;
SoundIoList<SoundIoJackPort> ports; SoundIoJackPort ports[SOUNDIO_MAX_CHANNELS];
int port_count;
}; };
static void flush_events_jack(struct SoundIoPrivate *si) { static void flush_events_jack(struct SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = &si->backend_data.jack;
bool change = false; bool change = false;
SoundIoDevicesInfo *old_devices_info = nullptr; SoundIoDevicesInfo *old_devices_info = nullptr;
@ -65,7 +58,7 @@ static void flush_events_jack(struct SoundIoPrivate *si) {
} }
static void wait_events_jack(struct SoundIoPrivate *si) { static void wait_events_jack(struct SoundIoPrivate *si) {
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = &si->backend_data.jack;
flush_events_jack(si); flush_events_jack(si);
soundio_os_mutex_lock(sij->mutex); soundio_os_mutex_lock(sij->mutex);
soundio_os_cond_wait(sij->cond, sij->mutex); soundio_os_cond_wait(sij->cond, sij->mutex);
@ -73,69 +66,150 @@ static void wait_events_jack(struct SoundIoPrivate *si) {
} }
static void wakeup_jack(struct SoundIoPrivate *si) { static void wakeup_jack(struct SoundIoPrivate *si) {
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = &si->backend_data.jack;
soundio_os_mutex_lock(sij->mutex); soundio_os_mutex_lock(sij->mutex);
soundio_os_cond_signal(sij->cond, sij->mutex); soundio_os_cond_signal(sij->cond, sij->mutex);
soundio_os_mutex_unlock(sij->mutex); soundio_os_mutex_unlock(sij->mutex);
} }
static int outstream_process_callback(jack_nframes_t nframes, void *arg) {
static int outstream_open_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *) { SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
soundio_panic("TODO"); SoundIoOutStream *outstream = &os->pub;
outstream->write_callback(outstream, nframes);
return 0;
} }
static void outstream_destroy_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *) { static void outstream_destroy_jack(struct SoundIoPrivate *is, struct SoundIoOutStreamPrivate *os) {
soundio_panic("TODO"); SoundIoOutStreamJack *osj = (SoundIoOutStreamJack *) os->backend_data;
if (!osj)
return;
jack_client_close(osj->client);
destroy(osj);
os->backend_data = nullptr;
} }
static int outstream_start_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *) { static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
soundio_panic("TODO"); SoundIoOutStream *outstream = &os->pub;
//TODO SoundIoDevice *device = outstream->device;
outstream->buffer_duration = 0.0; // TODO
outstream->period_duration = 0.0; // TODO
outstream->prebuf_duration = 0.0; // TODO
SoundIoOutStreamJack *osj = create<SoundIoOutStreamJack>();
if (!osj) {
outstream_destroy_jack(si, os);
return SoundIoErrorNoMem;
}
os->backend_data = osj;
outstream->layout_error = SoundIoErrorIncompatibleBackend;
jack_status_t status;
osj->client = jack_client_open(outstream->name, JackNoStartServer, &status);
if (!osj->client) {
outstream_destroy_jack(si, os);
assert(!(status & JackInvalidOption));
if (status & JackShmFailure)
return SoundIoErrorSystemResources;
if (status & JackNoSuchClient)
return SoundIoErrorNoSuchClient;
return SoundIoErrorOpeningDevice;
}
int err;
if ((err = jack_set_process_callback(osj->client, outstream_process_callback, os))) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
// TODO register the other callbacks and emit a stream error if they're called
// register ports
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
const char *channel_name = soundio_get_channel_name(outstream->layout.channels[ch]);
unsigned long flags = JackPortIsOutput;
if (!outstream->non_terminal_hint)
flags |= JackPortIsTerminal;
jack_port_t *port = jack_port_register(osj->client, channel_name, JACK_DEFAULT_AUDIO_TYPE, flags, 0);
if (!port) {
outstream_destroy_jack(si, os);
return SoundIoErrorOpeningDevice;
}
osj->ports[ch] = port;
}
return 0;
}
static int outstream_pause_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) {
SoundIoOutStreamJack *osj = (SoundIoOutStreamJack *) os->backend_data;
SoundIoOutStream *outstream = &os->pub;
int err;
if (pause) {
if ((err = jack_deactivate(osj->client)))
return SoundIoErrorStreaming;
} else {
if ((err = jack_activate(osj->client)))
return SoundIoErrorStreaming;
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
// TODO figure out source port name and dest port name
//if ((err = jack_connect(osj->client, source_port, dest_port)))
// return SoundIoErrorStreaming;
}
}
return 0;
}
static int outstream_start_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
return outstream_pause_jack(si, os, false);
} }
static int outstream_begin_write_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, static int outstream_begin_write_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
SoundIoChannelArea **out_areas, int *frame_count) SoundIoChannelArea **out_areas, int *frame_count)
{ {
soundio_panic("TODO"); soundio_panic("TODO begin write");
} }
static int outstream_end_write_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count) { static int outstream_end_write_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count) {
soundio_panic("TODO"); soundio_panic("TODO end write");
} }
static int outstream_clear_buffer_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *) { static int outstream_clear_buffer_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *) {
soundio_panic("TODO"); soundio_panic("TODO clear buffer");
} }
static int outstream_pause_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause) {
soundio_panic("TODO");
}
static int instream_open_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) { static int instream_open_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) {
soundio_panic("TODO"); soundio_panic("TODO open instream");
} }
static void instream_destroy_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) { static void instream_destroy_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) {
soundio_panic("TODO"); soundio_panic("TODO destroy instream");
} }
static int instream_start_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) { static int instream_start_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) {
soundio_panic("TODO"); soundio_panic("TODO start instream");
} }
static int instream_begin_read_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, static int instream_begin_read_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
SoundIoChannelArea **out_areas, int *frame_count) SoundIoChannelArea **out_areas, int *frame_count)
{ {
soundio_panic("TODO"); soundio_panic("TODO begin read");
} }
static int instream_end_read_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) { static int instream_end_read_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *) {
soundio_panic("TODO"); soundio_panic("TODO end read");
} }
static int instream_pause_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause) { static int instream_pause_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause) {
soundio_panic("TODO"); soundio_panic("TODO pause");
} }
static void split_str(const char *input_str, int input_str_len, char c, static void split_str(const char *input_str, int input_str_len, char c,
@ -179,6 +253,7 @@ static SoundIoJackClient *find_or_create_client(SoundIoList<SoundIoJackClient> *
client->purpose = purpose; client->purpose = purpose;
client->name = client_name; client->name = client_name;
client->name_len = client_name_len; client->name_len = client_name_len;
client->port_count = 0;
return client; return client;
} }
@ -193,7 +268,7 @@ static char *dupe_str(const char *str, int str_len) {
static int refresh_devices(SoundIoPrivate *si) { static int refresh_devices(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = &si->backend_data.jack;
SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>(); SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>();
if (!devices_info) if (!devices_info)
@ -208,13 +283,18 @@ static int refresh_devices(SoundIoPrivate *si) {
} }
SoundIoList<SoundIoJackClient> clients; SoundIoList<SoundIoJackClient> clients;
int err;
const char **port_name_ptr = port_names; const char **port_name_ptr = port_names;
while (*port_name_ptr) { while (*port_name_ptr) {
const char *client_and_port_name = *port_name_ptr; const char *client_and_port_name = *port_name_ptr;
jack_port_t *jport = jack_port_by_name(sij->client, client_and_port_name); jack_port_t *jport = jack_port_by_name(sij->client, client_and_port_name);
int flags = jack_port_flags(jport); int flags = jack_port_flags(jport);
const char *port_type = jack_port_type(jport);
if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) != 0) {
// we don't know how to support such a port
continue;
}
SoundIoDevicePurpose purpose = (flags & JackPortIsInput) ? SoundIoDevicePurpose purpose = (flags & JackPortIsInput) ?
SoundIoDevicePurposeOutput : SoundIoDevicePurposeInput; SoundIoDevicePurposeOutput : SoundIoDevicePurposeInput;
bool is_physical = flags & JackPortIsPhysical; bool is_physical = flags & JackPortIsPhysical;
@ -236,12 +316,11 @@ static int refresh_devices(SoundIoPrivate *si) {
destroy(devices_info); destroy(devices_info);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
if ((err = client->ports.add_one())) { if (client->port_count >= SOUNDIO_MAX_CHANNELS) {
jack_free(port_names); // we hit the channel limit, skip the leftovers
destroy(devices_info); continue;
return SoundIoErrorNoMem;
} }
SoundIoJackPort *port = &client->ports.last(); SoundIoJackPort *port = &client->ports[client->port_count++];
port->name = port_name; port->name = port_name;
port->name_len = port_name_len; port->name_len = port_name_len;
@ -251,7 +330,7 @@ static int refresh_devices(SoundIoPrivate *si) {
for (int i = 0; i < clients.length; i += 1) { for (int i = 0; i < clients.length; i += 1) {
SoundIoJackClient *client = &clients.at(i); SoundIoJackClient *client = &clients.at(i);
if (client->ports.length <= 0) if (client->port_count <= 0)
continue; continue;
SoundIoDevice *device = create<SoundIoDevice>(); SoundIoDevice *device = create<SoundIoDevice>();
@ -260,9 +339,9 @@ static int refresh_devices(SoundIoPrivate *si) {
destroy(devices_info); destroy(devices_info);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
int description_len = client->name_len + 3 + 2 * client->ports.length; int description_len = client->name_len + 3 + 2 * client->port_count;
for (int port_index = 0; port_index < client->ports.length; port_index += 1) { for (int port_index = 0; port_index < client->port_count; port_index += 1) {
SoundIoJackPort *port = &client->ports.at(port_index); SoundIoJackPort *port = &client->ports[port_index];
description_len += port->name_len; description_len += port->name_len;
} }
@ -294,21 +373,21 @@ static int refresh_devices(SoundIoPrivate *si) {
memcpy(device->description, client->name, client->name_len); memcpy(device->description, client->name, client->name_len);
memcpy(&device->description[client->name_len], ": ", 2); memcpy(&device->description[client->name_len], ": ", 2);
int index = client->name_len + 2; int index = client->name_len + 2;
for (int port_index = 0; port_index < client->ports.length; port_index += 1) { for (int port_index = 0; port_index < client->port_count; port_index += 1) {
SoundIoJackPort *port = &client->ports.at(port_index); SoundIoJackPort *port = &client->ports[port_index];
memcpy(&device->description[index], port->name, port->name_len); memcpy(&device->description[index], port->name, port->name_len);
index += port->name_len; index += port->name_len;
if (port_index + 1 < client->ports.length) { if (port_index + 1 < client->port_count) {
memcpy(&device->description[index], ", ", 2); memcpy(&device->description[index], ", ", 2);
index += 2; index += 2;
} }
} }
const struct SoundIoChannelLayout *layout = soundio_channel_layout_get_default(client->ports.length); const struct SoundIoChannelLayout *layout = soundio_channel_layout_get_default(client->port_count);
if (layout) { if (layout) {
device->current_layout = *layout; device->current_layout = *layout;
} else { } else {
for (int port_index = 0; port_index < client->ports.length; port_index += 1) for (int port_index = 0; port_index < client->port_count; port_index += 1)
device->current_layout.channels[port_index] = SoundIoChannelIdInvalid; device->current_layout.channels[port_index] = SoundIoChannelIdInvalid;
} }
@ -355,7 +434,7 @@ static int process_callback(jack_nframes_t nframes, void *arg) {
static int buffer_size_callback(jack_nframes_t nframes, void *arg) { static int buffer_size_callback(jack_nframes_t nframes, void *arg) {
SoundIoPrivate *si = (SoundIoPrivate *)arg; SoundIoPrivate *si = (SoundIoPrivate *)arg;
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = &si->backend_data.jack;
sij->buffer_size = nframes; sij->buffer_size = nframes;
if (sij->initialized) if (sij->initialized)
refresh_devices(si); refresh_devices(si);
@ -364,7 +443,7 @@ static int buffer_size_callback(jack_nframes_t nframes, void *arg) {
static int sample_rate_callback(jack_nframes_t nframes, void *arg) { static int sample_rate_callback(jack_nframes_t nframes, void *arg) {
SoundIoPrivate *si = (SoundIoPrivate *)arg; SoundIoPrivate *si = (SoundIoPrivate *)arg;
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = &si->backend_data.jack;
sij->sample_rate = nframes; sij->sample_rate = nframes;
if (sij->initialized) if (sij->initialized)
refresh_devices(si); refresh_devices(si);
@ -379,7 +458,7 @@ static int xrun_callback(void *arg) {
static void port_registration_callback(jack_port_id_t port_id, int reg, void *arg) { static void port_registration_callback(jack_port_id_t port_id, int reg, void *arg) {
SoundIoPrivate *si = (SoundIoPrivate *)arg; SoundIoPrivate *si = (SoundIoPrivate *)arg;
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = &si->backend_data.jack;
if (sij->initialized) if (sij->initialized)
refresh_devices(si); refresh_devices(si);
} }
@ -388,7 +467,7 @@ static int port_rename_calllback(jack_port_id_t port_id,
const char *old_name, const char *new_name, void *arg) const char *old_name, const char *new_name, void *arg)
{ {
SoundIoPrivate *si = (SoundIoPrivate *)arg; SoundIoPrivate *si = (SoundIoPrivate *)arg;
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = &si->backend_data.jack;
if (sij->initialized) if (sij->initialized)
refresh_devices(si); refresh_devices(si);
return 0; return 0;
@ -400,9 +479,7 @@ static void shutdown_callback(void *arg) {
} }
static void destroy_jack(SoundIoPrivate *si) { static void destroy_jack(SoundIoPrivate *si) {
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = &si->backend_data.jack;
if (!sij)
return;
if (sij->client) if (sij->client)
jack_client_close(sij->client); jack_client_close(sij->client);
@ -414,12 +491,10 @@ static void destroy_jack(SoundIoPrivate *si) {
soundio_os_mutex_destroy(sij->mutex); soundio_os_mutex_destroy(sij->mutex);
soundio_destroy_devices_info(sij->ready_devices_info); soundio_destroy_devices_info(sij->ready_devices_info);
destroy(sij);
si->backend_data = nullptr;
} }
int soundio_jack_init(struct SoundIoPrivate *si) { int soundio_jack_init(struct SoundIoPrivate *si) {
SoundIoJack *sij = &si->backend_data.jack;
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
if (!global_msg_callback_flag.test_and_set()) { if (!global_msg_callback_flag.test_and_set()) {
@ -430,14 +505,6 @@ int soundio_jack_init(struct SoundIoPrivate *si) {
global_msg_callback_flag.clear(); global_msg_callback_flag.clear();
} }
assert(!si->backend_data);
SoundIoJack *sij = create<SoundIoJack>();
if (!sij) {
destroy_jack(si);
return SoundIoErrorNoMem;
}
si->backend_data = sij;
sij->mutex = soundio_os_mutex_create(); sij->mutex = soundio_os_mutex_create();
if (!sij->mutex) { if (!sij->mutex) {
destroy_jack(si); destroy_jack(si);

View file

@ -8,7 +8,26 @@
#ifndef SOUNDIO_JACK_HPP #ifndef SOUNDIO_JACK_HPP
#define SOUNDIO_JACK_HPP #define SOUNDIO_JACK_HPP
#include "os.hpp"
#include <jack/jack.h>
int soundio_jack_init(struct SoundIoPrivate *si); int soundio_jack_init(struct SoundIoPrivate *si);
struct SoundIoDeviceJack {
};
struct SoundIoJack {
jack_client_t *client;
SoundIoOsMutex *mutex;
SoundIoOsCond *cond;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
bool initialized;
int sample_rate;
int buffer_size;
};
#endif #endif

View file

@ -7,13 +7,11 @@
#include "pulseaudio.hpp" #include "pulseaudio.hpp"
#include "soundio.hpp" #include "soundio.hpp"
#include "atomics.hpp"
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <pulse/pulseaudio.h>
struct SoundIoOutStreamPulseAudio { struct SoundIoOutStreamPulseAudio {
pa_stream *stream; pa_stream *stream;
@ -30,43 +28,17 @@ struct SoundIoInStreamPulseAudio {
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };
struct SoundIoPulseAudio {
bool connection_refused;
pa_context *pulse_context;
atomic_bool device_scan_queued;
// the one that we're working on building
struct SoundIoDevicesInfo *current_devices_info;
char * default_sink_name;
char * default_source_name;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
bool have_sink_list;
bool have_source_list;
bool have_default_sink;
atomic_bool ready_flag;
atomic_bool have_devices_flag;
pa_threaded_mainloop *main_loop;
pa_proplist *props;
};
static void subscribe_callback(pa_context *context, static void subscribe_callback(pa_context *context,
pa_subscription_event_type_t event_bits, uint32_t index, void *userdata) pa_subscription_event_type_t event_bits, uint32_t index, void *userdata)
{ {
SoundIoPrivate *si = (SoundIoPrivate *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
sipa->device_scan_queued = true; sipa->device_scan_queued = true;
pa_threaded_mainloop_signal(sipa->main_loop, 0); pa_threaded_mainloop_signal(sipa->main_loop, 0);
} }
static void subscribe_to_events(SoundIoPrivate *si) { static void subscribe_to_events(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
pa_subscription_mask_t events = (pa_subscription_mask_t)( pa_subscription_mask_t events = (pa_subscription_mask_t)(
PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SERVER); PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SERVER);
pa_operation *subscribe_op = pa_context_subscribe(sipa->pulse_context, pa_operation *subscribe_op = pa_context_subscribe(sipa->pulse_context,
@ -78,7 +50,7 @@ static void subscribe_to_events(SoundIoPrivate *si) {
static void context_state_callback(pa_context *context, void *userdata) { static void context_state_callback(pa_context *context, void *userdata) {
SoundIoPrivate *si = (SoundIoPrivate *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
switch (pa_context_get_state(context)) { switch (pa_context_get_state(context)) {
case PA_CONTEXT_UNCONNECTED: // The context hasn't been connected yet. case PA_CONTEXT_UNCONNECTED: // The context hasn't been connected yet.
@ -112,9 +84,7 @@ static void context_state_callback(pa_context *context, void *userdata) {
} }
static void destroy_pa(SoundIoPrivate *si) { static void destroy_pa(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
if (!sipa)
return;
if (sipa->main_loop) if (sipa->main_loop)
pa_threaded_mainloop_stop(sipa->main_loop); pa_threaded_mainloop_stop(sipa->main_loop);
@ -133,9 +103,6 @@ static void destroy_pa(SoundIoPrivate *si) {
free(sipa->default_sink_name); free(sipa->default_sink_name);
free(sipa->default_source_name); free(sipa->default_source_name);
destroy(sipa);
si->backend_data = nullptr;
} }
static SoundIoFormat from_pulseaudio_format(pa_sample_spec sample_spec) { static SoundIoFormat from_pulseaudio_format(pa_sample_spec sample_spec) {
@ -232,7 +199,7 @@ static int set_all_device_formats(SoundIoDevice *device) {
} }
static int perform_operation(SoundIoPrivate *si, pa_operation *op) { static int perform_operation(SoundIoPrivate *si, pa_operation *op) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
for (;;) { for (;;) {
switch (pa_operation_get_state(op)) { switch (pa_operation_get_state(op)) {
case PA_OPERATION_RUNNING: case PA_OPERATION_RUNNING:
@ -250,7 +217,7 @@ static int perform_operation(SoundIoPrivate *si, pa_operation *op) {
static void finish_device_query(SoundIoPrivate *si) { static void finish_device_query(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
if (!sipa->have_sink_list || if (!sipa->have_sink_list ||
!sipa->have_source_list || !sipa->have_source_list ||
@ -298,7 +265,7 @@ static void finish_device_query(SoundIoPrivate *si) {
static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *info, int eol, void *userdata) { static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *info, int eol, void *userdata) {
SoundIoPrivate *si = (SoundIoPrivate *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
int err; int err;
if (eol) { if (eol) {
sipa->have_sink_list = true; sipa->have_sink_list = true;
@ -348,7 +315,7 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in
static void source_info_callback(pa_context *pulse_context, const pa_source_info *info, int eol, void *userdata) { static void source_info_callback(pa_context *pulse_context, const pa_source_info *info, int eol, void *userdata) {
SoundIoPrivate *si = (SoundIoPrivate *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
int err; int err;
if (eol) { if (eol) {
sipa->have_source_list = true; sipa->have_source_list = true;
@ -399,7 +366,7 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info
static void server_info_callback(pa_context *pulse_context, const pa_server_info *info, void *userdata) { static void server_info_callback(pa_context *pulse_context, const pa_server_info *info, void *userdata) {
SoundIoPrivate *si = (SoundIoPrivate *)userdata; SoundIoPrivate *si = (SoundIoPrivate *)userdata;
assert(si); assert(si);
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
free(sipa->default_sink_name); free(sipa->default_sink_name);
free(sipa->default_source_name); free(sipa->default_source_name);
@ -416,7 +383,7 @@ static void server_info_callback(pa_context *pulse_context, const pa_server_info
} }
static void scan_devices(SoundIoPrivate *si) { static void scan_devices(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
sipa->have_sink_list = false; sipa->have_sink_list = false;
sipa->have_default_sink = false; sipa->have_default_sink = false;
@ -449,7 +416,7 @@ static void scan_devices(SoundIoPrivate *si) {
} }
static void block_until_have_devices(SoundIoPrivate *si) { static void block_until_have_devices(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
if (sipa->have_devices_flag) if (sipa->have_devices_flag)
return; return;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
@ -460,7 +427,7 @@ static void block_until_have_devices(SoundIoPrivate *si) {
} }
static void block_until_ready(SoundIoPrivate *si) { static void block_until_ready(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
if (sipa->ready_flag) if (sipa->ready_flag)
return; return;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
@ -474,7 +441,7 @@ static void flush_events(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
block_until_ready(si); block_until_ready(si);
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
if (sipa->device_scan_queued) { if (sipa->device_scan_queued) {
sipa->device_scan_queued = false; sipa->device_scan_queued = false;
@ -504,13 +471,13 @@ static void flush_events(SoundIoPrivate *si) {
} }
static void wait_events(SoundIoPrivate *si) { static void wait_events(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
flush_events(si); flush_events(si);
pa_threaded_mainloop_wait(sipa->main_loop); pa_threaded_mainloop_wait(sipa->main_loop);
} }
static void wakeup(SoundIoPrivate *si) { static void wakeup(SoundIoPrivate *si) {
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
pa_threaded_mainloop_signal(sipa->main_loop, 0); pa_threaded_mainloop_signal(sipa->main_loop, 0);
} }
@ -602,7 +569,7 @@ static void playback_stream_state_callback(pa_stream *stream, void *userdata) {
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
SoundIo *soundio = outstream->device->soundio; SoundIo *soundio = outstream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio; SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
switch (pa_stream_get_state(stream)) { switch (pa_stream_get_state(stream)) {
case PA_STREAM_UNCONNECTED: case PA_STREAM_UNCONNECTED:
@ -635,7 +602,7 @@ static void outstream_destroy_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os
if (!ospa) if (!ospa)
return; return;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
pa_stream *stream = ospa->stream; pa_stream *stream = ospa->stream;
if (stream) { if (stream) {
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
@ -666,7 +633,7 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
} }
os->backend_data = ospa; os->backend_data = ospa;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
ospa->stream_ready = false; ospa->stream_ready = false;
assert(sipa->pulse_context); assert(sipa->pulse_context);
@ -718,7 +685,7 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) { static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
@ -786,7 +753,7 @@ static int outstream_clear_buffer_pa(SoundIoPrivate *si,
SoundIoOutStreamPrivate *os) SoundIoOutStreamPrivate *os)
{ {
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
pa_stream *stream = ospa->stream; pa_stream *stream = ospa->stream;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
pa_operation *op = pa_stream_flush(stream, NULL, NULL); pa_operation *op = pa_stream_flush(stream, NULL, NULL);
@ -799,7 +766,7 @@ static int outstream_clear_buffer_pa(SoundIoPrivate *si,
static int outstream_pause_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, bool pause) { static int outstream_pause_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, bool pause) {
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
@ -821,7 +788,7 @@ static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
SoundIo *soundio = instream->device->soundio; SoundIo *soundio = instream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio; SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
switch (pa_stream_get_state(stream)) { switch (pa_stream_get_state(stream)) {
case PA_STREAM_UNCONNECTED: case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING: case PA_STREAM_CREATING:
@ -852,7 +819,7 @@ static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *inst
if (!ispa) if (!ispa)
return; return;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
pa_stream *stream = ispa->stream; pa_stream *stream = ispa->stream;
if (stream) { if (stream) {
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
@ -878,7 +845,7 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
} }
is->backend_data = ispa; is->backend_data = ispa;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
ispa->stream_ready = false; ispa->stream_ready = false;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
@ -923,7 +890,7 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
pa_stream_flags_t flags = (instream->period_duration > 0.0) ? PA_STREAM_ADJUST_LATENCY : PA_STREAM_NOFLAGS; pa_stream_flags_t flags = (instream->period_duration > 0.0) ? PA_STREAM_ADJUST_LATENCY : PA_STREAM_NOFLAGS;
@ -991,7 +958,7 @@ static int instream_end_read_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is)
static int instream_pause_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) { static int instream_pause_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) {
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
pa_threaded_mainloop_lock(sipa->main_loop); pa_threaded_mainloop_lock(sipa->main_loop);
@ -1009,14 +976,7 @@ static int instream_pause_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, boo
int soundio_pulseaudio_init(SoundIoPrivate *si) { int soundio_pulseaudio_init(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
assert(!si->backend_data);
SoundIoPulseAudio *sipa = create<SoundIoPulseAudio>();
if (!sipa) {
destroy_pa(si);
return SoundIoErrorNoMem;
}
si->backend_data = sipa;
sipa->connection_refused = false; sipa->connection_refused = false;
sipa->device_scan_queued = false; sipa->device_scan_queued = false;

View file

@ -8,6 +8,39 @@
#ifndef SOUNDIO_PULSEAUDIO_HPP #ifndef SOUNDIO_PULSEAUDIO_HPP
#define SOUNDIO_PULSEAUDIO_HPP #define SOUNDIO_PULSEAUDIO_HPP
#include "atomics.hpp"
#include <pulse/pulseaudio.h>
int soundio_pulseaudio_init(struct SoundIoPrivate *si); int soundio_pulseaudio_init(struct SoundIoPrivate *si);
struct SoundIoDevicePulseAudio {
};
struct SoundIoPulseAudio {
bool connection_refused;
pa_context *pulse_context;
atomic_bool device_scan_queued;
// the one that we're working on building
struct SoundIoDevicesInfo *current_devices_info;
char *default_sink_name;
char *default_source_name;
// this one is ready to be read with flush_events. protected by mutex
struct SoundIoDevicesInfo *ready_devices_info;
bool have_sink_list;
bool have_source_list;
bool have_default_sink;
atomic_bool ready_flag;
atomic_bool have_devices_flag;
pa_threaded_mainloop *main_loop;
pa_proplist *props;
};
#endif #endif

View file

@ -7,22 +7,9 @@
#include "soundio.hpp" #include "soundio.hpp"
#include "util.hpp" #include "util.hpp"
#include "dummy.hpp"
#include "os.hpp" #include "os.hpp"
#include "config.h" #include "config.h"
#ifdef SOUNDIO_HAVE_JACK
#include "jack.hpp"
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
#include "pulseaudio.hpp"
#endif
#ifdef SOUNDIO_HAVE_ALSA
#include "alsa.hpp"
#endif
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
@ -71,6 +58,7 @@ const char *soundio_strerror(int error) {
case SoundIoErrorStreaming: return "unrecoverable streaming failure"; case SoundIoErrorStreaming: return "unrecoverable streaming failure";
case SoundIoErrorIncompatibleDevice: return "incompatible device"; case SoundIoErrorIncompatibleDevice: return "incompatible device";
case SoundIoErrorNoSuchClient: return "no such client"; case SoundIoErrorNoSuchClient: return "no such client";
case SoundIoErrorIncompatibleBackend: return "incompatible backend";
} }
soundio_panic("invalid error enum value: %d", error); soundio_panic("invalid error enum value: %d", error);
} }
@ -198,6 +186,8 @@ int soundio_connect_backend(SoundIo *soundio, SoundIoBackend backend) {
if (!fn) if (!fn)
return SoundIoErrorBackendUnavailable; return SoundIoErrorBackendUnavailable;
memset(&si->backend_data, 0, sizeof(SoundIoBackendData));
int err; int err;
if ((err = backend_init_fns[backend](si))) { if ((err = backend_init_fns[backend](si))) {
soundio_disconnect(soundio); soundio_disconnect(soundio);
@ -211,7 +201,6 @@ void soundio_disconnect(struct SoundIo *soundio) {
if (si->destroy) if (si->destroy)
si->destroy(si); si->destroy(si);
assert(!si->backend_data);
soundio->current_backend = SoundIoBackendNone; soundio->current_backend = SoundIoBackendNone;

View file

@ -27,6 +27,7 @@ enum SoundIoError {
SoundIoErrorStreaming, SoundIoErrorStreaming,
SoundIoErrorIncompatibleDevice, SoundIoErrorIncompatibleDevice,
SoundIoErrorNoSuchClient, SoundIoErrorNoSuchClient,
SoundIoErrorIncompatibleBackend,
}; };
enum SoundIoChannelId { enum SoundIoChannelId {
@ -210,6 +211,7 @@ struct SoundIo {
// Optional: Application name. // Optional: Application name.
// PulseAudio uses this for "application name". // PulseAudio uses this for "application name".
// JACK uses this for `client_name`. // JACK uses this for `client_name`.
// Must not contain a colon (":").
const char *app_name; const char *app_name;
// Optional: JACK info and error callbacks. // Optional: JACK info and error callbacks.
@ -371,8 +373,14 @@ struct SoundIoOutStream {
// PulseAudio uses this for the stream name. // PulseAudio uses this for the stream name.
// JACK uses this for the client name of the client that connects when you // JACK uses this for the client name of the client that connects when you
// open the stream. // open the stream.
// Must not contain a colon (":").
const char *name; const char *name;
// Optional: Hint that this output stream is nonterminal. This is used by
// JACK and it means that the output stream data originates from an input
// stream. Defaults to `false`.
bool non_terminal_hint;
// computed automatically when you call soundio_outstream_open // computed automatically when you call soundio_outstream_open
int bytes_per_frame; int bytes_per_frame;
@ -425,6 +433,7 @@ struct SoundIoInStream {
// PulseAudio uses this for the stream name. // PulseAudio uses this for the stream name.
// JACK uses this for the client name of the client that connects when you // JACK uses this for the client name of the client that connects when you
// open the stream. // open the stream.
// Must not contain a colon (":").
const char *name; const char *name;
// computed automatically when you call soundio_instream_open // computed automatically when you call soundio_instream_open

View file

@ -11,6 +11,46 @@
#include "soundio.h" #include "soundio.h"
#include "list.hpp" #include "list.hpp"
#ifdef SOUNDIO_HAVE_JACK
#include "jack.hpp"
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
#include "pulseaudio.hpp"
#endif
#ifdef SOUNDIO_HAVE_ALSA
#include "alsa.hpp"
#endif
#include "dummy.hpp"
union SoundIoBackendData {
#ifdef SOUNDIO_HAVE_JACK
SoundIoJack jack;
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
SoundIoPulseAudio pulseaudio;
#endif
#ifdef SOUNDIO_HAVE_ALSA
SoundIoAlsa alsa;
#endif
SoundIoDummy dummy;
};
union SoundIoDeviceBackendData {
#ifdef SOUNDIO_HAVE_JACK
SoundIoDeviceJack jack;
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
SoundIoDevicePulseAudio pulseaudio;
#endif
#ifdef SOUNDIO_HAVE_ALSA
SoundIoDeviceAlsa alsa;
#endif
SoundIoDeviceDummy dummy;
};
struct SoundIoDevicesInfo { struct SoundIoDevicesInfo {
SoundIoList<SoundIoDevice *> input_devices; SoundIoList<SoundIoDevice *> input_devices;
SoundIoList<SoundIoDevice *> output_devices; SoundIoList<SoundIoDevice *> output_devices;
@ -35,7 +75,7 @@ struct SoundIoPrivate {
// Safe to read from a single thread without a mutex. // Safe to read from a single thread without a mutex.
struct SoundIoDevicesInfo *safe_devices_info; struct SoundIoDevicesInfo *safe_devices_info;
void *backend_data; SoundIoBackendData backend_data;
void (*destroy)(struct SoundIoPrivate *); void (*destroy)(struct SoundIoPrivate *);
void (*flush_events)(struct SoundIoPrivate *); void (*flush_events)(struct SoundIoPrivate *);
void (*wait_events)(struct SoundIoPrivate *); void (*wait_events)(struct SoundIoPrivate *);
@ -60,6 +100,11 @@ struct SoundIoPrivate {
int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause); int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause);
}; };
struct SoundIoDevicePrivate {
SoundIoDevice pub;
SoundIoDeviceBackendData backend_data;
};
void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info); void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info);