diff options
Diffstat (limited to 'src/sound/JackDriver.cpp')
-rw-r--r-- | src/sound/JackDriver.cpp | 2480 |
1 files changed, 2480 insertions, 0 deletions
diff --git a/src/sound/JackDriver.cpp b/src/sound/JackDriver.cpp new file mode 100644 index 0000000..24eb6fe --- /dev/null +++ b/src/sound/JackDriver.cpp @@ -0,0 +1,2480 @@ + +// -*- c-basic-offset: 4 -*- + +/* + Rosegarden + A sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + 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, QString 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); +} + +QStringList +JackDriver::getPluginInstancePrograms(InstrumentId id, int position) +{ + if (m_instrumentMixer) + return m_instrumentMixer->getPluginPrograms(id, position); + return QStringList(); +} + +QString +JackDriver::getPluginInstanceProgram(InstrumentId id, int position) +{ + if (m_instrumentMixer) + return m_instrumentMixer->getPluginProgram(id, position); + return QString(); +} + +QString +JackDriver::getPluginInstanceProgram(InstrumentId id, int position, + int bank, int program) +{ + if (m_instrumentMixer) + return m_instrumentMixer->getPluginProgram(id, position, bank, program); + return QString(); +} + +unsigned long +JackDriver::getPluginInstanceProgram(InstrumentId id, int position, QString name) +{ + if (m_instrumentMixer) + return m_instrumentMixer->getPluginProgram(id, position, name); + return 0; +} + +void +JackDriver::setPluginInstanceProgram(InstrumentId id, int position, QString program) +{ + if (m_instrumentMixer) + m_instrumentMixer->setPluginProgram(id, position, program); +} + +QString +JackDriver::configurePlugin(InstrumentId id, int position, QString key, QString value) +{ + if (m_instrumentMixer) + return m_instrumentMixer->configurePlugin(id, position, key, value); + return QString(); +} + +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 |