|
Packit |
675970 |
/*
|
|
Packit |
675970 |
* PCM - JACK plugin
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* Copyright (c) 2003 by Maarten de Boer <mdeboer@iua.upf.es>
|
|
Packit |
675970 |
* 2005 Takashi Iwai <tiwai@suse.de>
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* This library is free software; you can redistribute it and/or modify
|
|
Packit |
675970 |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit |
675970 |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
675970 |
* the License, or (at your option) any later version.
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
675970 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
675970 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
675970 |
* GNU Lesser General Public License for more details.
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
675970 |
* License along with this library; if not, write to the Free Software
|
|
Packit |
675970 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
*/
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#include <stdbool.h>
|
|
Packit |
675970 |
#include <byteswap.h>
|
|
Packit |
675970 |
#include <sys/shm.h>
|
|
Packit |
675970 |
#include <sys/types.h>
|
|
Packit |
675970 |
#include <sys/socket.h>
|
|
Packit |
675970 |
#include <jack/jack.h>
|
|
Packit |
675970 |
#include <alsa/asoundlib.h>
|
|
Packit |
675970 |
#include <alsa/pcm_external.h>
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#define MAX_PERIODS_MULTIPLE 64
|
|
Packit |
675970 |
|
|
Packit |
675970 |
typedef struct snd_pcm_jack_port_list {
|
|
Packit |
675970 |
struct snd_pcm_jack_port_list *next;
|
|
Packit |
675970 |
/* will always be allocated with size of the string.
|
|
Packit |
675970 |
* See snd_pcm_jack_port_list_add().
|
|
Packit |
675970 |
*/
|
|
Packit |
675970 |
char name[0];
|
|
Packit |
675970 |
} snd_pcm_jack_port_list_t;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
typedef struct {
|
|
Packit |
675970 |
snd_pcm_ioplug_t io;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
int fd;
|
|
Packit |
675970 |
int activated; /* jack is activated? */
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_pcm_jack_port_list_t **port_names;
|
|
Packit |
675970 |
unsigned int num_ports;
|
|
Packit |
675970 |
snd_pcm_uframes_t boundary;
|
|
Packit |
675970 |
snd_pcm_uframes_t hw_ptr;
|
|
Packit |
675970 |
unsigned int sample_bits;
|
|
Packit |
675970 |
snd_pcm_uframes_t min_avail;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_pcm_channel_area_t *areas;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack_port_t **ports;
|
|
Packit |
675970 |
jack_client_t *client;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
/* JACK thread -> ALSA thread */
|
|
Packit |
675970 |
bool xrun_detected;
|
|
Packit |
675970 |
} snd_pcm_jack_t;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
/* snd_pcm_ioplug_avail() was introduced after alsa-lib 1.1.6 */
|
|
Packit |
675970 |
#if SND_LIB_VERSION < 0x10107
|
|
Packit |
675970 |
static snd_pcm_uframes_t snd_pcm_ioplug_avail(const snd_pcm_ioplug_t *io,
|
|
Packit |
675970 |
const snd_pcm_uframes_t hw_ptr,
|
|
Packit |
675970 |
const snd_pcm_uframes_t appl_ptr)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
return io->buffer_size - snd_pcm_ioplug_hw_avail(io, hw_ptr, appl_ptr);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
#endif
|
|
Packit |
675970 |
|
|
Packit |
675970 |
/* adds one element to the head of the list */
|
|
Packit |
675970 |
static int snd_pcm_jack_port_list_add(snd_pcm_jack_t *jack,
|
|
Packit |
675970 |
const unsigned int channel,
|
|
Packit |
675970 |
const char * const name)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
const size_t name_size = strlen(name) + 1;
|
|
Packit |
675970 |
const size_t elem_size = sizeof(snd_pcm_jack_port_list_t) + name_size;
|
|
Packit |
675970 |
snd_pcm_jack_port_list_t * const elem = calloc(1, elem_size);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (elem == NULL)
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
/* Above it is guaranteed that elem->name is big enough for the size of
|
|
Packit |
675970 |
* name because strlen(name) + 1 will be used to allocate the buffer.
|
|
Packit |
675970 |
*/
|
|
Packit |
675970 |
strcpy(elem->name, name);
|
|
Packit |
675970 |
elem->next = jack->port_names[channel];
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->port_names[channel] = elem;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int pcm_poll_block_check(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
static char buf[32];
|
|
Packit |
675970 |
snd_pcm_uframes_t avail;
|
|
Packit |
675970 |
snd_pcm_jack_t *jack = io->private_data;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (io->state == SND_PCM_STATE_RUNNING ||
|
|
Packit |
675970 |
io->state == SND_PCM_STATE_DRAINING ||
|
|
Packit |
675970 |
(io->state == SND_PCM_STATE_PREPARED && io->stream == SND_PCM_STREAM_CAPTURE)) {
|
|
Packit |
675970 |
avail = snd_pcm_ioplug_avail(io, jack->hw_ptr, io->appl_ptr);
|
|
Packit |
675970 |
if (avail < jack->min_avail) {
|
|
Packit |
675970 |
while (read(io->poll_fd, &buf, sizeof(buf)) == sizeof(buf))
|
|
Packit |
675970 |
;
|
|
Packit |
675970 |
return 1;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int pcm_poll_unblock_check(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
static char buf[1];
|
|
Packit |
675970 |
snd_pcm_uframes_t avail;
|
|
Packit |
675970 |
snd_pcm_jack_t *jack = io->private_data;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
avail = snd_pcm_ioplug_avail(io, jack->hw_ptr, io->appl_ptr);
|
|
Packit |
675970 |
/* In draining state poll_fd is used to wait
|
|
Packit |
675970 |
* till all pending frames are played.
|
|
Packit |
675970 |
* Therefore it has to be guarantee that a poll event is also generated
|
|
Packit |
675970 |
* if the buffer contains less than min_avail frames
|
|
Packit |
675970 |
*/
|
|
Packit |
675970 |
if (avail >= jack->min_avail || io->state == SND_PCM_STATE_DRAINING) {
|
|
Packit |
675970 |
write(jack->fd, &buf, 1);
|
|
Packit |
675970 |
return 1;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void snd_pcm_jack_free(snd_pcm_jack_t *jack)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
if (jack == NULL)
|
|
Packit |
675970 |
return;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (jack->client)
|
|
Packit |
675970 |
jack_client_close(jack->client);
|
|
Packit |
675970 |
if (jack->port_names) {
|
|
Packit |
675970 |
unsigned int i;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
for (i = 0; i < jack->num_ports; i++) {
|
|
Packit |
675970 |
snd_pcm_jack_port_list_t *port_elem =
|
|
Packit |
675970 |
jack->port_names[i];
|
|
Packit |
675970 |
|
|
Packit |
675970 |
while (port_elem != NULL) {
|
|
Packit |
675970 |
snd_pcm_jack_port_list_t *next_port_elem =
|
|
Packit |
675970 |
port_elem->next;
|
|
Packit |
675970 |
free(port_elem);
|
|
Packit |
675970 |
port_elem = next_port_elem;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
free(jack->port_names);
|
|
Packit |
675970 |
jack->port_names = NULL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
if (jack->fd >= 0)
|
|
Packit |
675970 |
close(jack->fd);
|
|
Packit |
675970 |
if (jack->io.poll_fd >= 0)
|
|
Packit |
675970 |
close(jack->io.poll_fd);
|
|
Packit |
675970 |
free(jack->areas);
|
|
Packit |
675970 |
free(jack->ports);
|
|
Packit |
675970 |
free(jack);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_jack_close(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_jack_t *jack = io->private_data;
|
|
Packit |
675970 |
snd_pcm_jack_free(jack);
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_jack_poll_revents(snd_pcm_ioplug_t *io,
|
|
Packit |
675970 |
struct pollfd *pfds, unsigned int nfds,
|
|
Packit |
675970 |
unsigned short *revents)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
assert(pfds && nfds == 1 && revents);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
*revents = pfds[0].revents & ~(POLLIN | POLLOUT);
|
|
Packit |
675970 |
if (pfds[0].revents & POLLIN && !pcm_poll_block_check(io))
|
|
Packit |
675970 |
*revents |= (io->stream == SND_PCM_STREAM_PLAYBACK) ? POLLOUT : POLLIN;
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_sframes_t snd_pcm_jack_pointer(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_jack_t *jack = io->private_data;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (jack->xrun_detected)
|
|
Packit |
675970 |
return -EPIPE;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#ifdef SND_PCM_IOPLUG_FLAG_BOUNDARY_WA
|
|
Packit |
675970 |
return jack->hw_ptr;
|
|
Packit |
675970 |
#else
|
|
Packit |
675970 |
return jack->hw_ptr % io->buffer_size;
|
|
Packit |
675970 |
#endif
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int
|
|
Packit |
675970 |
snd_pcm_jack_process_cb(jack_nframes_t nframes, snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_jack_t *jack = io->private_data;
|
|
Packit |
675970 |
snd_pcm_uframes_t xfer = 0;
|
|
Packit |
675970 |
unsigned int channel;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
for (channel = 0; channel < io->channels; channel++) {
|
|
Packit |
675970 |
jack->areas[channel].addr =
|
|
Packit |
675970 |
jack_port_get_buffer (jack->ports[channel], nframes);
|
|
Packit |
675970 |
jack->areas[channel].first = 0;
|
|
Packit |
675970 |
jack->areas[channel].step = jack->sample_bits;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (io->state == SND_PCM_STATE_RUNNING ||
|
|
Packit |
675970 |
io->state == SND_PCM_STATE_DRAINING) {
|
|
Packit |
675970 |
snd_pcm_uframes_t hw_ptr = jack->hw_ptr;
|
|
Packit |
675970 |
const snd_pcm_uframes_t hw_avail = snd_pcm_ioplug_hw_avail(io, hw_ptr,
|
|
Packit |
675970 |
io->appl_ptr);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (hw_avail > 0) {
|
|
Packit |
675970 |
const snd_pcm_channel_area_t *areas = snd_pcm_ioplug_mmap_areas(io);
|
|
Packit |
675970 |
const snd_pcm_uframes_t offset = hw_ptr % io->buffer_size;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
xfer = nframes;
|
|
Packit |
675970 |
if (xfer > hw_avail)
|
|
Packit |
675970 |
xfer = hw_avail;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (io->stream == SND_PCM_STREAM_PLAYBACK)
|
|
Packit |
675970 |
snd_pcm_areas_copy_wrap(jack->areas, 0, nframes,
|
|
Packit |
675970 |
areas, offset,
|
|
Packit |
675970 |
io->buffer_size,
|
|
Packit |
675970 |
io->channels, xfer,
|
|
Packit |
675970 |
io->format);
|
|
Packit |
675970 |
else
|
|
Packit |
675970 |
snd_pcm_areas_copy_wrap(areas, offset,
|
|
Packit |
675970 |
io->buffer_size,
|
|
Packit |
675970 |
jack->areas, 0, nframes,
|
|
Packit |
675970 |
io->channels, xfer,
|
|
Packit |
675970 |
io->format);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
hw_ptr += xfer;
|
|
Packit |
675970 |
if (hw_ptr >= jack->boundary)
|
|
Packit |
675970 |
hw_ptr -= jack->boundary;
|
|
Packit |
675970 |
jack->hw_ptr = hw_ptr;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
/* check if requested frames were copied */
|
|
Packit |
675970 |
if (xfer < nframes) {
|
|
Packit |
675970 |
/* always fill the not yet written JACK buffer with silence */
|
|
Packit |
675970 |
if (io->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit |
675970 |
const snd_pcm_uframes_t frames = nframes - xfer;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_pcm_areas_silence(jack->areas, xfer, io->channels,
|
|
Packit |
675970 |
frames, io->format);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (io->state == SND_PCM_STATE_RUNNING ||
|
|
Packit |
675970 |
io->state == SND_PCM_STATE_DRAINING) {
|
|
Packit |
675970 |
/* report Xrun to user application */
|
|
Packit |
675970 |
jack->xrun_detected = true;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
pcm_poll_unblock_check(io); /* unblock socket for polling if needed */
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void jack_allocate_and_register_ports(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_jack_t *jack = io->private_data;
|
|
Packit |
675970 |
unsigned int i;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->ports = calloc(io->channels, sizeof(jack_port_t *));
|
|
Packit |
675970 |
|
|
Packit |
675970 |
for (i = 0; i < io->channels; i++) {
|
|
Packit |
675970 |
char port_name[32];
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (io->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit |
675970 |
sprintf(port_name, "out_%03d", i);
|
|
Packit |
675970 |
jack->ports[i] = jack_port_register(jack->client, port_name,
|
|
Packit |
675970 |
JACK_DEFAULT_AUDIO_TYPE,
|
|
Packit |
675970 |
JackPortIsOutput, 0);
|
|
Packit |
675970 |
} else {
|
|
Packit |
675970 |
sprintf(port_name, "in_%03d", i);
|
|
Packit |
675970 |
jack->ports[i] = jack_port_register(jack->client, port_name,
|
|
Packit |
675970 |
JACK_DEFAULT_AUDIO_TYPE,
|
|
Packit |
675970 |
JackPortIsInput, 0);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_jack_prepare(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_jack_t *jack = io->private_data;
|
|
Packit |
675970 |
unsigned int i;
|
|
Packit |
675970 |
snd_pcm_sw_params_t *swparams;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (io->channels != jack->num_ports) {
|
|
Packit |
675970 |
SNDERR("Channel count %d not equal to no. of ports %d in JACK",
|
|
Packit |
675970 |
io->channels, jack->num_ports);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->hw_ptr = 0;
|
|
Packit |
675970 |
jack->xrun_detected = false;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->min_avail = io->period_size;
|
|
Packit |
675970 |
snd_pcm_sw_params_alloca(&swparams);
|
|
Packit |
675970 |
err = snd_pcm_sw_params_current(io->pcm, swparams);
|
|
Packit |
675970 |
if (err == 0) {
|
|
Packit |
675970 |
snd_pcm_sw_params_get_avail_min(swparams, &jack->min_avail);
|
|
Packit |
675970 |
/* get boundary for available calulation */
|
|
Packit |
675970 |
snd_pcm_sw_params_get_boundary(swparams, &jack->boundary);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (io->stream == SND_PCM_STREAM_PLAYBACK)
|
|
Packit |
675970 |
pcm_poll_unblock_check(io); /* playback pcm initially accepts writes */
|
|
Packit |
675970 |
else
|
|
Packit |
675970 |
pcm_poll_block_check(io); /* block capture pcm if that's XRUN recovery */
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (!jack->ports) {
|
|
Packit |
675970 |
jack_allocate_and_register_ports(io);
|
|
Packit |
675970 |
jack_set_process_callback(jack->client,
|
|
Packit |
675970 |
(JackProcessCallback)snd_pcm_jack_process_cb, io);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (jack->activated)
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (jack_activate(jack->client))
|
|
Packit |
675970 |
return -EIO;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->activated = 1;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
for (i = 0; i < io->channels && i < jack->num_ports; i++) {
|
|
Packit |
675970 |
const char * const own_port = jack_port_name(jack->ports[i]);
|
|
Packit |
675970 |
snd_pcm_jack_port_list_t *port_elem;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
for (port_elem = jack->port_names[i]; port_elem != NULL;
|
|
Packit |
675970 |
port_elem = port_elem->next) {
|
|
Packit |
675970 |
const char *src, *dst;
|
|
Packit |
675970 |
if (io->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit |
675970 |
src = own_port;
|
|
Packit |
675970 |
dst = port_elem->name;
|
|
Packit |
675970 |
} else {
|
|
Packit |
675970 |
src = port_elem->name;
|
|
Packit |
675970 |
dst = own_port;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
if (jack_connect(jack->client, src, dst)) {
|
|
Packit |
675970 |
fprintf(stderr, "cannot connect %s to %s\n",
|
|
Packit |
675970 |
src, dst);
|
|
Packit |
675970 |
return -EIO;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_jack_start(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
(void)io;
|
|
Packit |
675970 |
/*
|
|
Packit |
675970 |
* Since the processing of jack_activate() and jack_connect() take a
|
|
Packit |
675970 |
* while longer, snd_pcm_jack_start() was blocked.
|
|
Packit |
675970 |
* Consider a usecase of reading the data from capture device and
|
|
Packit |
675970 |
* writing to a playback device, since the capture device is
|
|
Packit |
675970 |
* already started and the starting of playback device is blocked,
|
|
Packit |
675970 |
* it leads to XRUNs for capture device.
|
|
Packit |
675970 |
* Therefore these calls are moved to snd_pcm_jack_prepare(),
|
|
Packit |
675970 |
* So that the capture and playback devices can be prepared in advance
|
|
Packit |
675970 |
* and starting of the device doesn't take too long.
|
|
Packit |
675970 |
*/
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_jack_stop(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
(void)io;
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_jack_hw_free(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_jack_t *jack = io->private_data;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (jack->activated) {
|
|
Packit |
675970 |
jack_deactivate(jack->client);
|
|
Packit |
675970 |
jack->activated = 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
#if 0
|
|
Packit |
675970 |
unsigned i;
|
|
Packit |
675970 |
for (i = 0; i < io->channels; i++) {
|
|
Packit |
675970 |
if (jack->ports[i]) {
|
|
Packit |
675970 |
jack_port_unregister(jack->client, jack->ports[i]);
|
|
Packit |
675970 |
jack->ports[i] = NULL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
#endif
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_ioplug_callback_t jack_pcm_callback = {
|
|
Packit |
675970 |
.close = snd_pcm_jack_close,
|
|
Packit |
675970 |
.start = snd_pcm_jack_start,
|
|
Packit |
675970 |
.stop = snd_pcm_jack_stop,
|
|
Packit |
675970 |
.pointer = snd_pcm_jack_pointer,
|
|
Packit |
675970 |
.hw_free = snd_pcm_jack_hw_free,
|
|
Packit |
675970 |
.prepare = snd_pcm_jack_prepare,
|
|
Packit |
675970 |
.poll_revents = snd_pcm_jack_poll_revents,
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0]))
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int jack_set_hw_constraint(snd_pcm_jack_t *jack)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
unsigned int access_list[] = {
|
|
Packit |
675970 |
SND_PCM_ACCESS_MMAP_INTERLEAVED,
|
|
Packit |
675970 |
SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
|
|
Packit |
675970 |
SND_PCM_ACCESS_RW_INTERLEAVED,
|
|
Packit |
675970 |
SND_PCM_ACCESS_RW_NONINTERLEAVED
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
unsigned int format = SND_PCM_FORMAT_FLOAT;
|
|
Packit |
675970 |
unsigned int rate = jack_get_sample_rate(jack->client);
|
|
Packit |
675970 |
unsigned int psize_list[MAX_PERIODS_MULTIPLE];
|
|
Packit |
675970 |
unsigned int nframes = jack_get_buffer_size(jack->client);
|
|
Packit |
675970 |
unsigned int jack_buffer_bytes = (snd_pcm_format_size(format, nframes) *
|
|
Packit |
675970 |
jack->num_ports);
|
|
Packit |
675970 |
unsigned int i;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (!jack_buffer_bytes) {
|
|
Packit |
675970 |
SNDERR("Buffer size is zero");
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
for (i = 1; i <= ARRAY_SIZE(psize_list); i++)
|
|
Packit |
675970 |
psize_list[i-1] = jack_buffer_bytes * i;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->sample_bits = snd_pcm_format_physical_width(format);
|
|
Packit |
675970 |
if ((err = snd_pcm_ioplug_set_param_list(&jack->io, SND_PCM_IOPLUG_HW_ACCESS,
|
|
Packit |
675970 |
ARRAY_SIZE(access_list), access_list)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_list(&jack->io, SND_PCM_IOPLUG_HW_FORMAT,
|
|
Packit |
675970 |
1, &format)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_minmax(&jack->io, SND_PCM_IOPLUG_HW_CHANNELS,
|
|
Packit |
675970 |
jack->num_ports, jack->num_ports)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_minmax(&jack->io, SND_PCM_IOPLUG_HW_RATE,
|
|
Packit |
675970 |
rate, rate)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_list(&jack->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
|
|
Packit |
675970 |
ARRAY_SIZE(psize_list), psize_list)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_minmax(&jack->io, SND_PCM_IOPLUG_HW_PERIODS,
|
|
Packit |
675970 |
2, 64)) < 0)
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int parse_ports(snd_pcm_jack_t *jack, snd_config_t *conf)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_config_iterator_t i, next;
|
|
Packit |
675970 |
snd_pcm_jack_port_list_t **ports = NULL;
|
|
Packit |
675970 |
unsigned int cnt = 0;
|
|
Packit |
675970 |
unsigned int channel;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (conf == NULL)
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_config_for_each(i, next, conf) {
|
|
Packit |
675970 |
snd_config_t *n = snd_config_iterator_entry(i);
|
|
Packit |
675970 |
const char *id;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
cnt++;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
jack->port_names = ports = calloc(cnt, sizeof(jack->port_names[0]));
|
|
Packit |
675970 |
if (ports == NULL)
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
jack->num_ports = cnt;
|
|
Packit |
675970 |
snd_config_for_each(i, next, conf) {
|
|
Packit |
675970 |
snd_config_t *n = snd_config_iterator_entry(i);
|
|
Packit |
675970 |
const char *id;
|
|
Packit |
675970 |
const char *port;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
channel = atoi(id);
|
|
Packit |
675970 |
if (snd_config_get_string(n, &port) >= 0) {
|
|
Packit |
675970 |
err = snd_pcm_jack_port_list_add(jack, channel, port);
|
|
Packit |
675970 |
if (err < 0)
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
} else if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
675970 |
snd_config_iterator_t k, next_k;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_config_for_each(k, next_k, n) {
|
|
Packit |
675970 |
snd_config_t *m = snd_config_iterator_entry(k);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (snd_config_get_string(m, &port) < 0)
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
err = snd_pcm_jack_port_list_add(jack, channel,
|
|
Packit |
675970 |
port);
|
|
Packit |
675970 |
if (err < 0)
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
} else {
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int make_nonblock(int fd)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
int fl;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if ((fl = fcntl(fd, F_GETFL)) < 0)
|
|
Packit |
675970 |
return fl;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (fl & O_NONBLOCK)
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_jack_open(snd_pcm_t **pcmp, const char *name,
|
|
Packit |
675970 |
const char *client_name,
|
|
Packit |
675970 |
snd_config_t *playback_conf,
|
|
Packit |
675970 |
snd_config_t *capture_conf,
|
|
Packit |
675970 |
snd_pcm_stream_t stream, int mode)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_jack_t *jack;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
int fd[2];
|
|
Packit |
675970 |
static unsigned int num = 0;
|
|
Packit |
675970 |
char jack_client_name[32];
|
|
Packit |
675970 |
|
|
Packit |
675970 |
assert(pcmp);
|
|
Packit |
675970 |
jack = calloc(1, sizeof(*jack));
|
|
Packit |
675970 |
if (!jack)
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->fd = -1;
|
|
Packit |
675970 |
jack->io.poll_fd = -1;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
err = parse_ports(jack, stream == SND_PCM_STREAM_PLAYBACK ?
|
|
Packit |
675970 |
playback_conf : capture_conf);
|
|
Packit |
675970 |
if (err) {
|
|
Packit |
675970 |
snd_pcm_jack_free(jack);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (jack->num_ports == 0) {
|
|
Packit |
675970 |
SNDERR("define the %s_ports section",
|
|
Packit |
675970 |
stream == SND_PCM_STREAM_PLAYBACK ? "playback" : "capture");
|
|
Packit |
675970 |
snd_pcm_jack_free(jack);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (client_name == NULL)
|
|
Packit |
675970 |
err = snprintf(jack_client_name, sizeof(jack_client_name),
|
|
Packit |
675970 |
"alsa-jack.%s%s.%d.%d", name,
|
|
Packit |
675970 |
stream == SND_PCM_STREAM_PLAYBACK ? "P" : "C",
|
|
Packit |
675970 |
getpid(), num++);
|
|
Packit |
675970 |
else
|
|
Packit |
675970 |
err = snprintf(jack_client_name, sizeof(jack_client_name),
|
|
Packit |
675970 |
"%s", client_name);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (err >= (int)sizeof(jack_client_name)) {
|
|
Packit |
675970 |
fprintf(stderr, "%s: WARNING: JACK client name '%s' truncated to %d characters, might not be unique\n",
|
|
Packit |
675970 |
__func__, jack_client_name, (int)strlen(jack_client_name));
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->client = jack_client_open(jack_client_name, JackNoStartServer, NULL);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (jack->client == 0) {
|
|
Packit |
675970 |
snd_pcm_jack_free(jack);
|
|
Packit |
675970 |
return -ENOENT;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->areas = calloc(jack->num_ports, sizeof(snd_pcm_channel_area_t));
|
|
Packit |
675970 |
if (! jack->areas) {
|
|
Packit |
675970 |
snd_pcm_jack_free(jack);
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
socketpair(AF_LOCAL, SOCK_STREAM, 0, fd);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
make_nonblock(fd[0]);
|
|
Packit |
675970 |
make_nonblock(fd[1]);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->fd = fd[0];
|
|
Packit |
675970 |
|
|
Packit |
675970 |
jack->io.version = SND_PCM_IOPLUG_VERSION;
|
|
Packit |
675970 |
jack->io.name = "ALSA <-> JACK PCM I/O Plugin";
|
|
Packit |
675970 |
jack->io.callback = &jack_pcm_callback;
|
|
Packit |
675970 |
jack->io.private_data = jack;
|
|
Packit |
675970 |
jack->io.poll_fd = fd[1];
|
|
Packit |
675970 |
jack->io.poll_events = POLLIN;
|
|
Packit |
675970 |
jack->io.mmap_rw = 1;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#ifdef SND_PCM_IOPLUG_FLAG_BOUNDARY_WA
|
|
Packit |
675970 |
jack->io.flags = SND_PCM_IOPLUG_FLAG_BOUNDARY_WA;
|
|
Packit |
675970 |
#else
|
|
Packit |
675970 |
#warning hw_ptr updates of buffer_size will not be recognized by the ALSA library. Consider to update your ALSA library.
|
|
Packit |
675970 |
#endif
|
|
Packit |
675970 |
|
|
Packit |
675970 |
err = snd_pcm_ioplug_create(&jack->io, name, stream, mode);
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
snd_pcm_jack_free(jack);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
err = jack_set_hw_constraint(jack);
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
snd_pcm_ioplug_delete(&jack->io);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
*pcmp = jack->io.pcm;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
|
|
Packit |
675970 |
SND_PCM_PLUGIN_DEFINE_FUNC(jack)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_config_iterator_t i, next;
|
|
Packit |
675970 |
snd_config_t *playback_conf = NULL;
|
|
Packit |
675970 |
snd_config_t *capture_conf = NULL;
|
|
Packit |
675970 |
const char *client_name = NULL;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_config_for_each(i, next, conf) {
|
|
Packit |
675970 |
snd_config_t *n = snd_config_iterator_entry(i);
|
|
Packit |
675970 |
const char *id;
|
|
Packit |
675970 |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0)
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
if (strcmp(id, "name") == 0) {
|
|
Packit |
675970 |
snd_config_get_string(n, &client_name);
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
if (strcmp(id, "playback_ports") == 0) {
|
|
Packit |
675970 |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
675970 |
SNDERR("Invalid type for %s", id);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
playback_conf = n;
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
if (strcmp(id, "capture_ports") == 0) {
|
|
Packit |
675970 |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
675970 |
SNDERR("Invalid type for %s", id);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
capture_conf = n;
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
SNDERR("Unknown field %s", id);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
err = snd_pcm_jack_open(pcmp, name, client_name, playback_conf, capture_conf, stream, mode);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
SND_PCM_PLUGIN_SYMBOL(jack);
|