mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-10 13:30:05 -05:00
Improve loop callbacks
Pass just one data item to the callbacks. Add properties to port. Add user data to link Handle autolink with multiple ports More work on jack support
This commit is contained in:
parent
cfd9967637
commit
9ad1f911b2
38 changed files with 1773 additions and 271 deletions
|
|
@ -30,6 +30,7 @@
|
|||
#define JACK_PORT_NAME_SIZE 256
|
||||
#define JACK_PORT_TYPE_SIZE 32
|
||||
#define JACK_PROTOCOL_VERSION 8
|
||||
#define JACK_MESSAGE_SIZE 256
|
||||
|
||||
#define PORT_NUM_MAX 4096
|
||||
#define PORT_NUM_FOR_CLIENT 2048
|
||||
|
|
@ -60,8 +61,6 @@
|
|||
#define EMPTY 0xFFFD
|
||||
#define FREE 0xFFFC
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
JACK_TIMER_SYSTEM_CLOCK,
|
||||
JACK_TIMER_HPET,
|
||||
|
|
|
|||
508
src/modules/module-jack/jack-node.c
Normal file
508
src/modules/module-jack/jack-node.c
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include <spa/node.h>
|
||||
#include <spa/hook.h>
|
||||
#include <spa/format-builder.h>
|
||||
#include <spa/lib/format.h>
|
||||
#include <spa/audio/format-utils.h>
|
||||
|
||||
#include "pipewire/pipewire.h"
|
||||
#include "pipewire/core.h"
|
||||
#include "pipewire/private.h"
|
||||
|
||||
#include "jack.h"
|
||||
#include "jack-node.h"
|
||||
|
||||
#define NAME "jack-node"
|
||||
|
||||
/** \cond */
|
||||
|
||||
struct type {
|
||||
uint32_t format;
|
||||
struct spa_type_data data;
|
||||
struct spa_type_media_type media_type;
|
||||
struct spa_type_media_subtype media_subtype;
|
||||
struct spa_type_format_audio format_audio;
|
||||
struct spa_type_audio_format audio_format;
|
||||
};
|
||||
|
||||
struct pw_jack_node {
|
||||
struct pw_core *core;
|
||||
struct pw_node *node;
|
||||
|
||||
struct spa_hook_list listener_list;
|
||||
|
||||
struct jack_server *server;
|
||||
struct type type;
|
||||
int ref_num;
|
||||
struct pw_port *otherport;
|
||||
};
|
||||
|
||||
static inline void init_type(struct type *type, struct spa_type_map *map)
|
||||
{
|
||||
type->format = spa_type_map_get_id(map, SPA_TYPE__Format);
|
||||
spa_type_data_map(map, &type->data);
|
||||
spa_type_media_type_map(map, &type->media_type);
|
||||
spa_type_media_subtype_map(map, &type->media_subtype);
|
||||
spa_type_format_audio_map(map, &type->format_audio);
|
||||
spa_type_audio_format_map(map, &type->audio_format);
|
||||
}
|
||||
|
||||
struct buffer {
|
||||
struct spa_list link;
|
||||
struct spa_buffer *outbuf;
|
||||
void *ptr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct port_data {
|
||||
struct pw_jack_node *node;
|
||||
|
||||
int jack_port_id;
|
||||
struct jack_port *port;
|
||||
|
||||
struct spa_port_info info;
|
||||
|
||||
struct spa_port_io *io;
|
||||
|
||||
struct buffer buffers[64];
|
||||
uint32_t n_buffers;
|
||||
struct spa_list empty;
|
||||
|
||||
uint8_t buffer[1024];
|
||||
};
|
||||
|
||||
/** \endcond */
|
||||
|
||||
static int node_get_props(void *data, struct spa_props **props)
|
||||
{
|
||||
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static int node_set_props(void *data, const struct spa_props *props)
|
||||
{
|
||||
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static int node_send_command(void *data,
|
||||
const struct spa_command *command)
|
||||
{
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static struct pw_port* node_add_port(void *data,
|
||||
enum pw_direction direction,
|
||||
uint32_t port_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct buffer *buffer_dequeue(struct pw_jack_node *this, struct port_data *pd)
|
||||
{
|
||||
struct buffer *b;
|
||||
|
||||
if (spa_list_is_empty(&pd->empty))
|
||||
return NULL;
|
||||
|
||||
b = spa_list_first(&pd->empty, struct buffer, link);
|
||||
spa_list_remove(&b->link);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static void recycle_buffer(struct pw_jack_node *this, struct port_data *pd, uint32_t id)
|
||||
{
|
||||
struct buffer *b = &pd->buffers[id];
|
||||
pw_log_trace("recycle buffer %d", id);
|
||||
spa_list_append(&pd->empty, &b->link);
|
||||
}
|
||||
|
||||
static int node_process_input(void *data)
|
||||
{
|
||||
struct pw_jack_node *this = data;
|
||||
struct spa_graph_node *node = &this->node->rt.node;
|
||||
struct spa_graph_port *p;
|
||||
struct buffer *out;
|
||||
struct port_data *opd = pw_port_get_user_data(this->otherport);
|
||||
struct spa_port_io *out_io = opd->io;
|
||||
|
||||
pw_log_trace("process input");
|
||||
if (out_io->status == SPA_RESULT_HAVE_BUFFER)
|
||||
return SPA_RESULT_HAVE_BUFFER;
|
||||
|
||||
out = buffer_dequeue(this, opd);
|
||||
if (out == NULL)
|
||||
return SPA_RESULT_OUT_OF_BUFFERS;
|
||||
|
||||
spa_list_for_each(p, &node->ports[SPA_DIRECTION_INPUT], link) {
|
||||
struct pw_port *port = p->callbacks_data;
|
||||
struct port_data *ipd = pw_port_get_user_data(port);
|
||||
struct spa_port_io *in_io = ipd->io;
|
||||
|
||||
in_io->status = SPA_RESULT_NEED_BUFFER;
|
||||
}
|
||||
|
||||
out_io->buffer_id = out->outbuf->id;
|
||||
out_io->status = SPA_RESULT_HAVE_BUFFER;
|
||||
|
||||
return SPA_RESULT_HAVE_BUFFER;
|
||||
}
|
||||
|
||||
static int node_process_output(void *data)
|
||||
{
|
||||
struct pw_jack_node *this = data;
|
||||
struct spa_graph_node *node = &this->node->rt.node;
|
||||
struct spa_graph_port *p;
|
||||
|
||||
pw_log_trace(NAME "%p: process output", this);
|
||||
|
||||
spa_list_for_each(p, &node->ports[SPA_DIRECTION_OUTPUT], link) {
|
||||
struct pw_port *port = p->callbacks_data;
|
||||
struct port_data *opd = pw_port_get_user_data(port);
|
||||
struct spa_port_io *out_io = opd->io;
|
||||
|
||||
if (out_io->status == SPA_RESULT_HAVE_BUFFER)
|
||||
return SPA_RESULT_HAVE_BUFFER;
|
||||
|
||||
if (out_io->buffer_id != SPA_ID_INVALID) {
|
||||
recycle_buffer(this, opd, out_io->buffer_id);
|
||||
out_io->buffer_id = SPA_ID_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
spa_list_for_each(p, &node->ports[SPA_DIRECTION_INPUT], link) {
|
||||
struct pw_port *port = p->callbacks_data;
|
||||
struct port_data *ipd = pw_port_get_user_data(port);
|
||||
struct spa_port_io *in_io = ipd->io;
|
||||
|
||||
in_io->status = SPA_RESULT_NEED_BUFFER;
|
||||
}
|
||||
|
||||
spa_hook_list_call(&this->listener_list, struct pw_jack_node_events, process);
|
||||
|
||||
return SPA_RESULT_NEED_BUFFER;
|
||||
}
|
||||
|
||||
static const struct pw_node_implementation node_impl = {
|
||||
PW_VERSION_NODE_IMPLEMENTATION,
|
||||
.get_props = node_get_props,
|
||||
.set_props = node_set_props,
|
||||
.send_command = node_send_command,
|
||||
.add_port = node_add_port,
|
||||
.process_input = node_process_input,
|
||||
.process_output = node_process_output,
|
||||
};
|
||||
|
||||
static int port_set_io(void *data, struct spa_port_io *io)
|
||||
{
|
||||
struct port_data *pd = data;
|
||||
pd->io = io;
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
#define PROP(f,key,type,...) \
|
||||
SPA_POD_PROP (f,key,0,type,1,__VA_ARGS__)
|
||||
|
||||
static int port_enum_formats(void *data,
|
||||
struct spa_format **format,
|
||||
const struct spa_format *filter,
|
||||
int32_t index)
|
||||
{
|
||||
struct port_data *pd = data;
|
||||
struct type *t = &pd->node->type;
|
||||
struct spa_pod_builder b = { NULL, };
|
||||
struct spa_pod_frame f[2];
|
||||
|
||||
if (index > 0)
|
||||
return SPA_RESULT_ENUM_END;
|
||||
|
||||
spa_pod_builder_init(&b, pd->buffer, sizeof(pd->buffer));
|
||||
|
||||
if (pd->port) {
|
||||
if (pd->port->type_id == 0) {
|
||||
spa_pod_builder_format(&b, &f[0], t->format,
|
||||
t->media_type.audio, t->media_subtype.raw,
|
||||
PROP(&f[1], t->format_audio.format, SPA_POD_TYPE_ID, t->audio_format.F32),
|
||||
PROP(&f[1], t->format_audio.rate, SPA_POD_TYPE_INT, 44100),
|
||||
PROP(&f[1], t->format_audio.channels, SPA_POD_TYPE_INT, 1));
|
||||
}
|
||||
else if (pd->port->type_id == 1) {
|
||||
return SPA_RESULT_ENUM_END;
|
||||
}
|
||||
else
|
||||
return SPA_RESULT_ENUM_END;
|
||||
}
|
||||
else {
|
||||
spa_pod_builder_format(&b, &f[0], t->format,
|
||||
t->media_type.audio, t->media_subtype.raw,
|
||||
PROP(&f[1], t->format_audio.format, SPA_POD_TYPE_ID, t->audio_format.S16),
|
||||
PROP(&f[1], t->format_audio.rate, SPA_POD_TYPE_INT, 44100),
|
||||
PROP(&f[1], t->format_audio.channels, SPA_POD_TYPE_INT, 2));
|
||||
}
|
||||
*format = SPA_POD_BUILDER_DEREF(&b, f[0].ref, struct spa_format);
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static int port_set_format(void *data, uint32_t flags, const struct spa_format *format)
|
||||
{
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static int port_get_format(void *data, const struct spa_format **format)
|
||||
{
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static int port_get_info(void *data, const struct spa_port_info **info)
|
||||
{
|
||||
struct port_data *pd = data;
|
||||
|
||||
pd->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | SPA_PORT_INFO_FLAG_LIVE;
|
||||
pd->info.rate = 44100;
|
||||
*info = &pd->info;
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static int port_enum_params(void *data, uint32_t index, struct spa_param **param)
|
||||
{
|
||||
return SPA_RESULT_ENUM_END;
|
||||
}
|
||||
|
||||
static int port_set_param(void *data, struct spa_param *param)
|
||||
{
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static int port_use_buffers(void *data, struct spa_buffer **buffers, uint32_t n_buffers)
|
||||
{
|
||||
struct port_data *pd = data;
|
||||
struct type *t = &pd->node->type;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_buffers; i++) {
|
||||
struct buffer *b;
|
||||
struct spa_data *d = buffers[i]->datas;
|
||||
|
||||
b = &pd->buffers[i];
|
||||
b->outbuf = buffers[i];
|
||||
if ((d[0].type == t->data.MemPtr ||
|
||||
d[0].type == t->data.MemFd ||
|
||||
d[0].type == t->data.DmaBuf) && d[0].data != NULL) {
|
||||
b->ptr = d[0].data;
|
||||
b->size = d[0].maxsize;
|
||||
} else {
|
||||
pw_log_error(NAME " %p: invalid memory on buffer %p", pd, buffers[i]);
|
||||
return SPA_RESULT_ERROR;
|
||||
}
|
||||
spa_list_append(&pd->empty, &b->link);
|
||||
}
|
||||
pd->n_buffers = n_buffers;
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static int port_alloc_buffers(void *data,
|
||||
struct spa_param **params, uint32_t n_params,
|
||||
struct spa_buffer **buffers, uint32_t *n_buffers)
|
||||
{
|
||||
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static int port_reuse_buffer(void *data, uint32_t buffer_id)
|
||||
{
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static int port_send_command(void *data, struct spa_command *command)
|
||||
{
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static const struct pw_port_implementation port_impl = {
|
||||
PW_VERSION_PORT_IMPLEMENTATION,
|
||||
.set_io = port_set_io,
|
||||
.enum_formats = port_enum_formats,
|
||||
.set_format = port_set_format,
|
||||
.get_format = port_get_format,
|
||||
.get_info = port_get_info,
|
||||
.enum_params = port_enum_params,
|
||||
.set_param = port_set_param,
|
||||
.use_buffers = port_use_buffers,
|
||||
.alloc_buffers = port_alloc_buffers,
|
||||
.reuse_buffer = port_reuse_buffer,
|
||||
.send_command = port_send_command,
|
||||
};
|
||||
|
||||
static struct pw_port *make_port(struct pw_jack_node *node, enum pw_direction direction,
|
||||
int port_id, int jack_port_id, struct jack_port *jp, bool autoconnect)
|
||||
{
|
||||
struct pw_port *port;
|
||||
struct port_data *pd;
|
||||
struct pw_properties *properties = NULL;
|
||||
|
||||
if (autoconnect)
|
||||
properties = pw_properties_new("pipewire.autoconnect", "1", NULL);
|
||||
|
||||
port = pw_port_new(direction, port_id, properties, sizeof(struct port_data));
|
||||
pd = pw_port_get_user_data(port);
|
||||
pd->node = node;
|
||||
pd->jack_port_id = jack_port_id;
|
||||
pd->port = jp;
|
||||
spa_list_init(&pd->empty);
|
||||
pw_port_set_implementation(port, &port_impl, pd);
|
||||
pw_port_add(port, node->node);
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
struct pw_jack_node *pw_jack_node_new(struct pw_core *core,
|
||||
struct pw_global *parent,
|
||||
struct jack_server *server,
|
||||
int ref_num,
|
||||
struct pw_properties *properties)
|
||||
{
|
||||
struct pw_jack_node *this;
|
||||
struct pw_node *node;
|
||||
struct jack_client *client = server->client_table[ref_num];
|
||||
struct jack_graph_manager *mgr = server->graph_manager;
|
||||
struct jack_connection_manager *conn;
|
||||
int i;
|
||||
jack_int_t *p;
|
||||
bool make_input = false, make_output = false;
|
||||
|
||||
node = pw_node_new(core, NULL, parent, client->control->name,
|
||||
properties, sizeof(struct pw_jack_node));
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
|
||||
this = pw_node_get_user_data(node);
|
||||
pw_log_debug("jack-node %p: new", this);
|
||||
|
||||
this->node = node;
|
||||
this->core = core;
|
||||
spa_hook_list_init(&this->listener_list);
|
||||
this->server = server;
|
||||
this->ref_num = ref_num;
|
||||
init_type(&this->type, pw_core_get_type(core)->map);
|
||||
|
||||
pw_node_set_implementation(node, &node_impl, this);
|
||||
|
||||
conn = jack_graph_manager_next_start(mgr);
|
||||
|
||||
p = GET_ITEMS_FIXED_ARRAY1(conn->input_port[ref_num]);
|
||||
for (i = 0; i < PORT_NUM_FOR_CLIENT && p[i] != EMPTY; i++) {
|
||||
struct jack_port *jp = jack_graph_manager_get_port(mgr, p[i]);
|
||||
|
||||
if (jp->flags & JackPortIsPhysical)
|
||||
make_output = true;
|
||||
|
||||
make_port(this, PW_DIRECTION_INPUT, i, p[i], jp, false);
|
||||
}
|
||||
|
||||
p = GET_ITEMS_FIXED_ARRAY(conn->output_port[ref_num]);
|
||||
for (i = 0; i < PORT_NUM_FOR_CLIENT && p[i] != EMPTY; i++) {
|
||||
struct jack_port *jp = jack_graph_manager_get_port(mgr, p[i]);
|
||||
|
||||
if (jp->flags & JackPortIsPhysical)
|
||||
make_input = true;
|
||||
|
||||
make_port(this, PW_DIRECTION_OUTPUT, i, p[i], jp, false);
|
||||
}
|
||||
jack_graph_manager_next_stop(mgr);
|
||||
|
||||
if (make_output)
|
||||
this->otherport = make_port(this, PW_DIRECTION_OUTPUT, 0, -1, NULL, true);
|
||||
if (make_input)
|
||||
this->otherport = make_port(this, PW_DIRECTION_INPUT, 0, -1, NULL, true);
|
||||
|
||||
pw_node_register(node);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void pw_jack_node_destroy(struct pw_jack_node *node)
|
||||
{
|
||||
pw_log_debug("jack-node %p: destroy", node);
|
||||
pw_node_destroy(node->node);
|
||||
free(node);
|
||||
}
|
||||
|
||||
struct pw_node *pw_jack_node_get_node(struct pw_jack_node *node)
|
||||
{
|
||||
return node->node;
|
||||
}
|
||||
|
||||
void pw_jack_node_add_listener(struct pw_jack_node *node,
|
||||
struct spa_hook *listener,
|
||||
const struct pw_jack_node_events *events,
|
||||
void *data)
|
||||
{
|
||||
spa_hook_list_append(&node->listener_list, listener, events, data);
|
||||
}
|
||||
|
||||
struct find_data {
|
||||
jack_port_id_t port_id;
|
||||
struct pw_port *result;
|
||||
};
|
||||
|
||||
static bool find_port(void *data, struct pw_port *port)
|
||||
{
|
||||
struct find_data *d = data;
|
||||
struct port_data *pd = pw_port_get_user_data(port);
|
||||
|
||||
if (pd->jack_port_id == d->port_id) {
|
||||
d->result = port;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct pw_port *pw_jack_node_add_port(struct pw_jack_node *node,
|
||||
enum pw_direction direction,
|
||||
jack_port_id_t port_id)
|
||||
{
|
||||
struct jack_server *server = node->server;
|
||||
struct jack_graph_manager *mgr = server->graph_manager;
|
||||
struct jack_port *jp = jack_graph_manager_get_port(mgr, port_id);
|
||||
return make_port(node, direction, port_id, port_id, jp, false);
|
||||
}
|
||||
|
||||
struct pw_port *pw_jack_node_find_port(struct pw_jack_node *node,
|
||||
enum pw_direction direction,
|
||||
jack_port_id_t port_id)
|
||||
{
|
||||
struct find_data data = { port_id, };
|
||||
if (!pw_node_for_each_port(node->node, direction, find_port, &data))
|
||||
return data.result;
|
||||
return NULL;
|
||||
}
|
||||
73
src/modules/module-jack/jack-node.h
Normal file
73
src/modules/module-jack/jack-node.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PIPEWIRE_JACK_NODE_H__
|
||||
#define __PIPEWIRE_JACK_NODE_H__
|
||||
|
||||
#include <pipewire/node.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \class pw_jack_node
|
||||
*
|
||||
* PipeWire jack node interface
|
||||
*/
|
||||
struct pw_jack_node;
|
||||
|
||||
struct pw_jack_node_events {
|
||||
#define PW_VERSION_JACK_NODE_EVENTS 0
|
||||
uint32_t version;
|
||||
|
||||
void (*destroy) (void *data);
|
||||
|
||||
void (*process) (void *data);
|
||||
};
|
||||
|
||||
|
||||
struct pw_jack_node *
|
||||
pw_jack_node_new(struct pw_core *core,
|
||||
struct pw_global *parent,
|
||||
struct jack_server *server,
|
||||
int ref_num,
|
||||
struct pw_properties *properties);
|
||||
|
||||
void
|
||||
pw_jack_node_destroy(struct pw_jack_node *node);
|
||||
|
||||
struct pw_node *pw_jack_node_get_node(struct pw_jack_node *node);
|
||||
|
||||
void pw_jack_node_add_listener(struct pw_jack_node *node,
|
||||
struct spa_hook *listener,
|
||||
const struct pw_jack_node_events *events,
|
||||
void *data);
|
||||
|
||||
struct pw_port *pw_jack_node_add_port(struct pw_jack_node *node,
|
||||
enum pw_direction direction,
|
||||
jack_port_id_t port_id);
|
||||
|
||||
struct pw_port *pw_jack_node_find_port(struct pw_jack_node *node,
|
||||
enum pw_direction direction, jack_port_id_t port_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PIPEWIRE_JACK_NODE_H__ */
|
||||
28
src/modules/module-jack/jack.h
Normal file
28
src/modules/module-jack/jack.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/session.h>
|
||||
|
||||
#include "modules/module-jack/defs.h"
|
||||
#include "modules/module-jack/synchro.h"
|
||||
#include "modules/module-jack/shm.h"
|
||||
#include "modules/module-jack/shared.h"
|
||||
#include "modules/module-jack/port.h"
|
||||
#include "modules/module-jack/server.h"
|
||||
28
src/modules/module-jack/port.h
Normal file
28
src/modules/module-jack/port.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2017 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
static inline jack_port_type_id_t
|
||||
jack_port_get_type_id(const char *type)
|
||||
{
|
||||
if (strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0)
|
||||
return 0;
|
||||
else if (strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0)
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
|
@ -21,7 +21,8 @@ struct jack_client {
|
|||
int ref_num;
|
||||
struct client *owner;
|
||||
struct jack_client_control *control;
|
||||
struct pw_node *node;
|
||||
struct pw_jack_node *node;
|
||||
int fd; /* notify fd */
|
||||
};
|
||||
|
||||
struct jack_server {
|
||||
|
|
@ -34,6 +35,13 @@ struct jack_server {
|
|||
|
||||
struct jack_client* client_table[CLIENT_NUM];
|
||||
struct jack_synchro synchro_table[CLIENT_NUM];
|
||||
|
||||
int audio_ref_num;
|
||||
int freewheel_ref_num;
|
||||
|
||||
struct pw_jack_node *audio_node;
|
||||
struct pw_node *audio_node_node;
|
||||
int audio_used;
|
||||
};
|
||||
|
||||
static inline int
|
||||
|
|
|
|||
|
|
@ -98,6 +98,64 @@ static inline void jack_port_init(struct jack_port *port, int ref_num,
|
|||
port->tied = NO_PORT;
|
||||
}
|
||||
|
||||
static inline void jack_port_release(struct jack_port *port) {
|
||||
port->in_use = false;
|
||||
}
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_client_control {
|
||||
jack_shm_info_t info;
|
||||
char name[JACK_CLIENT_NAME_SIZE+1];
|
||||
bool callback[jack_notify_max];
|
||||
volatile jack_transport_state_t transport_state;
|
||||
volatile bool transport_sync;
|
||||
volatile bool transport_timebase;
|
||||
int ref_num;
|
||||
int PID;
|
||||
bool active;
|
||||
|
||||
int session_ID;
|
||||
char session_command[JACK_SESSION_COMMAND_SIZE];
|
||||
jack_session_flags_t session_flags;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
static inline struct jack_client_control *
|
||||
jack_client_control_alloc(const char* name, int pid, int ref_num, int uuid)
|
||||
{
|
||||
struct jack_client_control *ctrl;
|
||||
jack_shm_info_t info;
|
||||
size_t size;
|
||||
|
||||
size = sizeof(struct jack_client_control);
|
||||
if (jack_shm_alloc(size, &info, segment_num++) < 0)
|
||||
return NULL;
|
||||
|
||||
ctrl = (struct jack_client_control *)jack_shm_addr(&info);
|
||||
ctrl->info = info;
|
||||
|
||||
strcpy(ctrl->name, name);
|
||||
for (int i = 0; i < jack_notify_max; i++)
|
||||
ctrl->callback[i] = false;
|
||||
|
||||
// Always activated
|
||||
ctrl->callback[jack_notify_AddClient] = true;
|
||||
ctrl->callback[jack_notify_RemoveClient] = true;
|
||||
ctrl->callback[jack_notify_ActivateClient] = true;
|
||||
ctrl->callback[jack_notify_LatencyCallback] = true;
|
||||
// So that driver synchro are correctly setup in "flush" or "normal" mode
|
||||
ctrl->callback[jack_notify_StartFreewheelCallback] = true;
|
||||
ctrl->callback[jack_notify_StopFreewheelCallback] = true;
|
||||
ctrl->ref_num = ref_num;
|
||||
ctrl->PID = pid;
|
||||
ctrl->transport_state = JackTransportStopped;
|
||||
ctrl->transport_sync = false;
|
||||
ctrl->transport_timebase = false;
|
||||
ctrl->active = false;
|
||||
ctrl->session_ID = uuid;
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
#define MAKE_FIXED_ARRAY(size) \
|
||||
PRE_PACKED_STRUCTURE \
|
||||
struct { \
|
||||
|
|
@ -105,24 +163,41 @@ struct { \
|
|||
uint32_t counter; \
|
||||
} POST_PACKED_STRUCTURE
|
||||
|
||||
#define INIT_FIXED_ARRAY(arr) ({ \
|
||||
int i; \
|
||||
for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) \
|
||||
arr.table[i] = EMPTY; \
|
||||
arr.counter = 0; \
|
||||
#define INIT_FIXED_ARRAY(arr) ({ \
|
||||
int _i; \
|
||||
for (_i = 0; _i < SPA_N_ELEMENTS(arr.table); _i++) \
|
||||
arr.table[_i] = EMPTY; \
|
||||
arr.counter = 0; \
|
||||
})
|
||||
#define GET_ITEMS_FIXED_ARRAY(arr) ({ \
|
||||
arr.table; \
|
||||
})
|
||||
|
||||
#define ADD_FIXED_ARRAY(arr,item) ({ \
|
||||
int i,ret = -1; \
|
||||
for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) { \
|
||||
if (arr.table[i] == EMPTY) { \
|
||||
arr.table[i] = item; \
|
||||
arr.counter++; \
|
||||
ret = 0; \
|
||||
int _ret = -1; \
|
||||
if (arr.counter < SPA_N_ELEMENTS(arr.table)) { \
|
||||
_ret = arr.counter++; \
|
||||
arr.table[_ret] = item; \
|
||||
} \
|
||||
_ret; \
|
||||
})
|
||||
#define GET_FIXED_ARRAY(arr,item) ({ \
|
||||
int _i,_ret = -1; \
|
||||
for (_i = 0; _i < arr.counter; _i++) { \
|
||||
if (arr.table[_i] == item) { \
|
||||
_ret = _i; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
ret; \
|
||||
_ret; \
|
||||
})
|
||||
#define REMOVE_FIXED_ARRAY(arr,item) ({ \
|
||||
int _ret = GET_FIXED_ARRAY(arr,item); \
|
||||
if (_ret >= 0) { \
|
||||
arr.counter--; \
|
||||
arr.table[_ret] = arr.table[arr.counter]; \
|
||||
arr.table[arr.counter] = EMPTY; \
|
||||
} \
|
||||
_ret; \
|
||||
})
|
||||
|
||||
#define MAKE_FIXED_ARRAY1(size) \
|
||||
|
|
@ -136,8 +211,9 @@ struct { \
|
|||
INIT_FIXED_ARRAY(arr.array); \
|
||||
arr.used = false; \
|
||||
})
|
||||
|
||||
#define ADD_FIXED_ARRAY1(arr,item) ADD_FIXED_ARRAY(arr.array,item)
|
||||
#define GET_FIXED_ARRAY1(arr,item) GET_FIXED_ARRAY(arr.array,item)
|
||||
#define GET_ITEMS_FIXED_ARRAY1(arr) GET_ITEMS_FIXED_ARRAY(arr.array)
|
||||
|
||||
#define MAKE_FIXED_MATRIX(size) \
|
||||
PRE_PACKED_STRUCTURE \
|
||||
|
|
@ -152,6 +228,21 @@ struct { \
|
|||
mat.table[i][idx] = 0; \
|
||||
} \
|
||||
})
|
||||
#define GET_ITEMS_FIXED_MATRIX(mat,idx1) ({ \
|
||||
mat.table[idx1]; \
|
||||
})
|
||||
#define INC_FIXED_MATRIX(mat,idx1,idx2) ({ \
|
||||
++mat.table[idx1][idx2]; \
|
||||
})
|
||||
#define DEC_FIXED_MATRIX(mat,idx1,idx2) ({ \
|
||||
--mat.table[idx1][idx2]; \
|
||||
})
|
||||
#define GET_FIXED_MATRIX(mat,idx1,idx2) ({ \
|
||||
mat.table[idx1][idx2]; \
|
||||
})
|
||||
#define CLEAR_FIXED_MATRIX(mat,idx1,idx2) ({ \
|
||||
mat.table[idx1][idx2] = 0; \
|
||||
})
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_activation_count {
|
||||
|
|
@ -162,6 +253,33 @@ struct jack_activation_count {
|
|||
static inline void jack_activation_count_set_value(struct jack_activation_count *cnt, int32_t val) {
|
||||
cnt->value = val;
|
||||
}
|
||||
static inline int32_t jack_activation_count_get_value(struct jack_activation_count *cnt) {
|
||||
return cnt->value;
|
||||
}
|
||||
static inline int32_t jack_activation_count_get_count(struct jack_activation_count *cnt) {
|
||||
return cnt->count;
|
||||
}
|
||||
static inline void jack_activation_count_reset(struct jack_activation_count *cnt) {
|
||||
cnt->value = cnt->count;
|
||||
}
|
||||
static inline void jack_activation_count_inc_value(struct jack_activation_count *cnt) {
|
||||
cnt->count++;
|
||||
}
|
||||
static inline void jack_activation_count_dec_value(struct jack_activation_count *cnt) {
|
||||
cnt->count--;
|
||||
}
|
||||
static inline bool jack_activation_count_signal(struct jack_activation_count *cnt,
|
||||
struct jack_synchro *synchro)
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
if (cnt->value == 0)
|
||||
res = jack_synchro_signal(synchro);
|
||||
else if (__atomic_sub_fetch(&cnt->value, 1, __ATOMIC_SEQ_CST) == 0)
|
||||
res = jack_synchro_signal(synchro);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define MAKE_LOOP_FEEDBACK(size) \
|
||||
PRE_PACKED_STRUCTURE \
|
||||
|
|
@ -177,6 +295,62 @@ struct { \
|
|||
arr.table[i][2] = 0; \
|
||||
} \
|
||||
})
|
||||
#define ADD_LOOP_FEEDBACK(arr,ref1,ref2) ({ \
|
||||
int i,res = false; \
|
||||
for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) { \
|
||||
if (arr.table[i][0] == EMPTY) { \
|
||||
arr.table[i][0] = ref1; \
|
||||
arr.table[i][1] = ref2; \
|
||||
arr.table[i][2] = 1; \
|
||||
res = true; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
res; \
|
||||
})
|
||||
#define DEL_LOOP_FEEDBACK(arr,ref1,ref2) ({ \
|
||||
int i,res = false; \
|
||||
for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) { \
|
||||
if (arr.table[i][0] == ref1 && \
|
||||
arr.table[i][1] == ref2) { \
|
||||
arr.table[i][0] = EMPTY; \
|
||||
arr.table[i][1] = EMPTY; \
|
||||
arr.table[i][2] = 0; \
|
||||
res = true; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
res; \
|
||||
})
|
||||
#define GET_LOOP_FEEDBACK(arr,ref1,ref2) ({ \
|
||||
int i,res = 1; \
|
||||
for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) { \
|
||||
if (arr.table[i][0] == ref1 && \
|
||||
arr.table[i][1] == ref2) { \
|
||||
res = i; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
res; \
|
||||
})
|
||||
#define INC_LOOP_FEEDBACK(arr,ref1,ref2) ({ \
|
||||
int res = true, idx = GET_LOOP_FEEDBACK(arr,ref1,ref2); \
|
||||
if (idx >= 0) \
|
||||
arr.table[idx][2]++; \
|
||||
else \
|
||||
res = ADD_LOOP_FEEDBACK(arr,ref1,ref2); \
|
||||
res; \
|
||||
})
|
||||
#define DEC_LOOP_FEEDBACK(arr,idx) ({ \
|
||||
int res = true, idx = GET_LOOP_FEEDBACK(arr,ref1,ref2); \
|
||||
if (idx >= 0) { \
|
||||
if (--arr.table[idx][2] == 0) \
|
||||
res = DEL_LOOP_FEEDBACK(arr,ref1,ref2); \
|
||||
} \
|
||||
else \
|
||||
res = false; \
|
||||
res; \
|
||||
})
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_connection_manager {
|
||||
|
|
@ -194,7 +368,8 @@ jack_connection_manager_init_ref_num(struct jack_connection_manager *conn, int r
|
|||
INIT_FIXED_ARRAY1(conn->input_port[ref_num]);
|
||||
INIT_FIXED_ARRAY(conn->output_port[ref_num]);
|
||||
INIT_FIXED_MATRIX(conn->connection_ref, ref_num);
|
||||
jack_activation_count_set_value (&conn->input_counter[ref_num], 0);
|
||||
conn->input_counter[ref_num].count = 0;
|
||||
jack_activation_count_set_value(&conn->input_counter[ref_num], 0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
|
@ -210,14 +385,185 @@ jack_connection_manager_init(struct jack_connection_manager *conn)
|
|||
jack_connection_manager_init_ref_num(conn, i);
|
||||
}
|
||||
|
||||
static inline void
|
||||
jack_connection_manager_reset(struct jack_connection_manager *conn,
|
||||
struct jack_client_timing *timing)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < CLIENT_NUM; i++) {
|
||||
jack_activation_count_reset(&conn->input_counter[i]);
|
||||
timing[i].status = NotTriggered;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_add_port(struct jack_connection_manager *conn, bool input,
|
||||
int ref_num, jack_port_id_t port_id)
|
||||
{
|
||||
if (input)
|
||||
if (input) {
|
||||
return ADD_FIXED_ARRAY1(conn->input_port[ref_num], port_id);
|
||||
else
|
||||
}
|
||||
else {
|
||||
return ADD_FIXED_ARRAY(conn->output_port[ref_num], port_id);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_get_output_refnum(struct jack_connection_manager *conn,
|
||||
jack_port_id_t port_index)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < CLIENT_NUM; i++) {
|
||||
if (GET_FIXED_ARRAY(conn->output_port[i], port_index) != -1)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_get_input_refnum(struct jack_connection_manager *conn,
|
||||
jack_port_id_t port_index)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < CLIENT_NUM; i++) {
|
||||
if (GET_FIXED_ARRAY1(conn->input_port[i], port_index) != -1)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
jack_connection_manager_is_connected(struct jack_connection_manager *conn,
|
||||
jack_port_id_t src_id, jack_port_id_t dst_id)
|
||||
{
|
||||
return GET_FIXED_ARRAY(conn->connection[src_id], dst_id) != -1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_connect(struct jack_connection_manager *conn,
|
||||
jack_port_id_t src_id, jack_port_id_t dst_id)
|
||||
{
|
||||
return ADD_FIXED_ARRAY(conn->connection[src_id], dst_id);
|
||||
}
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_disconnect(struct jack_connection_manager *conn,
|
||||
jack_port_id_t src_id, jack_port_id_t dst_id)
|
||||
{
|
||||
return REMOVE_FIXED_ARRAY(conn->connection[src_id], dst_id);
|
||||
}
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_is_loop_path(struct jack_connection_manager *conn,
|
||||
jack_port_id_t src_id, jack_port_id_t dst_id)
|
||||
{
|
||||
/* FIXME */
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void
|
||||
jack_connection_manager_direct_connect(struct jack_connection_manager *conn,
|
||||
int ref1, int ref2)
|
||||
{
|
||||
if (INC_FIXED_MATRIX(conn->connection_ref, ref1, ref2) == 1)
|
||||
jack_activation_count_inc_value(&conn->input_counter[ref2]);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
jack_connection_manager_is_direct_connection(struct jack_connection_manager *conn,
|
||||
int ref1, int ref2)
|
||||
{
|
||||
return GET_FIXED_MATRIX(conn->connection_ref, ref1, ref2) > 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
jack_connection_manager_direct_disconnect(struct jack_connection_manager *conn,
|
||||
int ref1, int ref2)
|
||||
{
|
||||
if (DEC_FIXED_MATRIX(conn->connection_ref, ref1, ref2) == 0)
|
||||
jack_activation_count_dec_value(&conn->input_counter[ref2]);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
jack_connection_manager_inc_feedback_connection(struct jack_connection_manager *conn,
|
||||
jack_port_id_t src_id, jack_port_id_t dst_id)
|
||||
{
|
||||
int ref1 = jack_connection_manager_get_output_refnum(conn, src_id);
|
||||
int ref2 = jack_connection_manager_get_input_refnum(conn, dst_id);
|
||||
|
||||
if (ref1 != ref2)
|
||||
jack_connection_manager_direct_connect(conn, ref2, ref1);
|
||||
|
||||
return INC_LOOP_FEEDBACK(conn->loop_feedback, ref1, ref2);
|
||||
}
|
||||
|
||||
static inline void
|
||||
jack_connection_manager_inc_direct_connection(struct jack_connection_manager *conn,
|
||||
jack_port_id_t src_id, jack_port_id_t dst_id)
|
||||
{
|
||||
int ref1 = jack_connection_manager_get_output_refnum(conn, src_id);
|
||||
int ref2 = jack_connection_manager_get_input_refnum(conn, dst_id);
|
||||
|
||||
jack_connection_manager_direct_connect(conn, ref1, ref2);
|
||||
}
|
||||
|
||||
static inline void
|
||||
jack_connection_manager_dec_direct_connection(struct jack_connection_manager *conn,
|
||||
jack_port_id_t src_id, jack_port_id_t dst_id)
|
||||
{
|
||||
int ref1 = jack_connection_manager_get_output_refnum(conn, src_id);
|
||||
int ref2 = jack_connection_manager_get_input_refnum(conn, dst_id);
|
||||
|
||||
jack_connection_manager_direct_disconnect(conn, ref1, ref2);
|
||||
}
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_get_activation(struct jack_connection_manager *conn, int ref_num)
|
||||
{
|
||||
return jack_activation_count_get_value(&conn->input_counter[ref_num]);
|
||||
}
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_suspend_ref_num(struct jack_connection_manager *conn,
|
||||
struct jack_client_control *control,
|
||||
struct jack_synchro *synchro,
|
||||
struct jack_client_timing *timing)
|
||||
{
|
||||
int res = 0, ref_num = control->ref_num;
|
||||
jack_time_t current_date = 0;
|
||||
|
||||
if (jack_synchro_wait(&synchro[ref_num])) {
|
||||
timing[ref_num].status = Finished;
|
||||
timing[ref_num].awake_at = current_date;
|
||||
}
|
||||
return res ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_resume_ref_num(struct jack_connection_manager *conn,
|
||||
struct jack_client_control *control,
|
||||
struct jack_synchro *synchro,
|
||||
struct jack_client_timing *timing)
|
||||
{
|
||||
int i, res = 0, ref_num = control->ref_num;
|
||||
const jack_int_t* output_ref = GET_ITEMS_FIXED_MATRIX(conn->connection_ref, ref_num);
|
||||
jack_time_t current_date = 0;
|
||||
|
||||
timing[ref_num].status = Finished;
|
||||
timing[ref_num].finished_at = current_date;
|
||||
|
||||
for (i = 0; i < CLIENT_NUM; i++) {
|
||||
if (output_ref[i] <= 0)
|
||||
continue;
|
||||
|
||||
timing[i].status = Triggered;
|
||||
timing[i].signaled_at = current_date;
|
||||
|
||||
if (!jack_activation_count_signal(&conn->input_counter[i], &synchro[i]))
|
||||
res = -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
|
|
@ -286,6 +632,8 @@ jack_graph_manager_alloc(int port_max)
|
|||
|
||||
mgr = (struct jack_graph_manager *)jack_shm_addr(&info);
|
||||
mgr->info = info;
|
||||
Counter(mgr->state.counter) = 0;
|
||||
mgr->state.call_write_counter = 0;
|
||||
|
||||
jack_connection_manager_init(&mgr->state.state[0]);
|
||||
jack_connection_manager_init(&mgr->state.state[1]);
|
||||
|
|
@ -313,6 +661,31 @@ jack_graph_manager_allocate_port(struct jack_graph_manager *mgr,
|
|||
return NO_PORT;
|
||||
}
|
||||
|
||||
static inline void
|
||||
jack_graph_manager_release_port(struct jack_graph_manager *mgr, jack_port_id_t port_id)
|
||||
{
|
||||
jack_port_release(&mgr->port_array[port_id]);
|
||||
}
|
||||
|
||||
static inline struct jack_port *
|
||||
jack_graph_manager_get_port(struct jack_graph_manager *mgr, jack_port_id_t port_index)
|
||||
{
|
||||
if (port_index > 0 && port_index < mgr->port_max)
|
||||
return &mgr->port_array[port_index];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline jack_port_id_t
|
||||
jack_graph_manager_find_port(struct jack_graph_manager *mgr, const char *name)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < mgr->port_max; i++) {
|
||||
struct jack_port *port = &mgr->port_array[i];
|
||||
if (port->in_use && strcmp(port->name, name) == 0)
|
||||
return i;
|
||||
}
|
||||
return NO_PORT;
|
||||
}
|
||||
|
||||
static inline struct jack_connection_manager *
|
||||
jack_graph_manager_next_start(struct jack_graph_manager *manager)
|
||||
|
|
@ -370,6 +743,38 @@ jack_graph_manager_next_stop(struct jack_graph_manager *manager)
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
jack_graph_manager_is_pending_change(struct jack_graph_manager *manager)
|
||||
{
|
||||
return CurIndex(manager->state.counter) != NextIndex(manager->state.counter);
|
||||
}
|
||||
|
||||
static inline struct jack_connection_manager *
|
||||
jack_graph_manager_get_current(struct jack_graph_manager *manager)
|
||||
{
|
||||
return &manager->state.state[CurArrayIndex(manager->state.counter)];
|
||||
}
|
||||
|
||||
static inline struct jack_connection_manager *
|
||||
jack_graph_manager_try_switch(struct jack_graph_manager *manager)
|
||||
{
|
||||
struct jack_atomic_counter old_val;
|
||||
struct jack_atomic_counter new_val;
|
||||
do {
|
||||
old_val = manager->state.counter;
|
||||
new_val = old_val;
|
||||
CurIndex(new_val) = NextIndex(new_val);
|
||||
}
|
||||
while (!__atomic_compare_exchange_n((uint32_t*)&manager->state.counter,
|
||||
(uint32_t*)&Counter(old_val),
|
||||
Counter(new_val),
|
||||
false,
|
||||
__ATOMIC_SEQ_CST,
|
||||
__ATOMIC_SEQ_CST));
|
||||
|
||||
return &manager->state.state[CurArrayIndex(manager->state.counter)];
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
TransportCommandNone = 0,
|
||||
TransportCommandStart = 1,
|
||||
|
|
@ -515,7 +920,7 @@ jack_engine_control_alloc(const char* name)
|
|||
|
||||
ctrl->buffer_size = 512;
|
||||
ctrl->sample_rate = 48000;
|
||||
ctrl->sync_mode = false;
|
||||
ctrl->sync_mode = true;
|
||||
ctrl->temporary = false;
|
||||
ctrl->period_usecs = 1000000.f / ctrl->sample_rate * ctrl->buffer_size;
|
||||
ctrl->timeout_usecs = 0;
|
||||
|
|
@ -545,57 +950,3 @@ jack_engine_control_alloc(const char* name)
|
|||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_client_control {
|
||||
jack_shm_info_t info;
|
||||
char name[JACK_CLIENT_NAME_SIZE+1];
|
||||
bool callback[jack_notify_max];
|
||||
volatile jack_transport_state_t transport_state;
|
||||
volatile bool transport_sync;
|
||||
volatile bool transport_timebase;
|
||||
int ref_num;
|
||||
int PID;
|
||||
bool active;
|
||||
|
||||
int session_ID;
|
||||
char session_command[JACK_SESSION_COMMAND_SIZE];
|
||||
jack_session_flags_t session_flags;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
static inline struct jack_client_control *
|
||||
jack_client_control_alloc(const char* name, int pid, int ref_num, int uuid)
|
||||
{
|
||||
struct jack_client_control *ctrl;
|
||||
jack_shm_info_t info;
|
||||
size_t size;
|
||||
|
||||
size = sizeof(struct jack_client_control);
|
||||
if (jack_shm_alloc(size, &info, segment_num++) < 0)
|
||||
return NULL;
|
||||
|
||||
ctrl = (struct jack_client_control *)jack_shm_addr(&info);
|
||||
ctrl->info = info;
|
||||
|
||||
strcpy(ctrl->name, name);
|
||||
for (int i = 0; i < jack_notify_max; i++)
|
||||
ctrl->callback[i] = false;
|
||||
|
||||
// Always activated
|
||||
ctrl->callback[jack_notify_AddClient] = true;
|
||||
ctrl->callback[jack_notify_RemoveClient] = true;
|
||||
ctrl->callback[jack_notify_ActivateClient] = true;
|
||||
ctrl->callback[jack_notify_LatencyCallback] = true;
|
||||
// So that driver synchro are correctly setup in "flush" or "normal" mode
|
||||
ctrl->callback[jack_notify_StartFreewheelCallback] = true;
|
||||
ctrl->callback[jack_notify_StopFreewheelCallback] = true;
|
||||
ctrl->ref_num = ref_num;
|
||||
ctrl->PID = pid;
|
||||
ctrl->transport_state = JackTransportStopped;
|
||||
ctrl->transport_sync = false;
|
||||
ctrl->transport_timebase = false;
|
||||
ctrl->active = false;
|
||||
ctrl->session_ID = uuid;
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <semaphore.h>
|
||||
|
||||
struct jack_synchro {
|
||||
char name[SYNC_MAX_NAME_SIZE];
|
||||
bool flush;
|
||||
|
|
@ -46,3 +48,29 @@ jack_synchro_init(struct jack_synchro *synchro,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
jack_synchro_signal(struct jack_synchro *synchro)
|
||||
{
|
||||
int res;
|
||||
if (synchro->flush)
|
||||
return true;
|
||||
if ((res = sem_post(synchro->semaphore)) < 0)
|
||||
pw_log_error("semaphore %s post err = %s", synchro->name, strerror(errno));
|
||||
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
jack_synchro_wait(struct jack_synchro *synchro)
|
||||
{
|
||||
int res;
|
||||
while ((res = sem_wait(synchro->semaphore)) < 0) {
|
||||
if (errno != EINTR)
|
||||
continue;
|
||||
|
||||
pw_log_error("semaphore %s wait err = %s", synchro->name, strerror(errno));
|
||||
break;
|
||||
}
|
||||
return res == 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue