ladish/grauph/src/libs/engine/JackAudioDriver.cpp

374 lines
9.3 KiB
C++

/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
*
* Om 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.
*
* Om is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "JackAudioDriver.h"
#include "config.h"
#include "tuning.h"
#include <iostream>
#include <cstdlib>
#include "Om.h"
#include "OmApp.h"
#include "util.h"
#include "Event.h"
#include "QueuedEvent.h"
#include "EventSource.h"
#include "PostProcessor.h"
#include "util/Queue.h"
#include "Node.h"
#include "Patch.h"
#include "Port.h"
#include "PortInfo.h"
#include "MidiDriver.h"
#include "List.h"
#include "PortBase.h"
#ifdef HAVE_LASH
#include "LashDriver.h"
#endif
using std::cout; using std::cerr; using std::endl;
namespace Om {
//// JackAudioPort ////
JackAudioPort::JackAudioPort(JackAudioDriver* driver, PortBase<sample>* patch_port)
: DriverPort(),
ListNode<JackAudioPort*>(this),
m_driver(driver),
m_jack_port(NULL),
m_jack_buffer(NULL),
m_patch_port(patch_port)
{
assert(patch_port->tied_port() != NULL);
assert(patch_port->poly() == 1);
m_jack_port = jack_port_register(m_driver->jack_client(),
patch_port->path().c_str(), JACK_DEFAULT_AUDIO_TYPE,
(patch_port->port_info()->is_input()) ? JackPortIsInput : JackPortIsOutput,
0);
m_jack_buffer = new DriverBuffer<jack_sample_t>(driver->buffer_size());
patch_port->fixed_buffers(true);
}
JackAudioPort::~JackAudioPort()
{
jack_port_unregister(m_driver->jack_client(), m_jack_port);
}
void
JackAudioPort::add_to_driver()
{
m_driver->add_port(this);
}
void
JackAudioPort::remove_from_driver()
{
m_driver->remove_port(this);
}
void
JackAudioPort::prepare_buffer(jack_nframes_t nframes)
{
// Technically this doesn't need to be done every time for output ports
m_jack_buffer->set_data((jack_default_audio_sample_t*)
jack_port_get_buffer(m_jack_port, nframes));
assert(m_patch_port->tied_port() != NULL);
// FIXME: fixed_buffers switch on/off thing can be removed once this shit
// gets figured out and assertions can go away
m_patch_port->fixed_buffers(false);
m_patch_port->buffer(0)->join(m_jack_buffer);
m_patch_port->tied_port()->buffer(0)->join(m_jack_buffer);
m_patch_port->fixed_buffers(true);
assert(m_patch_port->buffer(0)->data() == m_patch_port->tied_port()->buffer(0)->data());
assert(m_patch_port->buffer(0)->data() == m_jack_buffer->data());
}
//// JackAudioDriver ////
JackAudioDriver::JackAudioDriver()
: m_client(NULL),
m_buffer_size(0),
m_sample_rate(0),
m_is_activated(false),
m_local_client(true),
m_root_patch(NULL),
m_start_of_current_cycle(0),
m_start_of_last_cycle(0)
{
m_client = jack_client_new("Om");
if (m_client == NULL) {
cerr << "[JackAudioDriver] Unable to connect to Jack. Exiting." << endl;
exit(EXIT_FAILURE);
}
jack_on_shutdown(m_client, shutdown_cb, this);
m_buffer_size = jack_get_buffer_size(m_client);
m_sample_rate = jack_get_sample_rate(m_client);
jack_set_sample_rate_callback(m_client, sample_rate_cb, this);
jack_set_buffer_size_callback(m_client, buffer_size_cb, this);
}
JackAudioDriver::JackAudioDriver(jack_client_t* jack_client)
: m_client(jack_client),
m_buffer_size(jack_get_buffer_size(jack_client)),
m_sample_rate(jack_get_sample_rate(jack_client)),
m_is_activated(false),
m_local_client(false),
m_start_of_current_cycle(0),
m_start_of_last_cycle(0)
{
jack_on_shutdown(m_client, shutdown_cb, this);
jack_set_sample_rate_callback(m_client, sample_rate_cb, this);
jack_set_buffer_size_callback(m_client, buffer_size_cb, this);
}
JackAudioDriver::~JackAudioDriver()
{
deactivate();
if (m_local_client)
jack_client_close(m_client);
}
void
JackAudioDriver::activate()
{
if (m_is_activated) {
cerr << "[JackAudioDriver] Jack driver already activated." << endl;
return;
}
jack_set_process_callback(m_client, process_cb, this);
m_is_activated = true;
if (jack_activate(m_client)) {
cerr << "[JackAudioDriver] Could not activate Jack client, aborting." << endl;
exit(EXIT_FAILURE);
} else {
cout << "[JackAudioDriver] Activated Jack client." << endl;
#ifdef HAVE_LASH
lash_driver->set_jack_client_name("Om"); // FIXME: unique name
#endif
}
}
void
JackAudioDriver::deactivate()
{
if (m_is_activated) {
// FIXME
reinterpret_cast<EventSource*>(om->osc_receiver())->stop();
jack_deactivate(m_client);
m_is_activated = false;
for (List<JackAudioPort*>::iterator i = m_ports.begin(); i != m_ports.end(); ++i)
jack_port_unregister(m_client, (*i)->jack_port());
m_ports.clear();
cout << "[JackAudioDriver] Deactivated Jack client." << endl;
om->post_processor()->stop();
}
}
/** Add a Jack port.
*
* Realtime safe, this is to be called at the beginning of a process cycle to
* insert (and actually begin using) a new port.
*
* See create_port() and remove_port().
*/
void
JackAudioDriver::add_port(JackAudioPort* port)
{
m_ports.push_back(port);
}
/** Remove a Jack port.
*
* Realtime safe. This is to be called at the beginning of a process cycle to
* remove the port from the lists read by the audio thread, so the port
* will no longer be used and can be removed afterwards.
*
* It is the callers responsibility to delete the returned port.
*/
JackAudioPort*
JackAudioDriver::remove_port(JackAudioPort* port)
{
for (List<JackAudioPort*>::iterator i = m_ports.begin(); i != m_ports.end(); ++i)
if ((*i) == port)
return m_ports.remove(i)->elem();
cerr << "[JackAudioDriver::remove_port] WARNING: Failed to find Jack port to remove!" << endl;
return NULL;
}
DriverPort*
JackAudioDriver::create_port(PortBase<sample>* patch_port)
{
if (patch_port->buffer_size() == m_buffer_size)
return new JackAudioPort(this, patch_port);
else
return NULL;
}
/** Process all the pending events for this cycle.
*
* Called from the realtime thread once every process cycle.
*/
void
JackAudioDriver::process_events(jack_nframes_t block_start, jack_nframes_t block_end)
{
Event* ev = NULL;
/* Limit the maximum number of queued events to process per cycle. This
* makes the process callback truly realtime-safe by preventing being
* choked by events coming in faster than they can be processed.
* FIXME: run the math on this and figure out a good value */
const int MAX_SLOW_EVENTS = m_buffer_size / 100;
int num_events_processed = 0;
int offset = 0;
// Process the "slow" events first, because it's possible some of the
// RT events depend on them
// FIXME
while ((ev = reinterpret_cast<EventSource*>(om->osc_receiver())
->pop_earliest_event_before(block_end)) != NULL) {
ev->execute(0); // QueuedEvents are not sample accurate
om->post_processor()->push(ev);
if (++num_events_processed > MAX_SLOW_EVENTS)
break;
}
while (!om->event_queue()->is_empty()
&& om->event_queue()->front()->time_stamp() < block_end) {
ev = om->event_queue()->pop();
offset = ev->time_stamp() - block_start;
if (offset < 0) offset = 0; // this can happen if we miss a cycle
ev->execute(offset);
om->post_processor()->push(ev);
++num_events_processed;
}
if (num_events_processed > 0)
om->post_processor()->signal();
}
/**** Jack Callbacks ****/
/** Jack process callback, drives entire audio thread.
*
* \callgraph
*/
int
JackAudioDriver::m_process_cb(jack_nframes_t nframes)
{
// FIXME: support nframes != buffer_size, even though that never damn well happens
assert(nframes == m_buffer_size);
// Note that jack can elect to not call this function for a cycle, if things aren't
// keeping up
m_start_of_current_cycle = jack_last_frame_time(m_client);
m_start_of_last_cycle = m_start_of_current_cycle - nframes;
assert(m_start_of_current_cycle - m_start_of_last_cycle == nframes);
m_transport_state = jack_transport_query(m_client, &m_position);
process_events(m_start_of_last_cycle, m_start_of_current_cycle);
om->midi_driver()->prepare_block(m_start_of_last_cycle, m_start_of_current_cycle);
// Set buffers of patch ports to Jack port buffers (zero-copy processing)
for (List<JackAudioPort*>::iterator i = m_ports.begin(); i != m_ports.end(); ++i)
(*i)->prepare_buffer(nframes);
// Run root patch
assert(m_root_patch != NULL);
m_root_patch->run(nframes);
return 0;
}
void
JackAudioDriver::m_shutdown_cb()
{
cout << "[JackAudioDriver] Jack shutdown. Exiting." << endl;
om->quit();
}
int
JackAudioDriver::m_sample_rate_cb(jack_nframes_t nframes)
{
if (m_is_activated) {
cerr << "[JackAudioDriver] Om does not support changing sample rate on the fly (yet). Aborting." << endl;
exit(EXIT_FAILURE);
} else {
m_sample_rate = nframes;
}
return 0;
}
int
JackAudioDriver::m_buffer_size_cb(jack_nframes_t nframes)
{
if (m_is_activated) {
cerr << "[JackAudioDriver] Om does not support chanding buffer size on the fly (yet). Aborting." << endl;
exit(EXIT_FAILURE);
} else {
m_buffer_size = nframes;
}
return 0;
}
} // namespace Om