JACK: determine channel layouts of devices

This commit is contained in:
Andrew Kelley 2015-07-27 11:27:41 -07:00
parent 42961553d8
commit f37ed6ddd4
3 changed files with 199 additions and 41 deletions

View file

@ -72,16 +72,6 @@ static struct SoundIoChannelLayout builtin_channel_layouts[] = {
SoundIoChannelIdBackCenter, SoundIoChannelIdBackCenter,
} }
}, },
{
"4.1",
4,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdLfe,
}
},
{ {
"Quad", "Quad",
4, 4,
@ -102,6 +92,17 @@ static struct SoundIoChannelLayout builtin_channel_layouts[] = {
SoundIoChannelIdSideRight, SoundIoChannelIdSideRight,
} }
}, },
{
"4.1",
5,
{
SoundIoChannelIdFrontLeft,
SoundIoChannelIdFrontRight,
SoundIoChannelIdFrontCenter,
SoundIoChannelIdBackCenter,
SoundIoChannelIdLfe,
}
},
{ {
"5.0", "5.0",
5, 5,
@ -395,3 +396,17 @@ bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout)
layout->name = nullptr; layout->name = nullptr;
return false; 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;
}

View file

@ -9,6 +9,7 @@
#include "soundio.hpp" #include "soundio.hpp"
#include "atomics.hpp" #include "atomics.hpp"
#include "os.hpp" #include "os.hpp"
#include "list.hpp"
#include <jack/jack.h> #include <jack/jack.h>
#include <stdio.h> #include <stdio.h>
@ -26,6 +27,19 @@ struct SoundIoJack {
int buffer_size; 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) { static void flush_events_jack(struct SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = (SoundIoJack *)si->backend_data;
@ -124,6 +138,59 @@ static int instream_pause_jack(struct SoundIoPrivate *, struct SoundIoInStreamPr
soundio_panic("TODO"); 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) { static int refresh_devices(SoundIoPrivate *si) {
SoundIo *soundio = &si->pub; SoundIo *soundio = &si->pub;
SoundIoJack *sij = (SoundIoJack *)si->backend_data; SoundIoJack *sij = (SoundIoJack *)si->backend_data;
@ -140,11 +207,52 @@ static int refresh_devices(SoundIoPrivate *si) {
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
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 *port_name = *port_name_ptr; const char *client_and_port_name = *port_name_ptr;
jack_port_t *port = jack_port_by_name(sij->client, port_name); jack_port_t *jport = jack_port_by_name(sij->client, client_and_port_name);
int flags = jack_port_flags(port); 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>(); SoundIoDevice *device = create<SoundIoDevice>();
if (!device) { if (!device) {
@ -152,15 +260,29 @@ 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;
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->ref_count = 1;
device->soundio = soundio; device->soundio = soundio;
device->is_raw = false; device->is_raw = false;
device->name = strdup(port_name); device->purpose = client->purpose;
device->description = strdup(port_name); device->name = dupe_str(client->name, client->name_len);
device->description = allocate<char>(description_len);
device->layout_count = 1; device->layout_count = 1;
device->layouts = create<SoundIoChannelLayout>(); device->layouts = create<SoundIoChannelLayout>();
device->format_count = 1; device->format_count = 1;
device->formats = create<SoundIoFormat>(); 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) { if (!device->name || !device->description || !device->layouts || !device->formats) {
jack_free(port_names); jack_free(port_names);
@ -169,48 +291,52 @@ static int refresh_devices(SoundIoPrivate *si) {
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
// TODO figure out how jack does channel layout memcpy(device->description, client->name, client->name_len);
device->layouts[0] = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono); memcpy(&device->description[client->name_len], ": ", 2);
device->current_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono); 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; const struct SoundIoChannelLayout *layout = soundio_channel_layout_get_default(client->ports.length);
device->current_format = SoundIoFormatFloat32NE; if (layout) {
device->current_layout = *layout;
device->sample_rate_min = sij->sample_rate; } else {
device->sample_rate_max = sij->sample_rate; for (int port_index = 0; port_index < client->ports.length; port_index += 1)
device->sample_rate_current = sij->sample_rate; device->current_layout.channels[port_index] = SoundIoChannelIdInvalid;
}
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;
device->layouts[0] = device->current_layout;
device->formats[0] = device->current_format;
SoundIoList<SoundIoDevice *> *device_list; SoundIoList<SoundIoDevice *> *device_list;
if (flags & JackPortIsInput) { if (device->purpose == SoundIoDevicePurposeOutput) {
device->purpose = SoundIoDevicePurposeOutput;
device_list = &devices_info->output_devices; 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; devices_info->default_output_index = device_list->length;
} else { } else {
assert(flags & JackPortIsOutput); assert(device->purpose == SoundIoDevicePurposeInput);
device->purpose = SoundIoDevicePurposeInput;
device_list = &devices_info->input_devices; 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; devices_info->default_input_index = device_list->length;
} }
if (device_list->append(device)) { if (device_list->append(device)) {
jack_free(port_names);
soundio_device_unref(device); soundio_device_unref(device);
destroy(devices_info); destroy(devices_info);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
port_name_ptr += 1;
} }
jack_free(port_names); jack_free(port_names);
soundio_os_mutex_lock(sij->mutex); soundio_os_mutex_lock(sij->mutex);
soundio_destroy_devices_info(sij->ready_devices_info); soundio_destroy_devices_info(sij->ready_devices_info);
sij->ready_devices_info = 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) { static void port_registration_callback(jack_port_id_t port_id, int reg, void *arg) {
//SoundIoPrivate *si = (SoundIoPrivate *)arg; SoundIoPrivate *si = (SoundIoPrivate *)arg;
soundio_panic("TODO port registration callback"); SoundIoJack *sij = (SoundIoJack *)si->backend_data;
if (sij->initialized)
refresh_devices(si);
} }
static int port_rename_calllback(jack_port_id_t port_id, 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;
soundio_panic("TODO port rename callback"); SoundIoJack *sij = (SoundIoJack *)si->backend_data;
if (sij->initialized)
refresh_devices(si);
return 0; return 0;
} }
@ -277,6 +407,14 @@ static void destroy_jack(SoundIoPrivate *si) {
if (sij->client) if (sij->client)
jack_client_close(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); destroy(sij);
si->backend_data = nullptr; si->backend_data = nullptr;
} }
@ -312,6 +450,8 @@ int soundio_jack_init(struct SoundIoPrivate *si) {
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
} }
// We pass JackNoStartServer due to
// https://github.com/jackaudio/jack2/issues/138
jack_status_t status; jack_status_t status;
sij->client = jack_client_open(soundio->app_name, JackNoStartServer, &status); sij->client = jack_client_open(soundio->app_name, JackNoStartServer, &status);
if (!sij->client) { if (!sij->client) {

View file

@ -489,6 +489,9 @@ const char *soundio_get_channel_name(enum SoundIoChannelId id);
int soundio_channel_layout_builtin_count(void); int soundio_channel_layout_builtin_count(void);
const struct SoundIoChannelLayout *soundio_channel_layout_get_builtin(int index); 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( int soundio_channel_layout_find_channel(
const struct SoundIoChannelLayout *layout, enum SoundIoChannelId channel); const struct SoundIoChannelLayout *layout, enum SoundIoChannelId channel);