mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 03:05:37 +00:00
JACK: determine channel layouts of devices
This commit is contained in:
parent
42961553d8
commit
f37ed6ddd4
|
@ -72,16 +72,6 @@ static struct SoundIoChannelLayout builtin_channel_layouts[] = {
|
|||
SoundIoChannelIdBackCenter,
|
||||
}
|
||||
},
|
||||
{
|
||||
"4.1",
|
||||
4,
|
||||
{
|
||||
SoundIoChannelIdFrontLeft,
|
||||
SoundIoChannelIdFrontRight,
|
||||
SoundIoChannelIdFrontCenter,
|
||||
SoundIoChannelIdLfe,
|
||||
}
|
||||
},
|
||||
{
|
||||
"Quad",
|
||||
4,
|
||||
|
@ -102,6 +92,17 @@ static struct SoundIoChannelLayout builtin_channel_layouts[] = {
|
|||
SoundIoChannelIdSideRight,
|
||||
}
|
||||
},
|
||||
{
|
||||
"4.1",
|
||||
5,
|
||||
{
|
||||
SoundIoChannelIdFrontLeft,
|
||||
SoundIoChannelIdFrontRight,
|
||||
SoundIoChannelIdFrontCenter,
|
||||
SoundIoChannelIdBackCenter,
|
||||
SoundIoChannelIdLfe,
|
||||
}
|
||||
},
|
||||
{
|
||||
"5.0",
|
||||
5,
|
||||
|
@ -395,3 +396,17 @@ bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout)
|
|||
layout->name = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const struct SoundIoChannelLayout *soundio_channel_layout_get_default(int channel_count) {
|
||||
switch (channel_count) {
|
||||
case 1: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
|
||||
case 2: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
|
||||
case 3: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId2Point1);
|
||||
case 4: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdQuad);
|
||||
case 5: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId4Point1);
|
||||
case 6: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId5Point1);
|
||||
case 7: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId6Point1);
|
||||
case 8: return soundio_channel_layout_get_builtin(SoundIoChannelLayoutId7Point1);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
202
src/jack.cpp
202
src/jack.cpp
|
@ -9,6 +9,7 @@
|
|||
#include "soundio.hpp"
|
||||
#include "atomics.hpp"
|
||||
#include "os.hpp"
|
||||
#include "list.hpp"
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <stdio.h>
|
||||
|
@ -26,6 +27,19 @@ struct SoundIoJack {
|
|||
int buffer_size;
|
||||
};
|
||||
|
||||
struct SoundIoJackPort {
|
||||
const char *name;
|
||||
int name_len;
|
||||
};
|
||||
|
||||
struct SoundIoJackClient {
|
||||
const char *name;
|
||||
int name_len;
|
||||
bool is_physical;
|
||||
SoundIoDevicePurpose purpose;
|
||||
SoundIoList<SoundIoJackPort> ports;
|
||||
};
|
||||
|
||||
static void flush_events_jack(struct SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
|
@ -124,6 +138,59 @@ static int instream_pause_jack(struct SoundIoPrivate *, struct SoundIoInStreamPr
|
|||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void split_str(const char *input_str, int input_str_len, char c,
|
||||
const char **out_1, int *out_len_1, const char **out_2, int *out_len_2)
|
||||
{
|
||||
*out_1 = input_str;
|
||||
while (*input_str) {
|
||||
if (*input_str == c) {
|
||||
*out_len_1 = input_str - *out_1;
|
||||
*out_2 = input_str + 1;
|
||||
*out_len_2 = input_str_len - 1 - *out_len_1;
|
||||
return;
|
||||
}
|
||||
input_str += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool eql_str(const char *str1, int str1_len, const char *str2, int str2_len) {
|
||||
if (str1_len != str2_len)
|
||||
return false;
|
||||
return memcmp(str1, str2, str1_len) == 0;
|
||||
}
|
||||
|
||||
static SoundIoJackClient *find_or_create_client(SoundIoList<SoundIoJackClient> *clients,
|
||||
SoundIoDevicePurpose purpose, bool is_physical, const char *client_name, int client_name_len)
|
||||
{
|
||||
for (int i = 0; i < clients->length; i += 1) {
|
||||
SoundIoJackClient *client = &clients->at(i);
|
||||
if (client->is_physical == is_physical &&
|
||||
client->purpose == purpose &&
|
||||
eql_str(client->name, client->name_len, client_name, client_name_len))
|
||||
{
|
||||
return client;
|
||||
}
|
||||
}
|
||||
int err;
|
||||
if ((err = clients->add_one()))
|
||||
return nullptr;
|
||||
SoundIoJackClient *client = &clients->last();
|
||||
client->is_physical = is_physical;
|
||||
client->purpose = purpose;
|
||||
client->name = client_name;
|
||||
client->name_len = client_name_len;
|
||||
return client;
|
||||
}
|
||||
|
||||
static char *dupe_str(const char *str, int str_len) {
|
||||
char *out = allocate_nonzero<char>(str_len + 1);
|
||||
if (!out)
|
||||
return nullptr;
|
||||
memcpy(out, str, str_len);
|
||||
out[str_len] = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
static int refresh_devices(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
|
@ -140,11 +207,52 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
SoundIoList<SoundIoJackClient> clients;
|
||||
int err;
|
||||
|
||||
const char **port_name_ptr = port_names;
|
||||
while (*port_name_ptr) {
|
||||
const char *port_name = *port_name_ptr;
|
||||
jack_port_t *port = jack_port_by_name(sij->client, port_name);
|
||||
int flags = jack_port_flags(port);
|
||||
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);
|
||||
SoundIoDevicePurpose purpose = (flags & JackPortIsInput) ?
|
||||
SoundIoDevicePurposeOutput : SoundIoDevicePurposeInput;
|
||||
bool is_physical = flags & JackPortIsPhysical;
|
||||
|
||||
const char *client_name = nullptr;
|
||||
const char *port_name = nullptr;
|
||||
int client_name_len;
|
||||
int port_name_len;
|
||||
split_str(client_and_port_name, strlen(client_and_port_name), ':',
|
||||
&client_name, &client_name_len, &port_name, &port_name_len);
|
||||
if (!client_name || !port_name) {
|
||||
// device does not have colon, skip it
|
||||
continue;
|
||||
}
|
||||
SoundIoJackClient *client = find_or_create_client(&clients, purpose, is_physical,
|
||||
client_name, client_name_len);
|
||||
if (!client) {
|
||||
jack_free(port_names);
|
||||
destroy(devices_info);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
if ((err = client->ports.add_one())) {
|
||||
jack_free(port_names);
|
||||
destroy(devices_info);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
SoundIoJackPort *port = &client->ports.last();
|
||||
port->name = port_name;
|
||||
port->name_len = port_name_len;
|
||||
|
||||
|
||||
port_name_ptr += 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < clients.length; i += 1) {
|
||||
SoundIoJackClient *client = &clients.at(i);
|
||||
if (client->ports.length <= 0)
|
||||
continue;
|
||||
|
||||
SoundIoDevice *device = create<SoundIoDevice>();
|
||||
if (!device) {
|
||||
|
@ -152,15 +260,29 @@ 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);
|
||||
description_len += port->name_len;
|
||||
}
|
||||
|
||||
device->ref_count = 1;
|
||||
device->soundio = soundio;
|
||||
device->is_raw = false;
|
||||
device->name = strdup(port_name);
|
||||
device->description = strdup(port_name);
|
||||
device->purpose = client->purpose;
|
||||
device->name = dupe_str(client->name, client->name_len);
|
||||
device->description = allocate<char>(description_len);
|
||||
device->layout_count = 1;
|
||||
device->layouts = create<SoundIoChannelLayout>();
|
||||
device->format_count = 1;
|
||||
device->formats = create<SoundIoFormat>();
|
||||
device->current_format = SoundIoFormatFloat32NE;
|
||||
device->sample_rate_min = sij->sample_rate;
|
||||
device->sample_rate_max = sij->sample_rate;
|
||||
device->sample_rate_current = sij->sample_rate;
|
||||
device->buffer_duration_min = sij->buffer_size / (double) sij->sample_rate;
|
||||
device->buffer_duration_max = device->buffer_duration_min;
|
||||
device->buffer_duration_current = device->buffer_duration_min;
|
||||
|
||||
if (!device->name || !device->description || !device->layouts || !device->formats) {
|
||||
jack_free(port_names);
|
||||
|
@ -169,48 +291,52 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
// TODO figure out how jack does channel layout
|
||||
device->layouts[0] = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
|
||||
device->current_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
|
||||
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);
|
||||
memcpy(&device->description[index], port->name, port->name_len);
|
||||
index += port->name_len;
|
||||
if (port_index + 1 < client->ports.length) {
|
||||
memcpy(&device->description[index], ", ", 2);
|
||||
index += 2;
|
||||
}
|
||||
}
|
||||
|
||||
device->formats[0] = SoundIoFormatFloat32NE;
|
||||
device->current_format = SoundIoFormatFloat32NE;
|
||||
|
||||
device->sample_rate_min = sij->sample_rate;
|
||||
device->sample_rate_max = sij->sample_rate;
|
||||
device->sample_rate_current = sij->sample_rate;
|
||||
|
||||
device->buffer_duration_min = sij->buffer_size / (double) sij->sample_rate;
|
||||
device->buffer_duration_max = device->buffer_duration_min;
|
||||
device->buffer_duration_current = device->buffer_duration_min;
|
||||
const struct SoundIoChannelLayout *layout = soundio_channel_layout_get_default(client->ports.length);
|
||||
if (layout) {
|
||||
device->current_layout = *layout;
|
||||
} else {
|
||||
for (int port_index = 0; port_index < client->ports.length; port_index += 1)
|
||||
device->current_layout.channels[port_index] = SoundIoChannelIdInvalid;
|
||||
}
|
||||
|
||||
device->layouts[0] = device->current_layout;
|
||||
device->formats[0] = device->current_format;
|
||||
|
||||
SoundIoList<SoundIoDevice *> *device_list;
|
||||
if (flags & JackPortIsInput) {
|
||||
device->purpose = SoundIoDevicePurposeOutput;
|
||||
if (device->purpose == SoundIoDevicePurposeOutput) {
|
||||
device_list = &devices_info->output_devices;
|
||||
if (devices_info->default_output_index < 0 && (flags & JackPortIsPhysical))
|
||||
if (devices_info->default_output_index < 0 && client->is_physical)
|
||||
devices_info->default_output_index = device_list->length;
|
||||
} else {
|
||||
assert(flags & JackPortIsOutput);
|
||||
device->purpose = SoundIoDevicePurposeInput;
|
||||
assert(device->purpose == SoundIoDevicePurposeInput);
|
||||
device_list = &devices_info->input_devices;
|
||||
if (devices_info->default_input_index < 0 && (flags & JackPortIsPhysical))
|
||||
if (devices_info->default_input_index < 0 && client->is_physical)
|
||||
devices_info->default_input_index = device_list->length;
|
||||
}
|
||||
|
||||
if (device_list->append(device)) {
|
||||
jack_free(port_names);
|
||||
soundio_device_unref(device);
|
||||
destroy(devices_info);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
port_name_ptr += 1;
|
||||
}
|
||||
|
||||
jack_free(port_names);
|
||||
|
||||
|
||||
soundio_os_mutex_lock(sij->mutex);
|
||||
soundio_destroy_devices_info(sij->ready_devices_info);
|
||||
sij->ready_devices_info = devices_info;
|
||||
|
@ -252,15 +378,19 @@ 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;
|
||||
soundio_panic("TODO port registration callback");
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
if (sij->initialized)
|
||||
refresh_devices(si);
|
||||
}
|
||||
|
||||
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;
|
||||
soundio_panic("TODO port rename callback");
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||
SoundIoJack *sij = (SoundIoJack *)si->backend_data;
|
||||
if (sij->initialized)
|
||||
refresh_devices(si);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -277,6 +407,14 @@ static void destroy_jack(SoundIoPrivate *si) {
|
|||
if (sij->client)
|
||||
jack_client_close(sij->client);
|
||||
|
||||
if (sij->cond)
|
||||
soundio_os_cond_destroy(sij->cond);
|
||||
|
||||
if (sij->mutex)
|
||||
soundio_os_mutex_destroy(sij->mutex);
|
||||
|
||||
soundio_destroy_devices_info(sij->ready_devices_info);
|
||||
|
||||
destroy(sij);
|
||||
si->backend_data = nullptr;
|
||||
}
|
||||
|
@ -312,6 +450,8 @@ int soundio_jack_init(struct SoundIoPrivate *si) {
|
|||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
// We pass JackNoStartServer due to
|
||||
// https://github.com/jackaudio/jack2/issues/138
|
||||
jack_status_t status;
|
||||
sij->client = jack_client_open(soundio->app_name, JackNoStartServer, &status);
|
||||
if (!sij->client) {
|
||||
|
|
|
@ -489,6 +489,9 @@ const char *soundio_get_channel_name(enum SoundIoChannelId id);
|
|||
int soundio_channel_layout_builtin_count(void);
|
||||
const struct SoundIoChannelLayout *soundio_channel_layout_get_builtin(int index);
|
||||
|
||||
// Get the default builtin channel layout for the given number of channels.
|
||||
const struct SoundIoChannelLayout *soundio_channel_layout_get_default(int channel_count);
|
||||
|
||||
int soundio_channel_layout_find_channel(
|
||||
const struct SoundIoChannelLayout *layout, enum SoundIoChannelId channel);
|
||||
|
||||
|
|
Loading…
Reference in a new issue