mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 22:35:28 +00:00
SoundIo, SoundIoDevice: unions instead of void *
for better cache locality
This commit is contained in:
parent
85549b59b7
commit
db1195877a
|
@ -233,12 +233,16 @@ view `coverage/index.html` in a browser.
|
|||
## Roadmap
|
||||
|
||||
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. merge in/out stream structures and functions?
|
||||
0. implement CoreAudio (OSX) backend, get examples working
|
||||
0. implement WASAPI (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
|
||||
which is why we have to pre-fill the ring buffer with silence for
|
||||
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
|
||||
call lock and then unlock when done.
|
||||
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
|
||||
- make sure every function which can return an error documents which errors
|
||||
it can return
|
||||
|
|
42
src/alsa.cpp
42
src/alsa.cpp
|
@ -7,8 +7,6 @@
|
|||
|
||||
#include "alsa.hpp"
|
||||
#include "soundio.hpp"
|
||||
#include "os.hpp"
|
||||
#include "atomics.hpp"
|
||||
|
||||
#include <alsa/asoundlib.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 {
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_chmap_t *chmap;
|
||||
|
@ -85,9 +68,7 @@ static void wakeup_device_poll(SoundIoAlsa *sia) {
|
|||
}
|
||||
|
||||
static void destroy_alsa(SoundIoPrivate *si) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
if (!sia)
|
||||
return;
|
||||
SoundIoAlsa *sia = &si->backend_data.alsa;
|
||||
|
||||
if (sia->thread) {
|
||||
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[1]);
|
||||
close(sia->notify_fd);
|
||||
|
||||
destroy(sia);
|
||||
si->backend_data = nullptr;
|
||||
}
|
||||
|
||||
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) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
SoundIoAlsa *sia = &si->backend_data.alsa;
|
||||
|
||||
SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>();
|
||||
if (!devices_info)
|
||||
|
@ -761,7 +739,7 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
|
||||
static void device_thread_run(void *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
|
||||
// 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) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
SoundIoAlsa *sia = &si->backend_data.alsa;
|
||||
block_until_have_devices(sia);
|
||||
|
||||
bool change = false;
|
||||
|
@ -888,7 +866,7 @@ static void flush_events(SoundIoPrivate *si) {
|
|||
}
|
||||
|
||||
static void wait_events(SoundIoPrivate *si) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
SoundIoAlsa *sia = &si->backend_data.alsa;
|
||||
flush_events(si);
|
||||
soundio_os_mutex_lock(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) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
SoundIoAlsa *sia = &si->backend_data.alsa;
|
||||
soundio_os_mutex_lock(sia->mutex);
|
||||
soundio_os_cond_signal(sia->cond, 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) {
|
||||
SoundIoAlsa *sia = &si->backend_data.alsa;
|
||||
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_wd = -1;
|
||||
sia->have_devices_flag.store(false);
|
||||
|
|
23
src/alsa.hpp
23
src/alsa.hpp
|
@ -8,7 +8,28 @@
|
|||
#ifndef SOUNDIO_ALSA_HPP
|
||||
#define SOUNDIO_ALSA_HPP
|
||||
|
||||
#include "os.hpp"
|
||||
#include "atomics.hpp"
|
||||
|
||||
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
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include "dummy.hpp"
|
||||
#include "soundio.hpp"
|
||||
#include "os.hpp"
|
||||
#include "atomics.hpp"
|
||||
#include "ring_buffer.hpp"
|
||||
|
||||
|
@ -39,12 +38,6 @@ struct SoundIoInStreamDummy {
|
|||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct SoundIoDummy {
|
||||
SoundIoOsMutex *mutex;
|
||||
SoundIoOsCond *cond;
|
||||
bool devices_emitted;
|
||||
};
|
||||
|
||||
static void playback_thread_run(void *arg) {
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
|
@ -127,23 +120,18 @@ static void capture_thread_run(void *arg) {
|
|||
}
|
||||
|
||||
static void destroy_dummy(SoundIoPrivate *si) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
|
||||
if (!sid)
|
||||
return;
|
||||
SoundIoDummy *sid = &si->backend_data.dummy;
|
||||
|
||||
if (sid->cond)
|
||||
soundio_os_cond_destroy(sid->cond);
|
||||
|
||||
if (sid->mutex)
|
||||
soundio_os_mutex_destroy(sid->mutex);
|
||||
|
||||
destroy(sid);
|
||||
si->backend_data = nullptr;
|
||||
}
|
||||
|
||||
static void flush_events(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
|
||||
SoundIoDummy *sid = &si->backend_data.dummy;
|
||||
if (sid->devices_emitted)
|
||||
return;
|
||||
sid->devices_emitted = true;
|
||||
|
@ -151,13 +139,13 @@ static void flush_events(SoundIoPrivate *si) {
|
|||
}
|
||||
|
||||
static void wait_events(SoundIoPrivate *si) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
|
||||
SoundIoDummy *sid = &si->backend_data.dummy;
|
||||
flush_events(si);
|
||||
soundio_os_cond_wait(sid->cond, nullptr);
|
||||
}
|
||||
|
||||
static void wakeup(SoundIoPrivate *si) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
|
||||
SoundIoDummy *sid = &si->backend_data.dummy;
|
||||
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) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
assert(!si->backend_data);
|
||||
SoundIoDummy *sid = create<SoundIoDummy>();
|
||||
if (!sid) {
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
si->backend_data = sid;
|
||||
SoundIoDummy *sid = &si->backend_data.dummy;
|
||||
|
||||
sid->mutex = soundio_os_mutex_create();
|
||||
if (!sid->mutex) {
|
||||
|
|
|
@ -8,6 +8,18 @@
|
|||
#ifndef SOUNDIO_DUMMY_HPP
|
||||
#define SOUNDIO_DUMMY_HPP
|
||||
|
||||
#include "os.hpp"
|
||||
|
||||
int soundio_dummy_init(struct SoundIoPrivate *si);
|
||||
|
||||
struct SoundIoDummy {
|
||||
SoundIoOsMutex *mutex;
|
||||
SoundIoOsCond *cond;
|
||||
bool devices_emitted;
|
||||
};
|
||||
|
||||
struct SoundIoDeviceDummy {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
203
src/jack.cpp
203
src/jack.cpp
|
@ -8,23 +8,15 @@
|
|||
#include "jack.hpp"
|
||||
#include "soundio.hpp"
|
||||
#include "atomics.hpp"
|
||||
#include "os.hpp"
|
||||
#include "list.hpp"
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static atomic_flag global_msg_callback_flag = ATOMIC_FLAG_INIT;
|
||||
|
||||
struct SoundIoJack {
|
||||
struct SoundIoOutStreamJack {
|
||||
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;
|
||||
jack_port_t *ports[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct SoundIoJackPort {
|
||||
|
@ -37,12 +29,13 @@ struct SoundIoJackClient {
|
|||
int name_len;
|
||||
bool is_physical;
|
||||
SoundIoDevicePurpose purpose;
|
||||
SoundIoList<SoundIoJackPort> ports;
|
||||
SoundIoJackPort ports[SOUNDIO_MAX_CHANNELS];
|
||||
int port_count;
|
||||
};
|
||||
|
||||
static void flush_events_jack(struct SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
|
||||
bool change = false;
|
||||
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) {
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
flush_events_jack(si);
|
||||
soundio_os_mutex_lock(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) {
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
soundio_os_mutex_lock(sij->mutex);
|
||||
soundio_os_cond_signal(sij->cond, sij->mutex);
|
||||
soundio_os_mutex_unlock(sij->mutex);
|
||||
}
|
||||
|
||||
|
||||
static int outstream_open_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *) {
|
||||
soundio_panic("TODO");
|
||||
static int outstream_process_callback(jack_nframes_t nframes, void *arg) {
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
outstream->write_callback(outstream, nframes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void outstream_destroy_jack(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *) {
|
||||
soundio_panic("TODO");
|
||||
static void outstream_destroy_jack(struct SoundIoPrivate *is, struct SoundIoOutStreamPrivate *os) {
|
||||
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 *) {
|
||||
soundio_panic("TODO");
|
||||
static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
||||
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 *,
|
||||
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) {
|
||||
soundio_panic("TODO");
|
||||
soundio_panic("TODO end write");
|
||||
}
|
||||
|
||||
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 *) {
|
||||
soundio_panic("TODO");
|
||||
soundio_panic("TODO open instream");
|
||||
}
|
||||
|
||||
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 *) {
|
||||
soundio_panic("TODO");
|
||||
soundio_panic("TODO start instream");
|
||||
}
|
||||
|
||||
static int instream_begin_read_jack(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
|
||||
SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
soundio_panic("TODO begin read");
|
||||
}
|
||||
|
||||
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) {
|
||||
soundio_panic("TODO");
|
||||
soundio_panic("TODO pause");
|
||||
}
|
||||
|
||||
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->name = client_name;
|
||||
client->name_len = client_name_len;
|
||||
client->port_count = 0;
|
||||
return client;
|
||||
}
|
||||
|
||||
|
@ -193,7 +268,7 @@ static char *dupe_str(const char *str, int str_len) {
|
|||
|
||||
static int refresh_devices(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
|
||||
SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>();
|
||||
if (!devices_info)
|
||||
|
@ -208,13 +283,18 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
}
|
||||
|
||||
SoundIoList<SoundIoJackClient> clients;
|
||||
int err;
|
||||
|
||||
const char **port_name_ptr = port_names;
|
||||
while (*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);
|
||||
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) ?
|
||||
SoundIoDevicePurposeOutput : SoundIoDevicePurposeInput;
|
||||
bool is_physical = flags & JackPortIsPhysical;
|
||||
|
@ -236,12 +316,11 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
destroy(devices_info);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
if ((err = client->ports.add_one())) {
|
||||
jack_free(port_names);
|
||||
destroy(devices_info);
|
||||
return SoundIoErrorNoMem;
|
||||
if (client->port_count >= SOUNDIO_MAX_CHANNELS) {
|
||||
// we hit the channel limit, skip the leftovers
|
||||
continue;
|
||||
}
|
||||
SoundIoJackPort *port = &client->ports.last();
|
||||
SoundIoJackPort *port = &client->ports[client->port_count++];
|
||||
port->name = port_name;
|
||||
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) {
|
||||
SoundIoJackClient *client = &clients.at(i);
|
||||
if (client->ports.length <= 0)
|
||||
if (client->port_count <= 0)
|
||||
continue;
|
||||
|
||||
SoundIoDevice *device = create<SoundIoDevice>();
|
||||
|
@ -260,9 +339,9 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
destroy(devices_info);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
int description_len = client->name_len + 3 + 2 * client->ports.length;
|
||||
for (int port_index = 0; port_index < client->ports.length; port_index += 1) {
|
||||
SoundIoJackPort *port = &client->ports.at(port_index);
|
||||
int description_len = client->name_len + 3 + 2 * client->port_count;
|
||||
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
|
||||
SoundIoJackPort *port = &client->ports[port_index];
|
||||
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_len], ": ", 2);
|
||||
int index = client->name_len + 2;
|
||||
for (int port_index = 0; port_index < client->ports.length; port_index += 1) {
|
||||
SoundIoJackPort *port = &client->ports.at(port_index);
|
||||
for (int port_index = 0; port_index < client->port_count; port_index += 1) {
|
||||
SoundIoJackPort *port = &client->ports[port_index];
|
||||
memcpy(&device->description[index], port->name, 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);
|
||||
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) {
|
||||
device->current_layout = *layout;
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
sij->buffer_size = nframes;
|
||||
if (sij->initialized)
|
||||
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) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
sij->sample_rate = nframes;
|
||||
if (sij->initialized)
|
||||
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) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
if (sij->initialized)
|
||||
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)
|
||||
{
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
if (sij->initialized)
|
||||
refresh_devices(si);
|
||||
return 0;
|
||||
|
@ -400,9 +479,7 @@ static void shutdown_callback(void *arg) {
|
|||
}
|
||||
|
||||
static void destroy_jack(SoundIoPrivate *si) {
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
if (!sij)
|
||||
return;
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
|
||||
if (sij->client)
|
||||
jack_client_close(sij->client);
|
||||
|
@ -414,12 +491,10 @@ static void destroy_jack(SoundIoPrivate *si) {
|
|||
soundio_os_mutex_destroy(sij->mutex);
|
||||
|
||||
soundio_destroy_devices_info(sij->ready_devices_info);
|
||||
|
||||
destroy(sij);
|
||||
si->backend_data = nullptr;
|
||||
}
|
||||
|
||||
int soundio_jack_init(struct SoundIoPrivate *si) {
|
||||
SoundIoJack *sij = &si->backend_data.jack;
|
||||
SoundIo *soundio = &si->pub;
|
||||
|
||||
if (!global_msg_callback_flag.test_and_set()) {
|
||||
|
@ -430,14 +505,6 @@ int soundio_jack_init(struct SoundIoPrivate *si) {
|
|||
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();
|
||||
if (!sij->mutex) {
|
||||
destroy_jack(si);
|
||||
|
|
19
src/jack.hpp
19
src/jack.hpp
|
@ -8,7 +8,26 @@
|
|||
#ifndef SOUNDIO_JACK_HPP
|
||||
#define SOUNDIO_JACK_HPP
|
||||
|
||||
#include "os.hpp"
|
||||
|
||||
#include <jack/jack.h>
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -7,13 +7,11 @@
|
|||
|
||||
#include "pulseaudio.hpp"
|
||||
#include "soundio.hpp"
|
||||
#include "atomics.hpp"
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
struct SoundIoOutStreamPulseAudio {
|
||||
pa_stream *stream;
|
||||
|
@ -30,43 +28,17 @@ struct SoundIoInStreamPulseAudio {
|
|||
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,
|
||||
pa_subscription_event_type_t event_bits, uint32_t index, void *userdata)
|
||||
{
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
sipa->device_scan_queued = true;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
}
|
||||
|
||||
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_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SERVER);
|
||||
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) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
switch (pa_context_get_state(context)) {
|
||||
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) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
if (!sipa)
|
||||
return;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
if (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_source_name);
|
||||
|
||||
destroy(sipa);
|
||||
si->backend_data = nullptr;
|
||||
}
|
||||
|
||||
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) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
for (;;) {
|
||||
switch (pa_operation_get_state(op)) {
|
||||
case PA_OPERATION_RUNNING:
|
||||
|
@ -250,7 +217,7 @@ static int perform_operation(SoundIoPrivate *si, pa_operation *op) {
|
|||
|
||||
static void finish_device_query(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
if (!sipa->have_sink_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) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
int err;
|
||||
if (eol) {
|
||||
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) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
int err;
|
||||
if (eol) {
|
||||
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) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
assert(si);
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
free(sipa->default_sink_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) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
sipa->have_sink_list = false;
|
||||
sipa->have_default_sink = false;
|
||||
|
@ -449,7 +416,7 @@ static void scan_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)
|
||||
return;
|
||||
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) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
if (sipa->ready_flag)
|
||||
return;
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
@ -474,7 +441,7 @@ static void flush_events(SoundIoPrivate *si) {
|
|||
SoundIo *soundio = &si->pub;
|
||||
block_until_ready(si);
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
if (sipa->device_scan_queued) {
|
||||
sipa->device_scan_queued = false;
|
||||
|
@ -504,13 +471,13 @@ static void flush_events(SoundIoPrivate *si) {
|
|||
}
|
||||
|
||||
static void wait_events(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
flush_events(si);
|
||||
pa_threaded_mainloop_wait(sipa->main_loop);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -602,7 +569,7 @@ static void playback_stream_state_callback(pa_stream *stream, void *userdata) {
|
|||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
switch (pa_stream_get_state(stream)) {
|
||||
case PA_STREAM_UNCONNECTED:
|
||||
|
@ -635,7 +602,7 @@ static void outstream_destroy_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os
|
|||
if (!ospa)
|
||||
return;
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
pa_stream *stream = ospa->stream;
|
||||
if (stream) {
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
@ -666,7 +633,7 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
|||
}
|
||||
os->backend_data = ospa;
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
ospa->stream_ready = false;
|
||||
|
||||
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) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
@ -786,7 +753,7 @@ static int outstream_clear_buffer_pa(SoundIoPrivate *si,
|
|||
SoundIoOutStreamPrivate *os)
|
||||
{
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
pa_stream *stream = ospa->stream;
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
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) {
|
||||
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);
|
||||
|
||||
|
@ -821,7 +788,7 @@ static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
|
|||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIo *soundio = instream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
switch (pa_stream_get_state(stream)) {
|
||||
case PA_STREAM_UNCONNECTED:
|
||||
case PA_STREAM_CREATING:
|
||||
|
@ -852,7 +819,7 @@ static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *inst
|
|||
if (!ispa)
|
||||
return;
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
pa_stream *stream = ispa->stream;
|
||||
if (stream) {
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
@ -878,7 +845,7 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
|||
}
|
||||
is->backend_data = ispa;
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
ispa->stream_ready = false;
|
||||
|
||||
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) {
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
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_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) {
|
||||
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);
|
||||
|
||||
|
@ -1009,14 +976,7 @@ static int instream_pause_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, boo
|
|||
|
||||
int soundio_pulseaudio_init(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
|
||||
assert(!si->backend_data);
|
||||
SoundIoPulseAudio *sipa = create<SoundIoPulseAudio>();
|
||||
if (!sipa) {
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
si->backend_data = sipa;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
sipa->connection_refused = false;
|
||||
sipa->device_scan_queued = false;
|
||||
|
|
|
@ -8,6 +8,39 @@
|
|||
#ifndef SOUNDIO_PULSEAUDIO_HPP
|
||||
#define SOUNDIO_PULSEAUDIO_HPP
|
||||
|
||||
#include "atomics.hpp"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
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
|
||||
|
|
|
@ -7,22 +7,9 @@
|
|||
|
||||
#include "soundio.hpp"
|
||||
#include "util.hpp"
|
||||
#include "dummy.hpp"
|
||||
#include "os.hpp"
|
||||
#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 <assert.h>
|
||||
|
||||
|
@ -71,6 +58,7 @@ const char *soundio_strerror(int error) {
|
|||
case SoundIoErrorStreaming: return "unrecoverable streaming failure";
|
||||
case SoundIoErrorIncompatibleDevice: return "incompatible device";
|
||||
case SoundIoErrorNoSuchClient: return "no such client";
|
||||
case SoundIoErrorIncompatibleBackend: return "incompatible backend";
|
||||
}
|
||||
soundio_panic("invalid error enum value: %d", error);
|
||||
}
|
||||
|
@ -198,6 +186,8 @@ int soundio_connect_backend(SoundIo *soundio, SoundIoBackend backend) {
|
|||
if (!fn)
|
||||
return SoundIoErrorBackendUnavailable;
|
||||
|
||||
memset(&si->backend_data, 0, sizeof(SoundIoBackendData));
|
||||
|
||||
int err;
|
||||
if ((err = backend_init_fns[backend](si))) {
|
||||
soundio_disconnect(soundio);
|
||||
|
@ -211,7 +201,6 @@ void soundio_disconnect(struct SoundIo *soundio) {
|
|||
|
||||
if (si->destroy)
|
||||
si->destroy(si);
|
||||
assert(!si->backend_data);
|
||||
|
||||
soundio->current_backend = SoundIoBackendNone;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ enum SoundIoError {
|
|||
SoundIoErrorStreaming,
|
||||
SoundIoErrorIncompatibleDevice,
|
||||
SoundIoErrorNoSuchClient,
|
||||
SoundIoErrorIncompatibleBackend,
|
||||
};
|
||||
|
||||
enum SoundIoChannelId {
|
||||
|
@ -210,6 +211,7 @@ struct SoundIo {
|
|||
// 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: JACK info and error callbacks.
|
||||
|
@ -371,8 +373,14 @@ struct SoundIoOutStream {
|
|||
// PulseAudio uses this for the stream name.
|
||||
// JACK uses this for the client name of the client that connects when you
|
||||
// open the stream.
|
||||
// Must not contain a colon (":").
|
||||
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
|
||||
int bytes_per_frame;
|
||||
|
@ -425,6 +433,7 @@ struct SoundIoInStream {
|
|||
// PulseAudio uses this for the stream name.
|
||||
// JACK uses this for the client name of the client that connects when you
|
||||
// open the stream.
|
||||
// Must not contain a colon (":").
|
||||
const char *name;
|
||||
|
||||
// computed automatically when you call soundio_instream_open
|
||||
|
|
|
@ -11,6 +11,46 @@
|
|||
#include "soundio.h"
|
||||
#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 {
|
||||
SoundIoList<SoundIoDevice *> input_devices;
|
||||
SoundIoList<SoundIoDevice *> output_devices;
|
||||
|
@ -35,7 +75,7 @@ struct SoundIoPrivate {
|
|||
// Safe to read from a single thread without a mutex.
|
||||
struct SoundIoDevicesInfo *safe_devices_info;
|
||||
|
||||
void *backend_data;
|
||||
SoundIoBackendData backend_data;
|
||||
void (*destroy)(struct SoundIoPrivate *);
|
||||
void (*flush_events)(struct SoundIoPrivate *);
|
||||
void (*wait_events)(struct SoundIoPrivate *);
|
||||
|
@ -60,6 +100,11 @@ struct SoundIoPrivate {
|
|||
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);
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue