You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/sound/JackDriver.cpp

2478 lines
72 KiB

/*
Rosegarden
A sequencer and musical notation editor.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <bownie@bownie.com>
The moral right of the authors to claim authorship of this work
has been asserted.
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. See the file
COPYING included with this distribution for more information.
*/
#include "JackDriver.h"
#include "AlsaDriver.h"
#include "MappedStudio.h"
#include "AudioProcess.h"
#include "Profiler.h"
#include "AudioLevel.h"
#include "Audit.h"
#include "PluginFactory.h"
#ifdef HAVE_ALSA
#ifdef HAVE_LIBJACK
//#define DEBUG_JACK_DRIVER 1
//#define DEBUG_JACK_TRANSPORT 1
//#define DEBUG_JACK_PROCESS 1
//#define DEBUG_JACK_XRUN 1
namespace Rosegarden
{
#if (defined(DEBUG_JACK_DRIVER) || defined(DEBUG_JACK_PROCESS) || defined(DEBUG_JACK_TRANSPORT))
static unsigned long framesThisPlay = 0;
static RealTime startTime;
#endif
JackDriver::JackDriver(AlsaDriver *alsaDriver) :
m_client(0),
m_bufferSize(0),
m_sampleRate(0),
m_tempOutBuffer(0),
m_jackTransportEnabled(false),
m_jackTransportMaster(false),
m_waiting(false),
m_waitingState(JackTransportStopped),
m_waitingToken(0),
m_ignoreProcessTransportCount(0),
m_bussMixer(0),
m_instrumentMixer(0),
m_fileReader(0),
m_fileWriter(0),
m_alsaDriver(alsaDriver),
m_masterLevel(1.0),
m_directMasterAudioInstruments(0L),
m_directMasterSynthInstruments(0L),
m_haveAsyncAudioEvent(false),
m_kickedOutAt(0),
m_framesProcessed(0),
m_ok(false)
{
assert(sizeof(sample_t) == sizeof(float));
initialise();
}
JackDriver::~JackDriver()
{
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::~JackDriver" << std::endl;
#endif
m_ok = false; // prevent any more work in process()
if (m_client) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::shutdown - deactivating JACK client"
<< std::endl;
#endif
if (jack_deactivate(m_client)) {
std::cerr << "JackDriver::shutdown - deactivation failed"
<< std::endl;
}
}
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::~JackDriver: terminating buss mixer" << std::endl;
#endif
AudioBussMixer *bussMixer = m_bussMixer;
m_bussMixer = 0;
if (bussMixer)
bussMixer->terminate();
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::~JackDriver: terminating instrument mixer" << std::endl;
#endif
AudioInstrumentMixer *instrumentMixer = m_instrumentMixer;
m_instrumentMixer = 0;
if (instrumentMixer) {
instrumentMixer->terminate();
instrumentMixer->destroyAllPlugins();
}
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::~JackDriver: terminating file reader" << std::endl;
#endif
AudioFileReader *reader = m_fileReader;
m_fileReader = 0;
if (reader)
reader->terminate();
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::~JackDriver: terminating file writer" << std::endl;
#endif
AudioFileWriter *writer = m_fileWriter;
m_fileWriter = 0;
if (writer)
writer->terminate();
if (m_client) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::shutdown - tearing down JACK client"
<< std::endl;
#endif
for (unsigned int i = 0; i < m_inputPorts.size(); ++i) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "unregistering input " << i << std::endl;
#endif
if (jack_port_unregister(m_client, m_inputPorts[i])) {
std::cerr << "JackDriver::shutdown - "
<< "can't unregister input port " << i + 1
<< std::endl;
}
}
for (unsigned int i = 0; i < m_outputSubmasters.size(); ++i) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "unregistering output sub " << i << std::endl;
#endif
if (jack_port_unregister(m_client, m_outputSubmasters[i])) {
std::cerr << "JackDriver::shutdown - "
<< "can't unregister output submaster " << i + 1 << std::endl;
}
}
for (unsigned int i = 0; i < m_outputMonitors.size(); ++i) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "unregistering output mon " << i << std::endl;
#endif
if (jack_port_unregister(m_client, m_outputMonitors[i])) {
std::cerr << "JackDriver::shutdown - "
<< "can't unregister output monitor " << i + 1 << std::endl;
}
}
for (unsigned int i = 0; i < m_outputMasters.size(); ++i) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "unregistering output master " << i << std::endl;
#endif
if (jack_port_unregister(m_client, m_outputMasters[i])) {
std::cerr << "JackDriver::shutdown - "
<< "can't unregister output master " << i + 1 << std::endl;
}
}
#ifdef DEBUG_JACK_DRIVER
std::cerr << "closing client" << std::endl;
#endif
jack_client_close(m_client);
std::cerr << "done" << std::endl;
m_client = 0;
}
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver: deleting mixers etc" << std::endl;
#endif
delete bussMixer;
delete instrumentMixer;
delete reader;
delete writer;
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::~JackDriver exiting" << std::endl;
#endif
}
void
JackDriver::initialise(bool reinitialise)
{
m_ok = false;
Audit audit;
audit << std::endl;
std::string jackClientName = "rosegarden";
// attempt connection to JACK server
//
if ((m_client = jack_client_new(jackClientName.c_str())) == 0) {
audit << "JackDriver::initialiseAudio - "
<< "JACK server not running"
<< std::endl;
return ;
}
InstrumentId instrumentBase;
int instrumentCount;
m_alsaDriver->getAudioInstrumentNumbers(instrumentBase, instrumentCount);
for (InstrumentId id = instrumentBase;
id < instrumentBase + instrumentCount; ++id) {
// prefill so that we can refer to the map without a lock (as
// the number of instruments won't change)
m_recordInputs[id] = RecordInputDesc(1000, -1, 0.0);
}
// set callbacks
//
jack_set_process_callback(m_client, jackProcessStatic, this);
jack_set_buffer_size_callback(m_client, jackBufferSize, this);
jack_set_sample_rate_callback(m_client, jackSampleRate, this);
jack_on_shutdown(m_client, jackShutdown, this);
jack_set_xrun_callback(m_client, jackXRun, this);
jack_set_sync_callback(m_client, jackSyncCallback, this);
// get and report the sample rate and buffer size
//
m_sampleRate = jack_get_sample_rate(m_client);
m_bufferSize = jack_get_buffer_size(m_client);
audit << "JackDriver::initialiseAudio - JACK sample rate = "
<< m_sampleRate << "Hz, buffer size = " << m_bufferSize
<< std::endl;
PluginFactory::setSampleRate(m_sampleRate);
// Get the initial buffer size before we activate the client
//
if (!reinitialise) {
// create processing buffer(s)
//
m_tempOutBuffer = new sample_t[m_bufferSize];
audit << "JackDriver::initialiseAudio - "
<< "creating disk thread" << std::endl;
m_fileReader = new AudioFileReader(m_alsaDriver, m_sampleRate);
m_fileWriter = new AudioFileWriter(m_alsaDriver, m_sampleRate);
m_instrumentMixer = new AudioInstrumentMixer
(m_alsaDriver, m_fileReader, m_sampleRate, m_bufferSize);
m_bussMixer = new AudioBussMixer
(m_alsaDriver, m_instrumentMixer, m_sampleRate, m_bufferSize);
m_instrumentMixer->setBussMixer(m_bussMixer);
// We run the file reader whatever, but we only run the other
// threads (instrument mixer, buss mixer, file writer) when we
// actually need them. (See updateAudioData and createRecordFile.)
m_fileReader->run();
}
// Create and connect the default numbers of ports. We always create
// one stereo pair each of master and monitor outs, and then we create
// record ins, fader outs and submaster outs according to the user's
// preferences. Since we don't know the user's preferences yet, we'll
// start by creating one pair of record ins and no fader or submaster
// outs.
//
m_outputMasters.clear();
m_outputMonitors.clear();
m_outputSubmasters.clear();
m_outputInstruments.clear();
m_inputPorts.clear();
if (!createMainOutputs()) { // one stereo pair master, one pair monitor
audit << "JackDriver::initialise - "
<< "failed to create main outputs!" << std::endl;
return ;
}
if (!createRecordInputs(1)) {
audit << "JackDriver::initialise - "
<< "failed to create record inputs!" << std::endl;
return ;
}
if (jack_activate(m_client)) {
audit << "JackDriver::initialise - "
<< "client activation failed" << std::endl;
return ;
}
// Now set up the default connections.
std::string playback_1, playback_2;
const char **ports =
jack_get_ports(m_client, NULL, NULL,
JackPortIsPhysical | JackPortIsInput);
if (ports) {
if (ports[0])
playback_1 = std::string(ports[0]);
if (ports[1])
playback_2 = std::string(ports[1]);
// count ports
unsigned int i = 0;
for (i = 0; ports[i]; i++)
;
audit << "JackDriver::initialiseAudio - "
<< "found " << i << " JACK physical outputs"
<< std::endl;
} else
audit << "JackDriver::initialiseAudio - "
<< "no JACK physical outputs found"
<< std::endl;
free(ports);
if (playback_1 != "") {
audit << "JackDriver::initialiseAudio - "
<< "connecting from "
<< "\"" << jack_port_name(m_outputMasters[0])
<< "\" to \"" << playback_1.c_str() << "\""
<< std::endl;
// connect our client up to the ALSA ports - first left output
//
if (jack_connect(m_client, jack_port_name(m_outputMasters[0]),
playback_1.c_str())) {
audit << "JackDriver::initialiseAudio - "
<< "cannot connect to JACK output port" << std::endl;
return ;
}
/*
if (jack_connect(m_client, jack_port_name(m_outputMonitors[0]),
playback_1.c_str()))
{
audit << "JackDriver::initialiseAudio - "
<< "cannot connect to JACK output port" << std::endl;
return;
}
*/
}
if (playback_2 != "") {
audit << "JackDriver::initialiseAudio - "
<< "connecting from "
<< "\"" << jack_port_name(m_outputMasters[1])
<< "\" to \"" << playback_2.c_str() << "\""
<< std::endl;
if (jack_connect(m_client, jack_port_name(m_outputMasters[1]),
playback_2.c_str())) {
audit << "JackDriver::initialiseAudio - "
<< "cannot connect to JACK output port" << std::endl;
}
/*
if (jack_connect(m_client, jack_port_name(m_outputMonitors[1]),
playback_2.c_str()))
{
audit << "JackDriver::initialiseAudio - "
<< "cannot connect to JACK output port" << std::endl;
}
*/
}
std::string capture_1, capture_2;
ports =
jack_get_ports(m_client, NULL, NULL,
JackPortIsPhysical | JackPortIsOutput);
if (ports) {
if (ports[0])
capture_1 = std::string(ports[0]);
if (ports[1])
capture_2 = std::string(ports[1]);
// count ports
unsigned int i = 0;
for (i = 0; ports[i]; i++)
;
audit << "JackDriver::initialiseAudio - "
<< "found " << i << " JACK physical inputs"
<< std::endl;
} else
audit << "JackDriver::initialiseAudio - "
<< "no JACK physical inputs found"
<< std::endl;
free(ports);
if (capture_1 != "") {
audit << "JackDriver::initialiseAudio - "
<< "connecting from "
<< "\"" << capture_1.c_str()
<< "\" to \"" << jack_port_name(m_inputPorts[0]) << "\""
<< std::endl;
if (jack_connect(m_client, capture_1.c_str(),
jack_port_name(m_inputPorts[0]))) {
audit << "JackDriver::initialiseAudio - "
<< "cannot connect to JACK input port" << std::endl;
}
}
if (capture_2 != "") {
audit << "JackDriver::initialiseAudio - "
<< "connecting from "
<< "\"" << capture_2.c_str()
<< "\" to \"" << jack_port_name(m_inputPorts[1]) << "\""
<< std::endl;
if (jack_connect(m_client, capture_2.c_str(),
jack_port_name(m_inputPorts[1]))) {
audit << "JackDriver::initialiseAudio - "
<< "cannot connect to JACK input port" << std::endl;
}
}
audit << "JackDriver::initialiseAudio - "
<< "initialised JACK audio subsystem"
<< std::endl;
m_ok = true;
}
bool
JackDriver::createMainOutputs()
{
if (!m_client)
return false;
jack_port_t *port = jack_port_register
(m_client, "master out L",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if (!port)
return false;
m_outputMasters.push_back(port);
port = jack_port_register
(m_client, "master out R",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if (!port)
return false;
m_outputMasters.push_back(port);
port = jack_port_register
(m_client, "record monitor out L",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if (!port)
return false;
m_outputMonitors.push_back(port);
port = jack_port_register
(m_client, "record monitor out R",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if (!port)
return false;
m_outputMonitors.push_back(port);
return true;
}
bool
JackDriver::createFaderOutputs(int audioPairs, int synthPairs)
{
if (!m_client)
return false;
int pairs = audioPairs + synthPairs;
int pairsNow = m_outputInstruments.size() / 2;
if (pairs == pairsNow)
return true;
for (int i = pairsNow; i < pairs; ++i) {
char namebuffer[22];
jack_port_t *port;
if (i < audioPairs) {
snprintf(namebuffer, 21, "audio fader %d out L", i + 1);
} else {
snprintf(namebuffer, 21, "synth fader %d out L", i - audioPairs + 1);
}
port = jack_port_register(m_client,
namebuffer,
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput,
0);
if (!port)
return false;
m_outputInstruments.push_back(port);
if (i < audioPairs) {
snprintf(namebuffer, 21, "audio fader %d out R", i + 1);
} else {
snprintf(namebuffer, 21, "synth fader %d out R", i - audioPairs + 1);
}
port = jack_port_register(m_client,
namebuffer,
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput,
0);
if (!port)
return false;
m_outputInstruments.push_back(port);
}
while ((int)m_outputInstruments.size() > pairs * 2) {
std::vector<jack_port_t *>::iterator itr = m_outputInstruments.end();
--itr;
jack_port_unregister(m_client, *itr);
m_outputInstruments.erase(itr);
}
return true;
}
bool
JackDriver::createSubmasterOutputs(int pairs)
{
if (!m_client)
return false;
int pairsNow = m_outputSubmasters.size() / 2;
if (pairs == pairsNow)
return true;
for (int i = pairsNow; i < pairs; ++i) {
char namebuffer[22];
jack_port_t *port;
snprintf(namebuffer, 21, "submaster %d out L", i + 1);
port = jack_port_register(m_client,
namebuffer,
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput,
0);
if (!port)
return false;
m_outputSubmasters.push_back(port);
snprintf(namebuffer, 21, "submaster %d out R", i + 1);
port = jack_port_register(m_client,
namebuffer,
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput,
0);
if (!port)
return false;
m_outputSubmasters.push_back(port);
}
while ((int)m_outputSubmasters.size() > pairs * 2) {
std::vector<jack_port_t *>::iterator itr = m_outputSubmasters.end();
--itr;
jack_port_unregister(m_client, *itr);
m_outputSubmasters.erase(itr);
}
return true;
}
bool
JackDriver::createRecordInputs(int pairs)
{
if (!m_client)
return false;
int pairsNow = m_inputPorts.size() / 2;
if (pairs == pairsNow)
return true;
for (int i = pairsNow; i < pairs; ++i) {
char namebuffer[22];
jack_port_t *port;
snprintf(namebuffer, 21, "record in %d L", i + 1);
port = jack_port_register(m_client,
namebuffer,
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsInput,
0);
if (!port)
return false;
m_inputPorts.push_back(port);
snprintf(namebuffer, 21, "record in %d R", i + 1);
port = jack_port_register(m_client,
namebuffer,
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsInput,
0);
if (!port)
return false;
m_inputPorts.push_back(port);
}
while ((int)m_outputSubmasters.size() > pairs * 2) {
std::vector<jack_port_t *>::iterator itr = m_outputSubmasters.end();
--itr;
jack_port_unregister(m_client, *itr);
m_outputSubmasters.erase(itr);
}
return true;
}
void
JackDriver::setAudioPorts(bool faderOuts, bool submasterOuts)
{
if (!m_client)
return ;
Audit audit;
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::setAudioPorts(" << faderOuts << "," << submasterOuts << ")" << std::endl;
#endif
if (!m_client) {
std::cerr << "JackDriver::setAudioPorts(" << faderOuts << "," << submasterOuts << "): no client yet" << std::endl;
return ;
}
if (faderOuts) {
InstrumentId instrumentBase;
int audioInstruments;
int synthInstruments;
m_alsaDriver->getAudioInstrumentNumbers(instrumentBase, audioInstruments);
m_alsaDriver->getSoftSynthInstrumentNumbers(instrumentBase, synthInstruments);
if (!createFaderOutputs(audioInstruments, synthInstruments)) {
m_ok = false;
audit << "Failed to create fader outs!" << std::endl;
return ;
}
} else {
createFaderOutputs(0, 0);
}
if (submasterOuts) {
// one fewer than returned here, because the master has a buss object too
if (!createSubmasterOutputs
(m_alsaDriver->getMappedStudio()->getObjectCount
(MappedObject::AudioBuss) - 1)) {
m_ok = false;
audit << "Failed to create submaster outs!" << std::endl;
return ;
}
} else {
createSubmasterOutputs(0);
}
}
RealTime
JackDriver::getAudioPlayLatency() const
{
if (!m_client)
return RealTime::zeroTime;
jack_nframes_t latency =
jack_port_get_total_latency(m_client, m_outputMasters[0]);
return RealTime::frame2RealTime(latency, m_sampleRate);
}
RealTime
JackDriver::getAudioRecordLatency() const
{
if (!m_client)
return RealTime::zeroTime;
jack_nframes_t latency =
jack_port_get_total_latency(m_client, m_inputPorts[0]);
return RealTime::frame2RealTime(latency, m_sampleRate);
}
RealTime
JackDriver::getInstrumentPlayLatency(InstrumentId id) const
{
if (m_instrumentLatencies.find(id) == m_instrumentLatencies.end()) {
return RealTime::zeroTime;
} else {
return m_instrumentLatencies.find(id)->second;
}
}
RealTime
JackDriver::getMaximumPlayLatency() const
{
return m_maxInstrumentLatency;
}
int
JackDriver::jackProcessStatic(jack_nframes_t nframes, void *arg)
{
JackDriver *inst = static_cast<JackDriver*>(arg);
if (inst)
return inst->jackProcess(nframes);
else
return 0;
}
int
JackDriver::jackProcess(jack_nframes_t nframes)
{
if (!m_ok || !m_client) {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: not OK" << std::endl;
#endif
return 0;
}
if (!m_bussMixer) {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: no buss mixer" << std::endl;
#endif
return jackProcessEmpty(nframes);
}
if (m_alsaDriver->areClocksRunning()) {
m_alsaDriver->checkTimerSync(m_framesProcessed);
} else {
m_alsaDriver->checkTimerSync(0);
}
bool lowLatencyMode = m_alsaDriver->getLowLatencyMode();
bool clocksRunning = m_alsaDriver->areClocksRunning();
bool playing = m_alsaDriver->isPlaying();
bool asyncAudio = m_haveAsyncAudioEvent;
#ifdef DEBUG_JACK_PROCESS
Profiler profiler("jackProcess", true);
#else
#ifdef DEBUG_JACK_XRUN
Profiler profiler("jackProcess", false);
#endif
#endif
if (lowLatencyMode) {
if (clocksRunning) {
if (playing || asyncAudio) {
if (m_instrumentMixer->tryLock() == 0) {
m_instrumentMixer->kick(false);
m_instrumentMixer->releaseLock();
//#ifdef DEBUG_JACK_PROCESS
} else {
std::cerr << "JackDriver::jackProcess: no instrument mixer lock available" << std::endl;
//#endif
}
if (m_bussMixer->getBussCount() > 0) {
if (m_bussMixer->tryLock() == 0) {
m_bussMixer->kick(false, false);
m_bussMixer->releaseLock();
//#ifdef DEBUG_JACK_PROCESS
} else {
std::cerr << "JackDriver::jackProcess: no buss mixer lock available" << std::endl;
//#endif
}
}
}
}
}
if (jack_cpu_load(m_client) > 97.0) {
reportFailure(MappedEvent::FailureCPUOverload);
return jackProcessEmpty(nframes);
}
#ifdef DEBUG_JACK_PROCESS
Profiler profiler2("jackProcess post mix", true);
#else
#ifdef DEBUG_JACK_XRUN
Profiler profiler2("jackProcess post mix", false);
#endif
#endif
SequencerDataBlock *sdb = m_alsaDriver->getSequencerDataBlock();
jack_position_t position;
jack_transport_state_t state = JackTransportRolling;
bool doneRecord = false;
int ignoreCount = m_ignoreProcessTransportCount;
if (ignoreCount > 0)
--m_ignoreProcessTransportCount;
InstrumentId audioInstrumentBase;
int audioInstruments;
m_alsaDriver->getAudioInstrumentNumbers(audioInstrumentBase, audioInstruments);
if (m_jackTransportEnabled) {
state = jack_transport_query(m_client, &position);
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: JACK transport state is " << state << std::endl;
#endif
if (state == JackTransportStopped) {
if (playing && clocksRunning && !m_waiting) {
ExternalTransport *transport =
m_alsaDriver->getExternalTransportControl();
if (transport) {
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackProcess: JACK transport stopped externally at " << position.frame << std::endl;
#endif
m_waitingToken =
transport->transportJump
(ExternalTransport::TransportStopAtTime,
RealTime::frame2RealTime(position.frame,
position.frame_rate));
}
} else if (clocksRunning) {
if (!asyncAudio) {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: no interesting async events" << std::endl;
#endif
// do this before record monitor, otherwise we lose monitor out
jackProcessEmpty(nframes);
}
// for monitoring:
int rv = 0;
for (InstrumentId id = audioInstrumentBase;
id < audioInstrumentBase + audioInstruments; ++id) {
int irv = jackProcessRecord(id, nframes, 0, 0, clocksRunning);
if (irv != 0)
rv = irv;
}
doneRecord = true;
if (!asyncAudio) {
return rv;
}
} else {
return jackProcessEmpty(nframes);
}
} else if (state == JackTransportStarting) {
return jackProcessEmpty(nframes);
} else if (state != JackTransportRolling) {
std::cerr << "JackDriver::jackProcess: unexpected JACK transport state " << state << std::endl;
}
}
if (state == JackTransportRolling) { // also covers not-on-transport case
if (m_waiting) {
if (ignoreCount > 0) {
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackProcess: transport rolling, but we're ignoring it (count = " << ignoreCount << ")" << std::endl;
#endif
} else {
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackProcess: transport rolling, telling ALSA driver to go!" << std::endl;
#endif
m_alsaDriver->startClocksApproved();
m_waiting = false;
}
}
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess (rolling or not on JACK transport)" << std::endl;
#endif
if (!clocksRunning) {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: clocks stopped" << std::endl;
#endif
return jackProcessEmpty(nframes);
} else if (!playing) {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: not playing" << std::endl;
#endif
if (!asyncAudio) {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: no interesting async events" << std::endl;
#endif
// do this before record monitor, otherwise we lose monitor out
jackProcessEmpty(nframes);
}
// for monitoring:
int rv = 0;
for (InstrumentId id = audioInstrumentBase;
id < audioInstrumentBase + audioInstruments; ++id) {
int irv = jackProcessRecord(id, nframes, 0, 0, clocksRunning);
if (irv != 0)
rv = irv;
}
doneRecord = true;
if (!asyncAudio) {
return rv;
}
}
}
#ifdef DEBUG_JACK_PROCESS
Profiler profiler3("jackProcess post transport", true);
#else
#ifdef DEBUG_JACK_XRUN
Profiler profiler3("jackProcess post transport", false);
#endif
#endif
InstrumentId synthInstrumentBase;
int synthInstruments;
m_alsaDriver->getSoftSynthInstrumentNumbers(synthInstrumentBase, synthInstruments);
// We always have the master out
sample_t *master[2] = {
static_cast<sample_t *>
(jack_port_get_buffer(m_outputMasters[0], nframes)),
static_cast<sample_t *>
(jack_port_get_buffer(m_outputMasters[1], nframes))
};
memset(master[0], 0, nframes * sizeof(sample_t));
memset(master[1], 0, nframes * sizeof(sample_t));
// Reset monitor outs (if present) here prior to mixing
if (m_outputMonitors.size() > 0) {
sample_t *buffer = static_cast<sample_t *>
(jack_port_get_buffer(m_outputMonitors[0], nframes));
if (buffer)
memset(buffer, 0, nframes * sizeof(sample_t));
}
if (m_outputMonitors.size() > 1) {
sample_t *buffer = static_cast<sample_t *>
(jack_port_get_buffer(m_outputMonitors[1], nframes));
if (buffer)
memset(buffer, 0, nframes * sizeof(sample_t));
}
int bussCount = m_bussMixer->getBussCount();
// If we have any busses, then we just mix from them (but we still
// need to keep ourselves up to date by reading and monitoring the
// instruments). If we have no busses, mix direct from instruments.
for (int buss = 0; buss < bussCount; ++buss) {
sample_t *submaster[2] = { 0, 0 };
sample_t peak[2] = { 0.0, 0.0 };
if ((int)m_outputSubmasters.size() > buss * 2 + 1) {
submaster[0] = static_cast<sample_t *>
(jack_port_get_buffer(m_outputSubmasters[buss * 2], nframes));
submaster[1] = static_cast<sample_t *>
(jack_port_get_buffer(m_outputSubmasters[buss * 2 + 1], nframes));
}
if (!submaster[0])
submaster[0] = m_tempOutBuffer;
if (!submaster[1])
submaster[1] = m_tempOutBuffer;
for (int ch = 0; ch < 2; ++ch) {
RingBuffer<AudioBussMixer::sample_t> *rb =
m_bussMixer->getRingBuffer(buss, ch);
if (!rb || m_bussMixer->isBussDormant(buss)) {
if (rb)
rb->skip(nframes);
if (submaster[ch])
memset(submaster[ch], 0, nframes * sizeof(sample_t));
} else {
size_t actual = rb->read(submaster[ch], nframes);
if (actual < nframes) {
reportFailure(MappedEvent::FailureBussMixUnderrun);
}
for (size_t i = 0; i < nframes; ++i) {
sample_t sample = submaster[ch][i];
if (sample > peak[ch])
peak[ch] = sample;
master[ch][i] += sample;
}
}
}
if (sdb) {
LevelInfo info;
info.level = AudioLevel::multiplier_to_fader
(peak[0], 127, AudioLevel::LongFader);
info.levelRight = AudioLevel::multiplier_to_fader
(peak[1], 127, AudioLevel::LongFader);
sdb->setSubmasterLevel(buss, info);
}
for (InstrumentId id = audioInstrumentBase;
id < audioInstrumentBase + audioInstruments; ++id) {
if (buss + 1 == m_recordInputs[id].input) {
jackProcessRecord(id, nframes, submaster[0], submaster[1], clocksRunning);
}
}
}
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: have " << audioInstruments << " audio and " << synthInstruments << " synth instruments and " << bussCount << " busses" << std::endl;
#endif
bool allInstrumentsDormant = true;
static RealTime dormantTime = RealTime::zeroTime;
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
InstrumentId id;
if (i < audioInstruments)
id = audioInstrumentBase + i;
else
id = synthInstrumentBase + (i - audioInstruments);
if (m_instrumentMixer->isInstrumentEmpty(id))
continue;
sample_t *instrument[2] = { 0, 0 };
sample_t peak[2] = { 0.0, 0.0 };
if (int(m_outputInstruments.size()) > i * 2 + 1) {
instrument[0] = static_cast<sample_t *>
(jack_port_get_buffer(m_outputInstruments[i * 2], nframes));
instrument[1] = static_cast<sample_t *>
(jack_port_get_buffer(m_outputInstruments[i * 2 + 1], nframes));
}
if (!instrument[0])
instrument[0] = m_tempOutBuffer;
if (!instrument[1])
instrument[1] = m_tempOutBuffer;
for (int ch = 0; ch < 2; ++ch) {
// We always need to read from an instrument's ring buffer
// to keep the instrument moving along, as well as for
// monitoring. If the instrument is connected straight to
// the master, then we also need to mix from it. (We have
// that information cached courtesy of updateAudioData.)
bool directToMaster = false;
if (i < audioInstruments) {
directToMaster = (m_directMasterAudioInstruments & (1 << i));
} else {
directToMaster = (m_directMasterSynthInstruments &
(1 << (i - audioInstruments)));
}
#ifdef DEBUG_JACK_PROCESS
if (id == 1000 || id == 10000) {
std::cerr << "JackDriver::jackProcess: instrument id " << id << ", base " << audioInstrumentBase << ", direct masters " << m_directMasterAudioInstruments << ": " << directToMaster << std::endl;
}
#endif
RingBuffer<AudioInstrumentMixer::sample_t, 2> *rb =
m_instrumentMixer->getRingBuffer(id, ch);
if (!rb || m_instrumentMixer->isInstrumentDormant(id)) {
#ifdef DEBUG_JACK_PROCESS
if (id == 1000 || id == 10000) {
if (rb) {
std::cerr << "JackDriver::jackProcess: instrument " << id << " dormant" << std::endl;
} else {
std::cerr << "JackDriver::jackProcess: instrument " << id << " has no ring buffer for channel " << ch << std::endl;
}
}
#endif
if (rb)
rb->skip(nframes);
if (instrument[ch])
memset(instrument[ch], 0, nframes * sizeof(sample_t));
} else {
allInstrumentsDormant = false;
size_t actual = rb->read(instrument[ch], nframes);
#ifdef DEBUG_JACK_PROCESS
if (id == 1000) {
std::cerr << "JackDriver::jackProcess: read " << actual << " of " << nframes << " frames for instrument " << id << " channel " << ch << std::endl;
}
#endif
if (actual < nframes) {
std::cerr << "JackDriver::jackProcess: read " << actual << " of " << nframes << " frames for " << id << " ch " << ch << " (pl " << playing << ", cl " << clocksRunning << ", aa " << asyncAudio << ")" << std::endl;
reportFailure(MappedEvent::FailureMixUnderrun);
}
for (size_t f = 0; f < nframes; ++f) {
sample_t sample = instrument[ch][f];
if (sample > peak[ch])
peak[ch] = sample;
if (directToMaster)
master[ch][f] += sample;
}
}
// If the instrument is connected straight to master we
// also need to skip() on the buss mixer's reader for it,
// otherwise it'll block because the buss mixer isn't
// needing to read it.
if (rb && directToMaster) {
rb->skip(nframes, 1); // 1 is the buss mixer's reader (magic)
}
}
if (sdb) {
LevelInfo info;
info.level = AudioLevel::multiplier_to_fader
(peak[0], 127, AudioLevel::LongFader);
info.levelRight = AudioLevel::multiplier_to_fader
(peak[1], 127, AudioLevel::LongFader);
sdb->setInstrumentLevel(id, info);
}
}
if (asyncAudio) {
if (!allInstrumentsDormant) {
dormantTime = RealTime::zeroTime;
} else {
dormantTime = dormantTime +
RealTime::frame2RealTime(m_bufferSize, m_sampleRate);
if (dormantTime > RealTime(10, 0)) {
std::cerr << "JackDriver: dormantTime = " << dormantTime << ", resetting m_haveAsyncAudioEvent" << std::endl;
m_haveAsyncAudioEvent = false;
}
}
}
// Get master fader levels. There's no pan on the master.
float gain = AudioLevel::dB_to_multiplier(m_masterLevel);
float masterPeak[2] = { 0.0, 0.0 };
for (int ch = 0; ch < 2; ++ch) {
for (size_t i = 0; i < nframes; ++i) {
sample_t sample = master[ch][i] * gain;
if (sample > masterPeak[ch])
masterPeak[ch] = sample;
master[ch][i] = sample;
}
}
if (sdb) {
LevelInfo info;
info.level = AudioLevel::multiplier_to_fader
(masterPeak[0], 127, AudioLevel::LongFader);
info.levelRight = AudioLevel::multiplier_to_fader
(masterPeak[1], 127, AudioLevel::LongFader);
sdb->setMasterLevel(info);
}
for (InstrumentId id = audioInstrumentBase;
id < audioInstrumentBase + audioInstruments; ++id) {
if (m_recordInputs[id].input == 0) {
jackProcessRecord(id, nframes, master[0], master[1], clocksRunning);
} else if (m_recordInputs[id].input < 1000) { // buss, already done
// nothing
} else if (!doneRecord) {
jackProcessRecord(id, nframes, 0, 0, clocksRunning);
}
}
if (playing) {
if (!lowLatencyMode) {
if (m_bussMixer->getBussCount() == 0) {
m_instrumentMixer->signal();
} else {
m_bussMixer->signal();
}
}
}
m_framesProcessed += nframes;
#if (defined(DEBUG_JACK_DRIVER) || defined(DEBUG_JACK_PROCESS) || defined(DEBUG_JACK_TRANSPORT))
framesThisPlay += nframes; //!!!
#endif
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: " << nframes << " frames, " << framesThisPlay << " this play, " << m_framesProcessed << " total" << std::endl;
#endif
return 0;
}
int
JackDriver::jackProcessEmpty(jack_nframes_t nframes)
{
sample_t *buffer;
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcessEmpty" << std::endl;
#endif
buffer = static_cast<sample_t *>
(jack_port_get_buffer(m_outputMasters[0], nframes));
if (buffer)
memset(buffer, 0, nframes * sizeof(sample_t));
buffer = static_cast<sample_t *>
(jack_port_get_buffer(m_outputMasters[1], nframes));
if (buffer)
memset(buffer, 0, nframes * sizeof(sample_t));
buffer = static_cast<sample_t *>
(jack_port_get_buffer(m_outputMonitors[0], nframes));
if (buffer)
memset(buffer, 0, nframes * sizeof(sample_t));
buffer = static_cast<sample_t *>
(jack_port_get_buffer(m_outputMonitors[1], nframes));
if (buffer)
memset(buffer, 0, nframes * sizeof(sample_t));
for (unsigned int i = 0; i < m_outputSubmasters.size(); ++i) {
buffer = static_cast<sample_t *>
(jack_port_get_buffer(m_outputSubmasters[i], nframes));
if (buffer)
memset(buffer, 0, nframes * sizeof(sample_t));
}
for (unsigned int i = 0; i < m_outputInstruments.size(); ++i) {
buffer = static_cast<sample_t *>
(jack_port_get_buffer(m_outputInstruments[i], nframes));
if (buffer)
memset(buffer, 0, nframes * sizeof(sample_t));
}
m_framesProcessed += nframes;
#if (defined(DEBUG_JACK_DRIVER) || defined(DEBUG_JACK_PROCESS) || defined(DEBUG_JACK_TRANSPORT))
framesThisPlay += nframes;
#endif
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcess: " << nframes << " frames, " << framesThisPlay << " this play, " << m_framesProcessed << " total" << std::endl;
#endif
return 0;
}
int
JackDriver::jackProcessRecord(InstrumentId id,
jack_nframes_t nframes,
sample_t *sourceBufferLeft,
sample_t *sourceBufferRight,
bool clocksRunning)
{
#ifdef DEBUG_JACK_PROCESS
Profiler profiler("jackProcessRecord", true);
#else
#ifdef DEBUG_JACK_XRUN
Profiler profiler("jackProcessRecord", false);
#endif
#endif
SequencerDataBlock *sdb = m_alsaDriver->getSequencerDataBlock();
bool wroteSomething = false;
sample_t peakLeft = 0.0, peakRight = 0.0;
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcessRecord(" << id << "): clocksRunning " << clocksRunning << std::endl;
#endif
// Get input buffers
//
sample_t *inputBufferLeft = 0, *inputBufferRight = 0;
int recInput = m_recordInputs[id].input;
int channel = m_recordInputs[id].channel;
int channels = (channel == -1 ? 2 : 1);
if (channels == 2)
channel = 0;
float level = m_recordInputs[id].level;
if (sourceBufferLeft) {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcessRecord(" << id << "): buss input provided" << std::endl;
#endif
inputBufferLeft = sourceBufferLeft;
if (sourceBufferRight)
inputBufferRight = sourceBufferRight;
} else if (recInput < 1000) {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcessRecord(" << id << "): no known input" << std::endl;
#endif
return 0;
} else {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcessRecord(" << id << "): record input " << recInput << std::endl;
#endif
int input = recInput - 1000;
int port = input * channels + channel;
int portRight = input * channels + 1;
if (port < int(m_inputPorts.size())) {
inputBufferLeft = static_cast<sample_t*>
(jack_port_get_buffer(m_inputPorts[port], nframes));
}
if (channels == 2 && portRight < int(m_inputPorts.size())) {
inputBufferRight = static_cast<sample_t*>
(jack_port_get_buffer(m_inputPorts[portRight], nframes));
}
}
float gain = AudioLevel::dB_to_multiplier(level);
if (m_alsaDriver->getRecordStatus() == RECORD_ON &&
clocksRunning &&
m_fileWriter->haveRecordFileOpen(id)) {
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcessRecord(" << id << "): recording" << std::endl;
#endif
memset(m_tempOutBuffer, 0, nframes * sizeof(sample_t));
if (inputBufferLeft) {
for (size_t i = 0; i < nframes; ++i) {
sample_t sample = inputBufferLeft[i] * gain;
if (sample > peakLeft)
peakLeft = sample;
m_tempOutBuffer[i] = sample;
}
if (m_outputMonitors.size() > 0) {
sample_t *buf =
static_cast<sample_t *>
(jack_port_get_buffer(m_outputMonitors[0], nframes));
if (buf) {
for (size_t i = 0; i < nframes; ++i) {
buf[i] += m_tempOutBuffer[i];
}
}
}
m_fileWriter->write(id, m_tempOutBuffer, 0, nframes);
}
if (channels == 2) {
if (inputBufferRight) {
for (size_t i = 0; i < nframes; ++i) {
sample_t sample = inputBufferRight[i] * gain;
if (sample > peakRight)
peakRight = sample;
m_tempOutBuffer[i] = sample;
}
if (m_outputMonitors.size() > 1) {
sample_t *buf =
static_cast<sample_t *>
(jack_port_get_buffer(m_outputMonitors[1], nframes));
if (buf) {
for (size_t i = 0; i < nframes; ++i) {
buf[i] += m_tempOutBuffer[i];
}
}
}
}
m_fileWriter->write(id, m_tempOutBuffer, 1, nframes);
}
wroteSomething = true;
} else {
// want peak levels and monitors anyway, even if not recording
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::jackProcessRecord(" << id << "): monitoring only" << std::endl;
#endif
if (inputBufferLeft) {
sample_t *buf = 0;
if (m_outputMonitors.size() > 0) {
buf = static_cast<sample_t *>
(jack_port_get_buffer(m_outputMonitors[0], nframes));
}
for (size_t i = 0; i < nframes; ++i) {
sample_t sample = inputBufferLeft[i] * gain;
if (sample > peakLeft)
peakLeft = sample;
if (buf)
buf[i] = sample;
}
if (channels == 2 && inputBufferRight) {
buf = 0;
if (m_outputMonitors.size() > 1) {
buf = static_cast<sample_t *>
(jack_port_get_buffer(m_outputMonitors[1], nframes));
}
for (size_t i = 0; i < nframes; ++i) {
sample_t sample = inputBufferRight[i] * gain;
if (sample > peakRight)
peakRight = sample;
if (buf)
buf[i] = sample;
}
}
}
}
if (channels < 2)
peakRight = peakLeft;
if (sdb) {
LevelInfo info;
info.level = AudioLevel::multiplier_to_fader
(peakLeft, 127, AudioLevel::LongFader);
info.levelRight = AudioLevel::multiplier_to_fader
(peakRight, 127, AudioLevel::LongFader);
sdb->setInstrumentRecordLevel(id, info);
}
if (wroteSomething) {
m_fileWriter->signal();
}
return 0;
}
int
JackDriver::jackSyncCallback(jack_transport_state_t state,
jack_position_t *position,
void *arg)
{
JackDriver *inst = (JackDriver *)arg;
if (!inst)
return true; // or rather, return "huh?"
inst->m_alsaDriver->checkTimerSync(0); // reset, as not processing
if (!inst->m_jackTransportEnabled)
return true; // ignore
ExternalTransport *transport =
inst->m_alsaDriver->getExternalTransportControl();
if (!transport)
return true;
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: state " << state << " [" << (state == 0 ? "stopped" : state == 1 ? "rolling" : state == 2 ? "looping" : state == 3 ? "starting" : "unknown") << "], frame " << position->frame << ", waiting " << inst->m_waiting << ", playing " << inst->m_alsaDriver->isPlaying() << std::endl;
std::cerr << "JackDriver::jackSyncCallback: m_waitingState " << inst->m_waitingState << ", unique_1 " << position->unique_1 << ", unique_2 " << position->unique_2 << std::endl;
std::cerr << "JackDriver::jackSyncCallback: rate " << position->frame_rate << ", bar " << position->bar << ", beat " << position->beat << ", tick " << position->tick << ", bpm " << position->beats_per_minute << std::endl;
#endif
ExternalTransport::TransportRequest request =
ExternalTransport::TransportNoChange;
if (inst->m_alsaDriver->isPlaying()) {
if (state == JackTransportStarting) {
request = ExternalTransport::TransportJumpToTime;
} else if (state == JackTransportStopped) {
request = ExternalTransport::TransportStop;
}
} else {
if (state == JackTransportStarting) {
request = ExternalTransport::TransportStartAtTime;
} else if (state == JackTransportStopped) {
request = ExternalTransport::TransportNoChange;
}
}
if (!inst->m_waiting || inst->m_waitingState != state) {
if (request == ExternalTransport::TransportJumpToTime ||
request == ExternalTransport::TransportStartAtTime) {
RealTime rt = RealTime::frame2RealTime(position->frame,
position->frame_rate);
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: Requesting jump to " << rt << std::endl;
#endif
inst->m_waitingToken = transport->transportJump(request, rt);
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: My token is " << inst->m_waitingToken << std::endl;
#endif
} else if (request == ExternalTransport::TransportStop) {
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: Requesting state change to " << request << std::endl;
#endif
inst->m_waitingToken = transport->transportChange(request);
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: My token is " << inst->m_waitingToken << std::endl;
#endif
} else if (request == ExternalTransport::TransportNoChange) {
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: Requesting no state change!" << std::endl;
#endif
inst->m_waitingToken = transport->transportChange(request);
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: My token is " << inst->m_waitingToken << std::endl;
#endif
}
inst->m_waiting = true;
inst->m_waitingState = state;
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: Setting waiting to " << inst->m_waiting << " and waiting state to " << inst->m_waitingState << " (request was " << request << ")" << std::endl;
#endif
return 0;
} else {
if (transport->isTransportSyncComplete(inst->m_waitingToken)) {
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: Sync complete" << std::endl;
#endif
return 1;
} else {
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::jackSyncCallback: Sync not complete" << std::endl;
#endif
return 0;
}
}
}
bool
JackDriver::relocateTransportInternal(bool alsoStart)
{
if (!m_client)
return true;
#ifdef DEBUG_JACK_TRANSPORT
const char *fn = (alsoStart ?
"JackDriver::startTransport" :
"JackDriver::relocateTransport");
#endif
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << fn << std::endl;
#else
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::relocateTransportInternal" << std::endl;
#endif
#endif
// m_waiting is true if we are waiting for the JACK transport
// to finish a change of state.
if (m_jackTransportEnabled) {
// If on the transport, we never return true here -- instead
// the JACK process calls startClocksApproved() to signal to
// the ALSA driver that it's time to go. But we do use this
// to manage our JACK transport state requests.
// Where did this request come from? Are we just responding
// to an external sync?
ExternalTransport *transport =
m_alsaDriver->getExternalTransportControl();
if (transport) {
if (transport->isTransportSyncComplete(m_waitingToken)) {
// Nope, this came from Rosegarden
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << fn << ": asking JACK transport to start, setting wait state" << std::endl;
#endif
m_waiting = true;
m_waitingState = JackTransportStarting;
long frame = RealTime::realTime2Frame
(m_alsaDriver->getSequencerTime(), m_sampleRate);
if (frame < 0) {
// JACK Transport doesn't support preroll and
// can't set transport position to before zero
// (frame count is unsigned), so there's no very
// satisfactory fix for what to do for count-in
// bars. Let's just start at zero instead.
jack_transport_locate(m_client, 0);
} else {
jack_transport_locate(m_client, frame);
}
if (alsoStart) {
jack_transport_start(m_client);
m_ignoreProcessTransportCount = 1;
} else {
m_ignoreProcessTransportCount = 2;
}
} else {
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << fn << ": waiting already" << std::endl;
#endif
}
}
return false;
}
#if (defined(DEBUG_JACK_DRIVER) || defined(DEBUG_JACK_PROCESS) || defined(DEBUG_JACK_TRANSPORT))
framesThisPlay = 0; //!!!
struct timeval tv;
(void)gettimeofday(&tv, 0);
startTime = RealTime(tv.tv_sec, tv.tv_usec * 1000); //!!!
#endif
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << fn << ": not on JACK transport, accepting right away" << std::endl;
#endif
return true;
}
bool
JackDriver::startTransport()
{
return relocateTransportInternal(true);
}
bool
JackDriver::relocateTransport()
{
return relocateTransportInternal(false);
}
void
JackDriver::stopTransport()
{
if (!m_client)
return ;
std::cerr << "JackDriver::stopTransport: resetting m_haveAsyncAudioEvent" << std::endl;
m_haveAsyncAudioEvent = false;
#ifdef DEBUG_JACK_TRANSPORT
struct timeval tv;
(void)gettimeofday(&tv, 0);
RealTime endTime = RealTime(tv.tv_sec, tv.tv_usec * 1000); //!!!
std::cerr << "\nJackDriver::stop: frames this play: " << framesThisPlay << ", elapsed " << (endTime - startTime) << std::endl;
#endif
if (m_jackTransportEnabled) {
// Where did this request come from? Is this a result of our
// sync to a transport that has in fact already stopped?
ExternalTransport *transport =
m_alsaDriver->getExternalTransportControl();
if (transport) {
if (transport->isTransportSyncComplete(m_waitingToken)) {
// No, we have no outstanding external requests; this
// must have genuinely been requested from within
// Rosegarden, so:
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::stop: internal request, asking JACK transport to stop" << std::endl;
#endif
jack_transport_stop(m_client);
} else {
// Nothing to do
#ifdef DEBUG_JACK_TRANSPORT
std::cerr << "JackDriver::stop: external request, JACK transport is already stopped" << std::endl;
#endif
}
}
}
if (m_instrumentMixer)
m_instrumentMixer->resetAllPlugins(true); // discard events too
}
// Pick up any change of buffer size
//
int
JackDriver::jackBufferSize(jack_nframes_t nframes, void *arg)
{
JackDriver *inst = static_cast<JackDriver*>(arg);
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::jackBufferSize - buffer size changed to "
<< nframes << std::endl;
#endif
inst->m_bufferSize = nframes;
// Recreate our temporary mix buffers to the new size
//
//!!! need buffer size change callbacks on plugins (so long as they
// have internal buffers) and the mix manager, with locks acquired
// appropriately
delete [] inst->m_tempOutBuffer;
inst->m_tempOutBuffer = new sample_t[inst->m_bufferSize];
return 0;
}
// Sample rate change
//
int
JackDriver::jackSampleRate(jack_nframes_t nframes, void *arg)
{
JackDriver *inst = static_cast<JackDriver*>(arg);
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::jackSampleRate - sample rate changed to "
<< nframes << std::endl;
#endif
inst->m_sampleRate = nframes;
return 0;
}
void
JackDriver::jackShutdown(void *arg)
{
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::jackShutdown() - callback received - "
<< "informing GUI" << std::endl;
#endif
#ifdef DEBUG_JACK_XRUN
std::cerr << "JackDriver::jackShutdown" << std::endl;
Profiles::getInstance()->dump();
#endif
JackDriver *inst = static_cast<JackDriver*>(arg);
inst->m_ok = false;
inst->m_kickedOutAt = time(0);
inst->reportFailure(MappedEvent::FailureJackDied);
}
int
JackDriver::jackXRun(void *arg)
{
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::jackXRun" << std::endl;
#endif
#ifdef DEBUG_JACK_XRUN
std::cerr << "JackDriver::jackXRun" << std::endl;
Profiles::getInstance()->dump();
#endif
// Report to GUI
//
JackDriver *inst = static_cast<JackDriver*>(arg);
inst->reportFailure(MappedEvent::FailureXRuns);
return 0;
}
void
JackDriver::restoreIfRestorable()
{
if (m_kickedOutAt == 0)
return ;
if (m_client) {
jack_client_close(m_client);
std::cerr << "closed client" << std::endl;
m_client = 0;
}
time_t now = time(0);
if (now < m_kickedOutAt || now >= m_kickedOutAt + 3) {
if (m_instrumentMixer)
m_instrumentMixer->resetAllPlugins(true);
std::cerr << "reset plugins" << std::endl;
initialise(true);
if (m_ok) {
reportFailure(MappedEvent::FailureJackRestart);
} else {
reportFailure(MappedEvent::FailureJackRestartFailed);
}
m_kickedOutAt = 0;
}
}
void
JackDriver::prepareAudio()
{
if (!m_instrumentMixer)
return ;
// This is used when restarting clocks after repositioning, but
// when not actually playing (yet). We need to do things like
// regenerating the processing buffers here. prebufferAudio()
// also does all of this, but rather more besides.
m_instrumentMixer->allocateBuffers();
m_instrumentMixer->resetAllPlugins(false);
}
void
JackDriver::prebufferAudio()
{
if (!m_instrumentMixer)
return ;
// We want this to happen when repositioning during playback, and
// stopTransport no longer happens then, so we call it from here.
// NB. Don't want to discard events here as this is called after
// pushing events to the soft synth queues at startup
m_instrumentMixer->resetAllPlugins(false);
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::prebufferAudio: sequencer time is "
<< m_alsaDriver->getSequencerTime() << std::endl;
#endif
RealTime sliceStart = getNextSliceStart(m_alsaDriver->getSequencerTime());
m_fileReader->fillBuffers(sliceStart);
if (m_bussMixer->getBussCount() > 0) {
m_bussMixer->fillBuffers(sliceStart); // also calls on m_instrumentMixer
} else {
m_instrumentMixer->fillBuffers(sliceStart);
}
}
void
JackDriver::kickAudio()
{
#ifdef DEBUG_JACK_PROCESS
std::cerr << "JackDriver::kickAudio" << std::endl;
#endif
if (m_fileReader)
m_fileReader->kick();
if (m_instrumentMixer)
m_instrumentMixer->kick();
if (m_bussMixer)
m_bussMixer->kick();
if (m_fileWriter)
m_fileWriter->kick();
}
void
JackDriver::updateAudioData()
{
if (!m_ok || !m_client)
return ;
#ifdef DEBUG_JACK_DRIVER
// std::cerr << "JackDriver::updateAudioData starting" << std::endl;
#endif
MappedAudioBuss *mbuss =
m_alsaDriver->getMappedStudio()->getAudioBuss(0);
if (mbuss) {
float level = 0.0;
(void)mbuss->getProperty(MappedAudioBuss::Level, level);
m_masterLevel = level;
}
unsigned long directMasterAudioInstruments = 0L;
unsigned long directMasterSynthInstruments = 0L;
InstrumentId audioInstrumentBase;
int audioInstruments;
m_alsaDriver->getAudioInstrumentNumbers(audioInstrumentBase, audioInstruments);
InstrumentId synthInstrumentBase;
int synthInstruments;
m_alsaDriver->getSoftSynthInstrumentNumbers(synthInstrumentBase, synthInstruments);
RealTime jackLatency = getAudioPlayLatency();
RealTime maxLatency = RealTime::zeroTime;
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
InstrumentId id;
if (i < audioInstruments)
id = audioInstrumentBase + i;
else
id = synthInstrumentBase + (i - audioInstruments);
MappedAudioFader *fader = m_alsaDriver->getMappedStudio()->getAudioFader(id);
if (!fader)
continue;
float f = 2;
(void)fader->getProperty(MappedAudioFader::Channels, f);
int channels = (int)f;
int inputChannel = -1;
if (channels == 1) {
float f = 0;
(void)fader->getProperty(MappedAudioFader::InputChannel, f);
inputChannel = (int)f;
}
float level = 0.0;
(void)fader->getProperty(MappedAudioFader::FaderRecordLevel, level);
// Like in base/Instrument.h, we use numbers < 1000 to
// mean buss numbers and >= 1000 to mean record ins
// when recording the record input number.
MappedObjectValueList connections = fader->getConnections
(MappedConnectableObject::In);
int input = 1000;
if (connections.empty()) {
std::cerr << "No connections in for record instrument "
<< (id) << " (mapped id " << fader->getId() << ")" << std::endl;
// oh dear.
input = 1000;
} else if (*connections.begin() == mbuss->getId()) {
input = 0;
} else {
MappedObject *obj = m_alsaDriver->getMappedStudio()->
getObjectById(MappedObjectId(*connections.begin()));
if (!obj) {
std::cerr << "No such object as " << *connections.begin() << std::endl;
input = 1000;
} else if (obj->getType() == MappedObject::AudioBuss) {
input = (int)((MappedAudioBuss *)obj)->getBussId();
} else if (obj->getType() == MappedObject::AudioInput) {
input = (int)((MappedAudioInput *)obj)->getInputNumber()
+ 1000;
} else {
std::cerr << "Object " << *connections.begin() << " is not buss or input" << std::endl;
input = 1000;
}
}
if (m_recordInputs[id].input != input) {
std::cerr << "Changing record input for instrument "
<< id << " to " << input << std::endl;
}
m_recordInputs[id] = RecordInputDesc(input, inputChannel, level);
size_t pluginLatency = 0;
bool empty = m_instrumentMixer->isInstrumentEmpty(id);
if (!empty) {
pluginLatency = m_instrumentMixer->getPluginLatency(id);
}
// If we find the object is connected to no output, or to buss
// number 0 (the master), then we set the bit appropriately.
connections = fader->getConnections(MappedConnectableObject::Out);
if (connections.empty() || (*connections.begin() == mbuss->getId())) {
if (i < audioInstruments) {
directMasterAudioInstruments |= (1 << i);
} else {
directMasterSynthInstruments |= (1 << (i - audioInstruments));
}
} else if (!empty) {
pluginLatency +=
m_instrumentMixer->getPluginLatency((unsigned int) * connections.begin());
}
if (empty) {
m_instrumentLatencies[id] = RealTime::zeroTime;
} else {
m_instrumentLatencies[id] = jackLatency +
RealTime::frame2RealTime(pluginLatency, m_sampleRate);
if (m_instrumentLatencies[id] > maxLatency) {
maxLatency = m_instrumentLatencies[id];
}
}
}
m_maxInstrumentLatency = maxLatency;
m_directMasterAudioInstruments = directMasterAudioInstruments;
m_directMasterSynthInstruments = directMasterSynthInstruments;
m_maxInstrumentLatency = maxLatency;
int inputs = m_alsaDriver->getMappedStudio()->
getObjectCount(MappedObject::AudioInput);
if (m_client) {
// this will return with no work if the inputs are already correct:
createRecordInputs(inputs);
}
m_bussMixer->updateInstrumentConnections();
m_instrumentMixer->updateInstrumentMuteStates();
if (m_bussMixer->getBussCount() == 0 || m_alsaDriver->getLowLatencyMode()) {
if (m_bussMixer->running()) {
m_bussMixer->terminate();
}
} else {
if (!m_bussMixer->running()) {
m_bussMixer->run();
}
}
if (m_alsaDriver->getLowLatencyMode()) {
if (m_instrumentMixer->running()) {
m_instrumentMixer->terminate();
}
} else {
if (!m_instrumentMixer->running()) {
m_instrumentMixer->run();
}
}
#ifdef DEBUG_JACK_DRIVER
// std::cerr << "JackDriver::updateAudioData exiting" << std::endl;
#endif
}
void
JackDriver::setAudioBussLevels(int bussNo, float dB, float pan)
{
if (m_bussMixer) {
m_bussMixer->setBussLevels(bussNo, dB, pan);
}
}
void
JackDriver::setAudioInstrumentLevels(InstrumentId instrument, float dB, float pan)
{
if (m_instrumentMixer) {
m_instrumentMixer->setInstrumentLevels(instrument, dB, pan);
}
}
RealTime
JackDriver::getNextSliceStart(const RealTime &now) const
{
jack_nframes_t frame;
bool neg = false;
if (now < RealTime::zeroTime) {
neg = true;
frame = RealTime::realTime2Frame(RealTime::zeroTime - now, m_sampleRate);
} else {
frame = RealTime::realTime2Frame(now, m_sampleRate);
}
jack_nframes_t rounded = frame;
rounded /= m_bufferSize;
rounded *= m_bufferSize;
RealTime roundrt;
if (rounded == frame)
roundrt = RealTime::frame2RealTime(rounded, m_sampleRate);
else if (neg)
roundrt = RealTime::frame2RealTime(rounded - m_bufferSize, m_sampleRate);
else
roundrt = RealTime::frame2RealTime(rounded + m_bufferSize, m_sampleRate);
if (neg)
roundrt = RealTime::zeroTime - roundrt;
return roundrt;
}
int
JackDriver::getAudioQueueLocks()
{
// We have to lock the mixers first, because the mixers can try to
// lock the disk manager from within a locked section -- so if we
// locked the disk manager first we would risk deadlock when
// trying to acquire the instrument mixer lock
int rv = 0;
if (m_bussMixer) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::getAudioQueueLocks: trying to lock buss mixer" << std::endl;
#endif
rv = m_bussMixer->getLock();
if (rv)
return rv;
}
if (m_instrumentMixer) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::getAudioQueueLocks: ok, now trying for instrument mixer" << std::endl;
#endif
rv = m_instrumentMixer->getLock();
if (rv)
return rv;
}
if (m_fileReader) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::getAudioQueueLocks: ok, now trying for disk reader" << std::endl;
#endif
rv = m_fileReader->getLock();
if (rv)
return rv;
}
if (m_fileWriter) {
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::getAudioQueueLocks: ok, now trying for disk writer" << std::endl;
#endif
rv = m_fileWriter->getLock();
}
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::getAudioQueueLocks: ok" << std::endl;
#endif
return rv;
}
int
JackDriver::tryAudioQueueLocks()
{
int rv = 0;
if (m_bussMixer) {
rv = m_bussMixer->tryLock();
if (rv)
return rv;
}
if (m_instrumentMixer) {
rv = m_instrumentMixer->tryLock();
if (rv) {
if (m_bussMixer) {
m_bussMixer->releaseLock();
}
}
}
if (m_fileReader) {
rv = m_fileReader->tryLock();
if (rv) {
if (m_instrumentMixer) {
m_instrumentMixer->releaseLock();
}
if (m_bussMixer) {
m_bussMixer->releaseLock();
}
}
}
if (m_fileWriter) {
rv = m_fileWriter->tryLock();
if (rv) {
if (m_fileReader) {
m_fileReader->releaseLock();
}
if (m_instrumentMixer) {
m_instrumentMixer->releaseLock();
}
if (m_bussMixer) {
m_bussMixer->releaseLock();
}
}
}
return rv;
}
int
JackDriver::releaseAudioQueueLocks()
{
int rv = 0;
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::releaseAudioQueueLocks" << std::endl;
#endif
if (m_fileWriter)
rv = m_fileWriter->releaseLock();
if (m_fileReader)
rv = m_fileReader->releaseLock();
if (m_instrumentMixer)
rv = m_instrumentMixer->releaseLock();
if (m_bussMixer)
rv = m_bussMixer->releaseLock();
return rv;
}
void
JackDriver::setPluginInstance(InstrumentId id, TQString identifier,
int position)
{
if (m_instrumentMixer) {
m_instrumentMixer->setPlugin(id, position, identifier);
}
if (!m_alsaDriver->isPlaying()) {
prebufferAudio(); // to ensure the plugin's ringbuffers are generated
}
}
void
JackDriver::removePluginInstance(InstrumentId id, int position)
{
if (m_instrumentMixer)
m_instrumentMixer->removePlugin(id, position);
}
void
JackDriver::removePluginInstances()
{
if (m_instrumentMixer)
m_instrumentMixer->removeAllPlugins();
}
void
JackDriver::setPluginInstancePortValue(InstrumentId id, int position,
unsigned long portNumber,
float value)
{
if (m_instrumentMixer)
m_instrumentMixer->setPluginPortValue(id, position, portNumber, value);
}
float
JackDriver::getPluginInstancePortValue(InstrumentId id, int position,
unsigned long portNumber)
{
if (m_instrumentMixer)
return m_instrumentMixer->getPluginPortValue(id, position, portNumber);
return 0;
}
void
JackDriver::setPluginInstanceBypass(InstrumentId id, int position, bool value)
{
if (m_instrumentMixer)
m_instrumentMixer->setPluginBypass(id, position, value);
}
TQStringList
JackDriver::getPluginInstancePrograms(InstrumentId id, int position)
{
if (m_instrumentMixer)
return m_instrumentMixer->getPluginPrograms(id, position);
return TQStringList();
}
TQString
JackDriver::getPluginInstanceProgram(InstrumentId id, int position)
{
if (m_instrumentMixer)
return m_instrumentMixer->getPluginProgram(id, position);
return TQString();
}
TQString
JackDriver::getPluginInstanceProgram(InstrumentId id, int position,
int bank, int program)
{
if (m_instrumentMixer)
return m_instrumentMixer->getPluginProgram(id, position, bank, program);
return TQString();
}
unsigned long
JackDriver::getPluginInstanceProgram(InstrumentId id, int position, TQString name)
{
if (m_instrumentMixer)
return m_instrumentMixer->getPluginProgram(id, position, name);
return 0;
}
void
JackDriver::setPluginInstanceProgram(InstrumentId id, int position, TQString program)
{
if (m_instrumentMixer)
m_instrumentMixer->setPluginProgram(id, position, program);
}
TQString
JackDriver::configurePlugin(InstrumentId id, int position, TQString key, TQString value)
{
if (m_instrumentMixer)
return m_instrumentMixer->configurePlugin(id, position, key, value);
return TQString();
}
RunnablePluginInstance *
JackDriver::getSynthPlugin(InstrumentId id)
{
if (m_instrumentMixer)
return m_instrumentMixer->getSynthPlugin(id);
else
return 0;
}
void
JackDriver::clearSynthPluginEvents()
{
if (!m_instrumentMixer) return;
#ifdef DEBUG_JACK_DRIVER
std::cerr << "JackDriver::clearSynthPluginEvents" << std::endl;
#endif
m_instrumentMixer->discardPluginEvents();
}
bool
JackDriver::openRecordFile(InstrumentId id,
const std::string &filename)
{
if (m_fileWriter) {
if (!m_fileWriter->running()) {
m_fileWriter->run();
}
return m_fileWriter->openRecordFile(id, filename);
} else {
std::cerr << "JackDriver::openRecordFile: No file writer available!" << std::endl;
return false;
}
}
bool
JackDriver::closeRecordFile(InstrumentId id,
AudioFileId &returnedId)
{
if (m_fileWriter) {
return m_fileWriter->closeRecordFile(id, returnedId);
if (m_fileWriter->running() && !m_fileWriter->haveRecordFilesOpen()) {
m_fileWriter->terminate();
}
} else
return false;
}
void
JackDriver::reportFailure(MappedEvent::FailureCode code)
{
if (m_alsaDriver)
m_alsaDriver->reportFailure(code);
}
}
#endif // HAVE_LIBJACK
#endif // HAVE_ALSA