315 lines
8.9 KiB
C++
315 lines
8.9 KiB
C++
// ------------------------------------------------------------------------
|
|
// audioio-jack.cpp: Interface to JACK audio framework
|
|
// Copyright (C) 2001-2003,2008,2009 Kai Vehmanen
|
|
//
|
|
// Attributes:
|
|
// eca-style-version: 3 (see Ecasound Programmer's Guide)
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program 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 General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
// ------------------------------------------------------------------------
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <iostream>
|
|
|
|
#include <jack/jack.h>
|
|
#include <kvu_dbc.h>
|
|
#include <kvu_numtostr.h>
|
|
|
|
#include "audioio.h"
|
|
#include "eca-version.h"
|
|
#include "eca-logger.h"
|
|
#include "samplebuffer.h"
|
|
|
|
#include "audioio_jack.h"
|
|
#include "audioio_jack_manager.h"
|
|
|
|
#ifdef ECA_ENABLE_AUDIOIO_PLUGINS
|
|
/* see eca-static-object-maps.cpp */
|
|
static const char* audio_io_keyword_const = "jack";
|
|
static const char* audio_io_keyword_regex_const = "(^jack$)|(^jack_alsa$)|(^jack_auto$)|(^jack_generic$)";
|
|
|
|
AUDIO_IO* audio_io_descriptor(void) { return new AUDIO_IO_JACK(); }
|
|
const char* audio_io_keyword(void) {return audio_io_keyword_const; }
|
|
const char* audio_io_keyword_regex(void){return audio_io_keyword_regex_const; }
|
|
int audio_io_interface_version(void) { return ecasound_library_version_current; }
|
|
#endif
|
|
|
|
AUDIO_IO_JACK::AUDIO_IO_JACK (void)
|
|
: jackmgr_rep(0),
|
|
myid_rep(0),
|
|
error_flag_rep(false)
|
|
{
|
|
ECA_LOG_MSG(ECA_LOGGER::functions, "constructor");
|
|
|
|
}
|
|
|
|
AUDIO_IO_JACK::~AUDIO_IO_JACK(void)
|
|
{
|
|
if (is_open() == true && is_running()) stop();
|
|
if (is_open() == true) {
|
|
close();
|
|
}
|
|
}
|
|
|
|
AUDIO_IO_MANAGER* AUDIO_IO_JACK::create_object_manager(void) const
|
|
{
|
|
return new AUDIO_IO_JACK_MANAGER();
|
|
}
|
|
|
|
void AUDIO_IO_JACK::set_manager(AUDIO_IO_JACK_MANAGER* mgr, int id)
|
|
{
|
|
string mgrname = (mgr != 0 ? mgr->name() : "null");
|
|
ECA_LOG_MSG(ECA_LOGGER::system_objects,
|
|
"setting manager to " + mgrname);
|
|
jackmgr_rep = mgr;
|
|
myid_rep = id;
|
|
}
|
|
|
|
void AUDIO_IO_JACK::open(void) throw(AUDIO_IO::SETUP_ERROR&)
|
|
{
|
|
ECA_LOG_MSG(ECA_LOGGER::system_objects, "open");
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
set_sample_format(ECA_AUDIO_FORMAT::sfmt_f32_be);
|
|
#else
|
|
set_sample_format(ECA_AUDIO_FORMAT::sfmt_f32_le);
|
|
#endif
|
|
toggle_interleaved_channels(false);
|
|
|
|
if (jackmgr_rep != 0) {
|
|
string my_in_portname ("in"), my_out_portname ("out");
|
|
|
|
if (label() == "jack" &&
|
|
params_rep.size() > 2 &&
|
|
params_rep[2].size() > 0) {
|
|
my_in_portname = my_out_portname = params_rep[2];
|
|
}
|
|
/* note: deprecated interface */
|
|
else if (label() == "jack_generic" &&
|
|
params_rep.size() > 1) {
|
|
my_in_portname = my_out_portname = params_rep[1];
|
|
}
|
|
|
|
jackmgr_rep->open(myid_rep);
|
|
|
|
if (jackmgr_rep->is_open() != true) {
|
|
/* unable to open connection to jackd, exit */
|
|
throw(SETUP_ERROR(SETUP_ERROR::unexpected, "AUDIOIO-JACK: Unable to open JACK-client"));
|
|
}
|
|
|
|
if (samples_per_second() != jackmgr_rep->samples_per_second()) {
|
|
set_samples_per_second(jackmgr_rep->samples_per_second());
|
|
ECA_LOG_MSG(ECA_LOGGER::system_objects,
|
|
"Note! Locking to jackd samplerate " +
|
|
kvu_numtostr(samples_per_second()));
|
|
}
|
|
|
|
if (buffersize() != jackmgr_rep->buffersize()) {
|
|
long int jackd_bsize = jackmgr_rep->buffersize();
|
|
jackmgr_rep->close(myid_rep);
|
|
throw(SETUP_ERROR(SETUP_ERROR::unexpected,
|
|
"AUDIOIO-JACK: Cannot connect open connection! Buffersize " +
|
|
kvu_numtostr(buffersize()) + " differs from JACK server's buffersize of " +
|
|
kvu_numtostr(jackd_bsize) + "."));
|
|
}
|
|
|
|
if (io_mode() == AUDIO_IO::io_read) {
|
|
jackmgr_rep->register_jack_ports(myid_rep, channels(), my_in_portname);
|
|
}
|
|
else {
|
|
jackmgr_rep->register_jack_ports(myid_rep, channels(), my_out_portname);
|
|
}
|
|
|
|
/* - make automatic connections */
|
|
|
|
if (label() == "jack" &&
|
|
params_rep.size() > 1 &&
|
|
params_rep[1].size() > 0) {
|
|
/* note: if 2nd param given, use it as the client to autoconnect to */
|
|
jackmgr_rep->auto_connect_jack_port_client(myid_rep, params_rep[1], channels());
|
|
}
|
|
else if (label() == "jack_multi") {
|
|
int i;
|
|
for(i = 0; i < channels(); i++) {
|
|
if (static_cast<int>(params_rep.size()) > i + 1 &&
|
|
params_rep[i + 1].size() > 0) {
|
|
ECA_LOG_MSG(ECA_LOGGER::user_objects,
|
|
"adding auto connection from " +
|
|
my_out_portname + "_" + kvu_numtostr(i + 1) +
|
|
" to " +
|
|
params_rep[i + 1]);
|
|
jackmgr_rep->auto_connect_jack_port(myid_rep, i + 1, params_rep[i + 1]);
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
else if (label() == "jack_alsa") {
|
|
/* note: deprecated feature: 'alsa_pcm' is hidden in the port
|
|
list returned by jack_get_ports(), but as you can still
|
|
connect with the direct backend names, we have to keep this
|
|
code around to be backward compatible */
|
|
string in_aconn_portprefix, out_aconn_portprefix;
|
|
|
|
in_aconn_portprefix = "alsa_pcm:capture_";
|
|
out_aconn_portprefix = "alsa_pcm:playback_";
|
|
|
|
for(int n = 0; n < channels(); n++) {
|
|
if (io_mode() == AUDIO_IO::io_read) {
|
|
jackmgr_rep->auto_connect_jack_port(myid_rep, n + 1, in_aconn_portprefix + kvu_numtostr(n + 1));
|
|
}
|
|
else {
|
|
jackmgr_rep->auto_connect_jack_port(myid_rep, n + 1, out_aconn_portprefix + kvu_numtostr(n + 1));
|
|
}
|
|
}
|
|
}
|
|
/* note: deprecated interface, plain "jack" should be used now */
|
|
else if (label() == "jack_auto" &&
|
|
params_rep.size() > 1 &&
|
|
params_rep[1].size() > 0) {
|
|
jackmgr_rep->auto_connect_jack_port_client(myid_rep, params_rep[1], channels());
|
|
}
|
|
}
|
|
|
|
AUDIO_IO_DEVICE::open();
|
|
}
|
|
|
|
void AUDIO_IO_JACK::close(void)
|
|
{
|
|
ECA_LOG_MSG(ECA_LOGGER::system_objects, "close");
|
|
|
|
if (jackmgr_rep != 0) {
|
|
jackmgr_rep->unregister_jack_ports(myid_rep);
|
|
jackmgr_rep->close(myid_rep);
|
|
}
|
|
|
|
AUDIO_IO_DEVICE::close();
|
|
}
|
|
|
|
bool AUDIO_IO_JACK::finished(void) const
|
|
{
|
|
if (is_open() != true ||
|
|
jackmgr_rep == 0 ||
|
|
jackmgr_rep->is_open() != true ||
|
|
error_flag_rep == true)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
long int AUDIO_IO_JACK::read_samples(void* target_buffer, long int samples)
|
|
{
|
|
if (jackmgr_rep != 0) {
|
|
DBC_CHECK(samples == jackmgr_rep->buffersize());
|
|
long int res = jackmgr_rep->read_samples(myid_rep, target_buffer, samples);
|
|
return res;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void AUDIO_IO_JACK::write_buffer(SAMPLE_BUFFER* sbuf)
|
|
{
|
|
/* note: this is reimplemented only to catch errors with unsupported
|
|
* input streams (e.g. one produces by 'resample' object' */
|
|
|
|
if (sbuf->length_in_samples() > 0 &&
|
|
sbuf->length_in_samples() != jackmgr_rep->buffersize() &&
|
|
sbuf->event_tag_test(SAMPLE_BUFFER::tag_end_of_stream) != true) {
|
|
error_flag_rep = true;
|
|
ECA_LOG_MSG(ECA_LOGGER::errors,
|
|
"ERROR: Variable size input buffers detected at JACK output, stopping processing. "
|
|
"This can happen e.g. with a 'resample' input object.");
|
|
}
|
|
|
|
AUDIO_IO_DEVICE::write_buffer(sbuf);
|
|
}
|
|
|
|
void AUDIO_IO_JACK::write_samples(void* target_buffer, long int samples)
|
|
{
|
|
DBC_CHECK(samples <= jackmgr_rep->buffersize());
|
|
if (jackmgr_rep != 0) {
|
|
jackmgr_rep->write_samples(myid_rep, target_buffer, samples);
|
|
}
|
|
}
|
|
|
|
void AUDIO_IO_JACK::prepare(void)
|
|
{
|
|
ECA_LOG_MSG(ECA_LOGGER::system_objects, "prepare / " + label());
|
|
error_flag_rep = false;
|
|
AUDIO_IO_DEVICE::prepare();
|
|
}
|
|
|
|
void AUDIO_IO_JACK::start(void)
|
|
{
|
|
ECA_LOG_MSG(ECA_LOGGER::system_objects, "start / " + label());
|
|
AUDIO_IO_DEVICE::start();
|
|
}
|
|
|
|
void AUDIO_IO_JACK::stop(void)
|
|
{
|
|
ECA_LOG_MSG(ECA_LOGGER::system_objects, "stop / " + label());
|
|
AUDIO_IO_DEVICE::stop();
|
|
}
|
|
|
|
long int AUDIO_IO_JACK::latency(void) const
|
|
{
|
|
return jackmgr_rep == 0 ? 0 : jackmgr_rep->client_latency(myid_rep);
|
|
}
|
|
|
|
std::string AUDIO_IO_JACK::parameter_names(void) const
|
|
{
|
|
if (label() == "jack_generic")
|
|
return "label,portname";
|
|
|
|
else if (label() == "jack_auto")
|
|
return "label,client";
|
|
|
|
else if (label() == "jack_multi") {
|
|
string paramlist = "label,";
|
|
int i;
|
|
for(i = 0; i < channels(); i++) {
|
|
paramlist += ",dstport" + kvu_numtostr(i + 1);
|
|
}
|
|
return paramlist;
|
|
}
|
|
|
|
/* jack */
|
|
return "label,client,portprefix";
|
|
}
|
|
|
|
void AUDIO_IO_JACK::set_parameter(int param, std::string value)
|
|
{
|
|
if (param > static_cast<int>(params_rep.size()))
|
|
params_rep.resize(param);
|
|
|
|
params_rep[param - 1] = value;
|
|
|
|
if (param == 1) {
|
|
set_label(value);
|
|
}
|
|
}
|
|
|
|
std::string AUDIO_IO_JACK::get_parameter(int param) const
|
|
{
|
|
if (param > 0 && param <= static_cast<int>(params_rep.size()))
|
|
return params_rep[param - 1];
|
|
|
|
return AUDIO_IO::get_parameter(param);
|
|
}
|