From 566479a3ac4c8c6c9d190d6b4e8ecd104402180c Mon Sep 17 00:00:00 2001
From: David Robillard <d@drobilla.net>
Date: Tue, 5 Jan 2010 21:55:24 +0000
Subject: JackAudioDriver.[ch]pp -> JackDriver.[ch]pp

git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@2344 a436a847-0d15-0410-975c-d299462d15a1
---
 src/engine/JackAudioDriver.cpp | 498 -----------------------------------------
 src/engine/JackAudioDriver.hpp | 173 --------------
 src/engine/JackDriver.cpp      | 498 +++++++++++++++++++++++++++++++++++++++++
 src/engine/JackDriver.hpp      | 173 ++++++++++++++
 src/engine/ingen_jack.cpp      |   2 +-
 src/engine/wscript             |   2 +-
 6 files changed, 673 insertions(+), 673 deletions(-)
 delete mode 100644 src/engine/JackAudioDriver.cpp
 delete mode 100644 src/engine/JackAudioDriver.hpp
 create mode 100644 src/engine/JackDriver.cpp
 create mode 100644 src/engine/JackDriver.hpp

diff --git a/src/engine/JackAudioDriver.cpp b/src/engine/JackAudioDriver.cpp
deleted file mode 100644
index 44951f27..00000000
--- a/src/engine/JackAudioDriver.cpp
+++ /dev/null
@@ -1,498 +0,0 @@
-/* This file is part of Ingen.
- * Copyright (C) 2007-2009 Dave Robillard <http://drobilla.net>
- *
- * Ingen 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.
- *
- * Ingen 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.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <iostream>
-#include <cstdlib>
-#include <jack/midiport.h>
-#include "raul/List.hpp"
-#include "shared/LV2Features.hpp"
-#include "shared/LV2URIMap.hpp"
-#include "AudioBuffer.hpp"
-#include "DuplexPort.hpp"
-#include "Engine.hpp"
-#include "Event.hpp"
-#include "EventBuffer.hpp"
-#include "EventSource.hpp"
-#include "EventSource.hpp"
-#include "JackAudioDriver.hpp"
-#include "MessageContext.hpp"
-#include "PatchImpl.hpp"
-#include "PortImpl.hpp"
-#include "PostProcessor.hpp"
-#include "ProcessSlave.hpp"
-#include "QueuedEvent.hpp"
-#include "ThreadManager.hpp"
-#include "ingen-config.h"
-#include "tuning.hpp"
-#include "util.hpp"
-
-using namespace std;
-using namespace Raul;
-
-namespace Ingen {
-
-
-//// JackAudioPort ////
-
-JackAudioPort::JackAudioPort(JackAudioDriver* driver, DuplexPort* patch_port)
-	: DriverPort(patch_port)
-	, Raul::List<JackAudioPort*>::Node(this)
-	, _driver(driver)
-	, _jack_port(NULL)
-{
-	assert(patch_port->poly() == 1);
-
-	create();
-
-	patch_port->buffer(0)->clear();
-}
-
-
-JackAudioPort::~JackAudioPort()
-{
-	assert(_jack_port == NULL);
-}
-
-
-void
-JackAudioPort::create()
-{
-	_jack_port = jack_port_register(
-			_driver->jack_client(),
-			ingen_jack_port_name(_patch_port->path()).c_str(),
-			(_patch_port->type() == PortType::AUDIO)
-				? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE,
-			(_patch_port->is_input())
-				? JackPortIsInput : JackPortIsOutput,
-			0);
-
-	if (_jack_port == NULL) {
-		cerr << "[JackAudioPort] ERROR: Failed to register port " << _patch_port->path() << endl;
-		throw JackAudioDriver::PortRegistrationFailedException();
-	}
-}
-
-
-void
-JackAudioPort::destroy()
-{
-	assert(_jack_port);
-	if (jack_port_unregister(_driver->jack_client(), _jack_port))
-		cerr << "[JackMidiPort] ERROR: Unable to unregister port" << endl;
-	_jack_port = NULL;
-}
-
-
-void
-JackAudioPort::move(const Raul::Path& path)
-{
-	jack_port_set_name(_jack_port, ingen_jack_port_name(path).c_str());
-}
-
-
-void
-JackAudioPort::pre_process(ProcessContext& context)
-{
-	if (!is_input())
-		return;
-
-	const SampleCount nframes = context.nframes();
-
-	if (_patch_port->type() == PortType::AUDIO) {
-		jack_sample_t* jack_buf  = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes);
-		AudioBuffer*   patch_buf = (AudioBuffer*)_patch_port->buffer(0).get();
-
-		patch_buf->copy(jack_buf, 0, nframes - 1);
-
-	} else if (_patch_port->type() == PortType::EVENTS) {
-		void*          jack_buf  = jack_port_get_buffer(_jack_port, nframes);
-		EventBuffer*   patch_buf = (EventBuffer*)_patch_port->buffer(0).get();
-
-		const jack_nframes_t event_count = jack_midi_get_event_count(jack_buf);
-
-		patch_buf->prepare_write(context);
-
-		// Copy events from Jack port buffer into patch port buffer
-		for (jack_nframes_t i = 0; i < event_count; ++i) {
-			jack_midi_event_t ev;
-			jack_midi_event_get(&ev, jack_buf, i);
-
-			if (!patch_buf->append(ev.time, 0, _driver->_midi_event_type, ev.size, ev.buffer))
-				cerr << "WARNING: Failed to write MIDI to port buffer, event(s) lost!" << endl;
-		}
-	}
-}
-
-
-void
-JackAudioPort::post_process(ProcessContext& context)
-{
-	if (is_input())
-		return;
-
-	const SampleCount nframes = context.nframes();
-
-	if (_patch_port->type() == PortType::AUDIO) {
-		jack_sample_t* jack_buf  = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes);
-		AudioBuffer*   patch_buf = (AudioBuffer*)_patch_port->buffer(0).get();
-
-		memcpy(jack_buf, patch_buf->data(), nframes * sizeof(Sample));
-
-	} else if (_patch_port->type() == PortType::EVENTS) {
-		void*        jack_buf  = jack_port_get_buffer(_jack_port, context.nframes());
-		EventBuffer* patch_buf = (EventBuffer*)_patch_port->buffer(0).get();
-
-		patch_buf->prepare_read(context);
-		jack_midi_clear_buffer(jack_buf);
-
-		uint32_t frames    = 0;
-		uint32_t subframes = 0;
-		uint16_t type      = 0;
-		uint16_t size      = 0;
-		uint8_t* data      = NULL;
-
-		// Copy events from Jack port buffer into patch port buffer
-		for (patch_buf->rewind(); patch_buf->is_valid(); patch_buf->increment()) {
-			patch_buf->get_event(&frames, &subframes, &type, &size, &data);
-			jack_midi_event_write(jack_buf, frames, data, size);
-		}
-	}
-}
-
-
-//// JackAudioDriver ////
-
-JackAudioDriver::JackAudioDriver(Engine& engine)
-	: _engine(engine)
-	, _jack_thread(NULL)
-	, _sem(0)
-	, _flag(0)
-	, _client(NULL)
-	, _buffer_size(0)
-	, _sample_rate(0)
-	, _is_activated(false)
-	, _local_client(true)
-	, _process_context(engine)
-	, _root_patch(NULL)
-{
-	SharedPtr<Shared::LV2URIMap> map = PtrCast<Shared::LV2URIMap>(
-			_engine.world()->lv2_features->feature(LV2_URI_MAP_URI));
-	_midi_event_type = map->uri_to_id(NULL, "http://lv2plug.in/ns/ext/midi#MidiEvent");
-}
-
-
-JackAudioDriver::~JackAudioDriver()
-{
-	deactivate();
-
-	if (_local_client)
-		jack_client_close(_client);
-}
-
-
-bool
-JackAudioDriver::supports(Shared::PortType port_type, Shared::EventType event_type)
-{
-	return (port_type == PortType::AUDIO
-			|| (port_type == PortType::EVENTS && event_type == EventType::MIDI));
-}
-
-
-bool
-JackAudioDriver::attach(const std::string& server_name,
-                        const std::string& client_name,
-                        void*              jack_client)
-{
-	assert(!_client);
-	if (!jack_client) {
-		// Try supplied server name
-		if (server_name != "") {
-			_client = jack_client_open(client_name.c_str(),
-					JackServerName, NULL, server_name.c_str());
-			if (_client)
-				cerr << "[JackAudioDriver] Connected to JACK server '" <<
-					server_name << "'" << endl;
-		}
-
-		// Either server name not specified, or supplied server name does not exist
-		// Connect to default server
-		if (!_client) {
-			_client = jack_client_open(client_name.c_str(), JackNullOption, NULL);
-
-			if (_client)
-				cerr << "[JackAudioDriver] Connected to default JACK server." << endl;
-		}
-
-		// Still failed
-		if (!_client) {
-			cerr << "[JackAudioDriver] Unable to connect to Jack.  Exiting." << endl;
-			return false;
-		}
-	} else {
-		_client = (jack_client_t*)jack_client;
-	}
-
-	_local_client = (jack_client == NULL);
-
-	_buffer_size = jack_get_buffer_size(_client) * sizeof(Sample);
-	_sample_rate = jack_get_sample_rate(_client);
-
-	jack_on_shutdown(_client, shutdown_cb, this);
-
-	jack_set_thread_init_callback(_client, thread_init_cb, this);
-	jack_set_sample_rate_callback(_client, sample_rate_cb, this);
-	jack_set_buffer_size_callback(_client, buffer_size_cb, this);
-
-	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
-		(*i)->create();
-
-	return true;
-}
-
-
-void
-JackAudioDriver::activate()
-{
-	if (_is_activated) {
-		cerr << "[JackAudioDriver] Jack driver already activated." << endl;
-		return;
-	}
-
-	if (!_client)
-		attach(_engine.world()->conf->option("jack-server").get_string(),
-				_engine.world()->conf->option("jack-client").get_string(), NULL);
-
-	jack_set_process_callback(_client, process_cb, this);
-
-	_is_activated = true;
-
-	if (jack_activate(_client)) {
-		cerr << "[JackAudioDriver] Could not activate Jack client, aborting." << endl;
-		exit(EXIT_FAILURE);
-	} else {
-		cout << "[JackAudioDriver] Activated Jack client." << endl;
-	}
-}
-
-
-void
-JackAudioDriver::deactivate()
-{
-	if (_is_activated) {
-		_flag = 1;
-		_is_activated = false;
-		_sem.wait();
-
-		for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
-			(*i)->destroy();
-
-		jack_deactivate(_client);
-
-		if (_local_client) {
-			jack_client_close(_client);
-			_client = NULL;
-		}
-
-		_jack_thread->stop();
-		cout << "[JackAudioDriver] Deactivated Jack client." << endl;
-	}
-}
-
-
-/** 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(DriverPort* port)
-{
-	assert(ThreadManager::current_thread_id() == THREAD_PROCESS);
-	assert(dynamic_cast<JackAudioPort*>(port));
-	_ports.push_back((JackAudioPort*)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.
- */
-Raul::List<DriverPort*>::Node*
-JackAudioDriver::remove_port(const Path& path)
-{
-	assert(ThreadManager::current_thread_id() == THREAD_PROCESS);
-
-	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
-		if ((*i)->patch_port()->path() == path)
-			return (Raul::List<DriverPort*>::Node*)(_ports.erase(i));
-
-	cerr << "[JackAudioDriver::remove_port] WARNING: Unable to find port " << path << endl;
-	return NULL;
-}
-
-
-DriverPort*
-JackAudioDriver::port(const Path& path)
-{
-	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
-		if ((*i)->patch_port()->path() == path)
-			return (*i);
-
-	return NULL;
-}
-
-
-DriverPort*
-JackAudioDriver::create_port(DuplexPort* patch_port)
-{
-	try {
-		if (patch_port->buffer_size() == _buffer_size)
-			return new JackAudioPort(this, patch_port);
-		else
-			return NULL;
-	} catch (...) {
-		return NULL;
-	}
-}
-
-
-DriverPort*
-JackAudioDriver::driver_port(const Path& path)
-{
-	assert(ThreadManager::current_thread_id() == THREAD_PROCESS);
-
-	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
-		if ((*i)->patch_port()->path() == path)
-			return (*i);
-
-	return NULL;
-}
-
-
-/**** Jack Callbacks ****/
-
-
-
-/** Jack process callback, drives entire audio thread.
- *
- * \callgraph
- */
-int
-JackAudioDriver::_process_cb(jack_nframes_t nframes)
-{
-	if (nframes == 0 || ! _is_activated) {
-		if (_flag == 1)
-			_sem.post();
-		return 0;
-	}
-
-	// FIXME: all of this time stuff is screwy
-
-	// FIXME: support nframes != buffer_size, even though that never damn well happens
-	assert(nframes == _buffer_size / sizeof(Sample));
-
-	// Note that Jack can not call this function for a cycle, if overloaded
-	const jack_nframes_t start_of_current_cycle = jack_last_frame_time(_client);
-	const jack_nframes_t end_of_current_cycle   = start_of_current_cycle + nframes;
-
-	_transport_state = jack_transport_query(_client, &_position);
-
-	_process_context.set_time_slice(nframes, start_of_current_cycle, end_of_current_cycle);
-
-	for (Engine::ProcessSlaves::iterator i = _engine.process_slaves().begin();
-			i != _engine.process_slaves().end(); ++i) {
-		(*i)->context().set_time_slice(nframes, start_of_current_cycle, end_of_current_cycle);
-	}
-
-	// Process events that came in during the last cycle
-	// (Aiming for jitter-free 1 block event latency, ideally)
-	_engine.process_events(_process_context);
-
-	// Read input
-	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
-		(*i)->pre_process(_process_context);
-
-	// Run root patch
-	if (_root_patch)
-		_root_patch->process(_process_context);
-
-	// Signal message context to run if necessary
-	if (_engine.message_context()->has_requests())
-		_engine.message_context()->signal(_process_context);
-
-	// Write output
-	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
-		(*i)->post_process(_process_context);
-
-	_engine.post_processor()->set_end_time(_process_context.end());
-
-	return 0;
-}
-
-
-void
-JackAudioDriver::_thread_init_cb()
-{
-	// Initialize thread specific data
-	_jack_thread = Thread::create_for_this_thread("Jack");
-	assert(&Thread::get() == _jack_thread);
-	_jack_thread->set_context(THREAD_PROCESS);
-	assert(ThreadManager::current_thread_id() == THREAD_PROCESS);
-}
-
-void
-JackAudioDriver::_shutdown_cb()
-{
-	cout << "[JackAudioDriver] Jack shutdown.  Exiting." << endl;
-	_is_activated = false;
-	delete _jack_thread;
-	_jack_thread = NULL;
-	_client = NULL;
-}
-
-
-int
-JackAudioDriver::_sample_rate_cb(jack_nframes_t nframes)
-{
-	if (_is_activated) {
-		cerr << "[JackAudioDriver] On-the-fly sample rate changing not supported (yet).  Aborting." << endl;
-		exit(EXIT_FAILURE);
-	} else {
-		_sample_rate = nframes;
-	}
-	return 0;
-}
-
-
-int
-JackAudioDriver::_buffer_size_cb(jack_nframes_t nframes)
-{
-	if (_root_patch) {
-		_buffer_size = nframes * sizeof(Sample);
-		_root_patch->set_buffer_size(*_engine.buffer_factory(), _buffer_size);
-	}
-	return 0;
-}
-
-
-} // namespace Ingen
diff --git a/src/engine/JackAudioDriver.hpp b/src/engine/JackAudioDriver.hpp
deleted file mode 100644
index 5ff6bf64..00000000
--- a/src/engine/JackAudioDriver.hpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/* This file is part of Ingen.
- * Copyright (C) 2007-2009 Dave Robillard <http://drobilla.net>
- *
- * Ingen 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.
- *
- * Ingen 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.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef JACKAUDIODRIVER_H
-#define JACKAUDIODRIVER_H
-
-#include <jack/jack.h>
-#include <jack/transport.h>
-#include "raul/AtomicInt.hpp"
-#include "raul/List.hpp"
-#include "raul/Semaphore.hpp"
-#include "raul/Thread.hpp"
-#include "Driver.hpp"
-#include "Buffer.hpp"
-#include "ProcessContext.hpp"
-
-namespace Raul { class Path; }
-
-namespace Ingen {
-
-class Engine;
-class PatchImpl;
-class PortImpl;
-class DuplexPort;
-class JackAudioDriver;
-typedef jack_default_audio_sample_t jack_sample_t;
-
-
-/** Used internally by JackAudioDriver to represent a Jack port.
- *
- * A Jack port always has a one-to-one association with a Patch port.
- */
-class JackAudioPort : public DriverPort, public Raul::List<JackAudioPort*>::Node
-{
-public:
-	JackAudioPort(JackAudioDriver* driver, DuplexPort* patch_port);
-	~JackAudioPort();
-
-	void create();
-	void destroy();
-
-	void move(const Raul::Path& path);
-
-	void pre_process(ProcessContext& context);
-	void post_process(ProcessContext& context);
-
-	jack_port_t*  jack_port() const  { return _jack_port; }
-
-private:
-	JackAudioDriver* _driver;
-	jack_port_t*     _jack_port;
-};
-
-
-
-/** The Jack Driver.
- *
- * The process callback here drives the entire audio thread by "pulling"
- * events from queues, processing them, running the patches, and passing
- * events along to the PostProcessor.
- *
- * \ingroup engine
- */
-class JackAudioDriver : public Driver
-{
-public:
-	JackAudioDriver(Engine& engine);
-	~JackAudioDriver();
-
-	bool supports(Shared::PortType port_type, Shared::EventType event_type);
-
-	bool attach(const std::string& server_name,
-	            const std::string& client_name,
-	            void*              jack_client);
-
-	void activate();
-	void deactivate();
-	void enable();
-	void disable();
-
-	DriverPort* port(const Raul::Path& path);
-	DriverPort* create_port(DuplexPort* patch_port);
-
-	void        add_port(DriverPort* port);
-	DriverPort* driver_port(const Raul::Path& path);
-
-	Raul::List<DriverPort*>::Node* remove_port(const Raul::Path& path);
-
-	PatchImpl* root_patch()                     { return _root_patch; }
-	void       set_root_patch(PatchImpl* patch) { _root_patch = patch; }
-
-	ProcessContext& context() { return _process_context; }
-
-	/** Transport state for this frame.
-	 * Intended to only be called from the audio thread. */
-	inline const jack_position_t* position()        { return &_position; }
-	inline jack_transport_state_t transport_state() { return _transport_state; }
-
-	bool is_realtime() const { return jack_is_realtime(_client); }
-
-	jack_client_t* jack_client()  const { return _client; }
-	SampleCount    buffer_size()  const { return _buffer_size; }
-	SampleCount    sample_rate()  const { return _sample_rate; }
-	bool           is_activated() const { return _is_activated; }
-
-	inline SampleCount frame_time() const { return _client ? jack_frame_time(_client) : 0; }
-
-	class PortRegistrationFailedException : public std::exception {};
-
-private:
-	friend class JackAudioPort;
-
-	// Static JACK callbacks which call the non-static callbacks (methods)
-	inline static void thread_init_cb(void* const jack_driver) {
-		return ((JackAudioDriver*)jack_driver)->_thread_init_cb();
-	}
-	inline static void shutdown_cb(void* const jack_driver) {
-		return ((JackAudioDriver*)jack_driver)->_shutdown_cb();
-	}
-	inline static int process_cb(jack_nframes_t nframes, void* const jack_driver) {
-		return ((JackAudioDriver*)jack_driver)->_process_cb(nframes);
-	}
-	inline static int buffer_size_cb(jack_nframes_t nframes, void* const jack_driver) {
-		return ((JackAudioDriver*)jack_driver)->_buffer_size_cb(nframes);
-	}
-	inline static int sample_rate_cb(jack_nframes_t nframes, void* const jack_driver) {
-		return ((JackAudioDriver*)jack_driver)->_sample_rate_cb(nframes);
-	}
-
-	// Non static callbacks (methods)
-	void _thread_init_cb();
-	void _shutdown_cb();
-	int  _process_cb(jack_nframes_t nframes);
-	int  _buffer_size_cb(jack_nframes_t nframes);
-	int  _sample_rate_cb(jack_nframes_t nframes);
-
-	Engine&                _engine;
-	Raul::Thread*          _jack_thread;
-	Raul::Semaphore        _sem;
-	Raul::AtomicInt        _flag;
-	jack_client_t*         _client;
-	jack_nframes_t         _buffer_size;
-	jack_nframes_t         _sample_rate;
-	uint32_t               _midi_event_type;
-	bool                   _is_activated;
-	bool                   _local_client; ///< Whether _client should be closed on destruction
-	jack_position_t        _position;
-	jack_transport_state_t _transport_state;
-
-	Raul::List<JackAudioPort*> _ports;
-	ProcessContext             _process_context;
-
-	PatchImpl* _root_patch;
-};
-
-
-} // namespace Ingen
-
-#endif // JACKAUDIODRIVER_H
diff --git a/src/engine/JackDriver.cpp b/src/engine/JackDriver.cpp
new file mode 100644
index 00000000..68a7712b
--- /dev/null
+++ b/src/engine/JackDriver.cpp
@@ -0,0 +1,498 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007-2009 Dave Robillard <http://drobilla.net>
+ *
+ * Ingen 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.
+ *
+ * Ingen 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <iostream>
+#include <cstdlib>
+#include <jack/midiport.h>
+#include "raul/List.hpp"
+#include "shared/LV2Features.hpp"
+#include "shared/LV2URIMap.hpp"
+#include "AudioBuffer.hpp"
+#include "DuplexPort.hpp"
+#include "Engine.hpp"
+#include "Event.hpp"
+#include "EventBuffer.hpp"
+#include "EventSource.hpp"
+#include "EventSource.hpp"
+#include "JackDriver.hpp"
+#include "MessageContext.hpp"
+#include "PatchImpl.hpp"
+#include "PortImpl.hpp"
+#include "PostProcessor.hpp"
+#include "ProcessSlave.hpp"
+#include "QueuedEvent.hpp"
+#include "ThreadManager.hpp"
+#include "ingen-config.h"
+#include "tuning.hpp"
+#include "util.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+
+
+//// JackAudioPort ////
+
+JackAudioPort::JackAudioPort(JackAudioDriver* driver, DuplexPort* patch_port)
+	: DriverPort(patch_port)
+	, Raul::List<JackAudioPort*>::Node(this)
+	, _driver(driver)
+	, _jack_port(NULL)
+{
+	assert(patch_port->poly() == 1);
+
+	create();
+
+	patch_port->buffer(0)->clear();
+}
+
+
+JackAudioPort::~JackAudioPort()
+{
+	assert(_jack_port == NULL);
+}
+
+
+void
+JackAudioPort::create()
+{
+	_jack_port = jack_port_register(
+			_driver->jack_client(),
+			ingen_jack_port_name(_patch_port->path()).c_str(),
+			(_patch_port->type() == PortType::AUDIO)
+				? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE,
+			(_patch_port->is_input())
+				? JackPortIsInput : JackPortIsOutput,
+			0);
+
+	if (_jack_port == NULL) {
+		cerr << "[JackAudioPort] ERROR: Failed to register port " << _patch_port->path() << endl;
+		throw JackAudioDriver::PortRegistrationFailedException();
+	}
+}
+
+
+void
+JackAudioPort::destroy()
+{
+	assert(_jack_port);
+	if (jack_port_unregister(_driver->jack_client(), _jack_port))
+		cerr << "[JackMidiPort] ERROR: Unable to unregister port" << endl;
+	_jack_port = NULL;
+}
+
+
+void
+JackAudioPort::move(const Raul::Path& path)
+{
+	jack_port_set_name(_jack_port, ingen_jack_port_name(path).c_str());
+}
+
+
+void
+JackAudioPort::pre_process(ProcessContext& context)
+{
+	if (!is_input())
+		return;
+
+	const SampleCount nframes = context.nframes();
+
+	if (_patch_port->type() == PortType::AUDIO) {
+		jack_sample_t* jack_buf  = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes);
+		AudioBuffer*   patch_buf = (AudioBuffer*)_patch_port->buffer(0).get();
+
+		patch_buf->copy(jack_buf, 0, nframes - 1);
+
+	} else if (_patch_port->type() == PortType::EVENTS) {
+		void*          jack_buf  = jack_port_get_buffer(_jack_port, nframes);
+		EventBuffer*   patch_buf = (EventBuffer*)_patch_port->buffer(0).get();
+
+		const jack_nframes_t event_count = jack_midi_get_event_count(jack_buf);
+
+		patch_buf->prepare_write(context);
+
+		// Copy events from Jack port buffer into patch port buffer
+		for (jack_nframes_t i = 0; i < event_count; ++i) {
+			jack_midi_event_t ev;
+			jack_midi_event_get(&ev, jack_buf, i);
+
+			if (!patch_buf->append(ev.time, 0, _driver->_midi_event_type, ev.size, ev.buffer))
+				cerr << "WARNING: Failed to write MIDI to port buffer, event(s) lost!" << endl;
+		}
+	}
+}
+
+
+void
+JackAudioPort::post_process(ProcessContext& context)
+{
+	if (is_input())
+		return;
+
+	const SampleCount nframes = context.nframes();
+
+	if (_patch_port->type() == PortType::AUDIO) {
+		jack_sample_t* jack_buf  = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes);
+		AudioBuffer*   patch_buf = (AudioBuffer*)_patch_port->buffer(0).get();
+
+		memcpy(jack_buf, patch_buf->data(), nframes * sizeof(Sample));
+
+	} else if (_patch_port->type() == PortType::EVENTS) {
+		void*        jack_buf  = jack_port_get_buffer(_jack_port, context.nframes());
+		EventBuffer* patch_buf = (EventBuffer*)_patch_port->buffer(0).get();
+
+		patch_buf->prepare_read(context);
+		jack_midi_clear_buffer(jack_buf);
+
+		uint32_t frames    = 0;
+		uint32_t subframes = 0;
+		uint16_t type      = 0;
+		uint16_t size      = 0;
+		uint8_t* data      = NULL;
+
+		// Copy events from Jack port buffer into patch port buffer
+		for (patch_buf->rewind(); patch_buf->is_valid(); patch_buf->increment()) {
+			patch_buf->get_event(&frames, &subframes, &type, &size, &data);
+			jack_midi_event_write(jack_buf, frames, data, size);
+		}
+	}
+}
+
+
+//// JackAudioDriver ////
+
+JackAudioDriver::JackAudioDriver(Engine& engine)
+	: _engine(engine)
+	, _jack_thread(NULL)
+	, _sem(0)
+	, _flag(0)
+	, _client(NULL)
+	, _buffer_size(0)
+	, _sample_rate(0)
+	, _is_activated(false)
+	, _local_client(true)
+	, _process_context(engine)
+	, _root_patch(NULL)
+{
+	SharedPtr<Shared::LV2URIMap> map = PtrCast<Shared::LV2URIMap>(
+			_engine.world()->lv2_features->feature(LV2_URI_MAP_URI));
+	_midi_event_type = map->uri_to_id(NULL, "http://lv2plug.in/ns/ext/midi#MidiEvent");
+}
+
+
+JackAudioDriver::~JackAudioDriver()
+{
+	deactivate();
+
+	if (_local_client)
+		jack_client_close(_client);
+}
+
+
+bool
+JackAudioDriver::supports(Shared::PortType port_type, Shared::EventType event_type)
+{
+	return (port_type == PortType::AUDIO
+			|| (port_type == PortType::EVENTS && event_type == EventType::MIDI));
+}
+
+
+bool
+JackAudioDriver::attach(const std::string& server_name,
+                        const std::string& client_name,
+                        void*              jack_client)
+{
+	assert(!_client);
+	if (!jack_client) {
+		// Try supplied server name
+		if (server_name != "") {
+			_client = jack_client_open(client_name.c_str(),
+					JackServerName, NULL, server_name.c_str());
+			if (_client)
+				cerr << "[JackAudioDriver] Connected to JACK server '" <<
+					server_name << "'" << endl;
+		}
+
+		// Either server name not specified, or supplied server name does not exist
+		// Connect to default server
+		if (!_client) {
+			_client = jack_client_open(client_name.c_str(), JackNullOption, NULL);
+
+			if (_client)
+				cerr << "[JackAudioDriver] Connected to default JACK server." << endl;
+		}
+
+		// Still failed
+		if (!_client) {
+			cerr << "[JackAudioDriver] Unable to connect to Jack.  Exiting." << endl;
+			return false;
+		}
+	} else {
+		_client = (jack_client_t*)jack_client;
+	}
+
+	_local_client = (jack_client == NULL);
+
+	_buffer_size = jack_get_buffer_size(_client) * sizeof(Sample);
+	_sample_rate = jack_get_sample_rate(_client);
+
+	jack_on_shutdown(_client, shutdown_cb, this);
+
+	jack_set_thread_init_callback(_client, thread_init_cb, this);
+	jack_set_sample_rate_callback(_client, sample_rate_cb, this);
+	jack_set_buffer_size_callback(_client, buffer_size_cb, this);
+
+	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
+		(*i)->create();
+
+	return true;
+}
+
+
+void
+JackAudioDriver::activate()
+{
+	if (_is_activated) {
+		cerr << "[JackAudioDriver] Jack driver already activated." << endl;
+		return;
+	}
+
+	if (!_client)
+		attach(_engine.world()->conf->option("jack-server").get_string(),
+				_engine.world()->conf->option("jack-client").get_string(), NULL);
+
+	jack_set_process_callback(_client, process_cb, this);
+
+	_is_activated = true;
+
+	if (jack_activate(_client)) {
+		cerr << "[JackAudioDriver] Could not activate Jack client, aborting." << endl;
+		exit(EXIT_FAILURE);
+	} else {
+		cout << "[JackAudioDriver] Activated Jack client." << endl;
+	}
+}
+
+
+void
+JackAudioDriver::deactivate()
+{
+	if (_is_activated) {
+		_flag = 1;
+		_is_activated = false;
+		_sem.wait();
+
+		for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
+			(*i)->destroy();
+
+		jack_deactivate(_client);
+
+		if (_local_client) {
+			jack_client_close(_client);
+			_client = NULL;
+		}
+
+		_jack_thread->stop();
+		cout << "[JackAudioDriver] Deactivated Jack client." << endl;
+	}
+}
+
+
+/** 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(DriverPort* port)
+{
+	assert(ThreadManager::current_thread_id() == THREAD_PROCESS);
+	assert(dynamic_cast<JackAudioPort*>(port));
+	_ports.push_back((JackAudioPort*)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.
+ */
+Raul::List<DriverPort*>::Node*
+JackAudioDriver::remove_port(const Path& path)
+{
+	assert(ThreadManager::current_thread_id() == THREAD_PROCESS);
+
+	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
+		if ((*i)->patch_port()->path() == path)
+			return (Raul::List<DriverPort*>::Node*)(_ports.erase(i));
+
+	cerr << "[JackAudioDriver::remove_port] WARNING: Unable to find port " << path << endl;
+	return NULL;
+}
+
+
+DriverPort*
+JackAudioDriver::port(const Path& path)
+{
+	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
+		if ((*i)->patch_port()->path() == path)
+			return (*i);
+
+	return NULL;
+}
+
+
+DriverPort*
+JackAudioDriver::create_port(DuplexPort* patch_port)
+{
+	try {
+		if (patch_port->buffer_size() == _buffer_size)
+			return new JackAudioPort(this, patch_port);
+		else
+			return NULL;
+	} catch (...) {
+		return NULL;
+	}
+}
+
+
+DriverPort*
+JackAudioDriver::driver_port(const Path& path)
+{
+	assert(ThreadManager::current_thread_id() == THREAD_PROCESS);
+
+	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
+		if ((*i)->patch_port()->path() == path)
+			return (*i);
+
+	return NULL;
+}
+
+
+/**** Jack Callbacks ****/
+
+
+
+/** Jack process callback, drives entire audio thread.
+ *
+ * \callgraph
+ */
+int
+JackAudioDriver::_process_cb(jack_nframes_t nframes)
+{
+	if (nframes == 0 || ! _is_activated) {
+		if (_flag == 1)
+			_sem.post();
+		return 0;
+	}
+
+	// FIXME: all of this time stuff is screwy
+
+	// FIXME: support nframes != buffer_size, even though that never damn well happens
+	assert(nframes == _buffer_size / sizeof(Sample));
+
+	// Note that Jack can not call this function for a cycle, if overloaded
+	const jack_nframes_t start_of_current_cycle = jack_last_frame_time(_client);
+	const jack_nframes_t end_of_current_cycle   = start_of_current_cycle + nframes;
+
+	_transport_state = jack_transport_query(_client, &_position);
+
+	_process_context.set_time_slice(nframes, start_of_current_cycle, end_of_current_cycle);
+
+	for (Engine::ProcessSlaves::iterator i = _engine.process_slaves().begin();
+			i != _engine.process_slaves().end(); ++i) {
+		(*i)->context().set_time_slice(nframes, start_of_current_cycle, end_of_current_cycle);
+	}
+
+	// Process events that came in during the last cycle
+	// (Aiming for jitter-free 1 block event latency, ideally)
+	_engine.process_events(_process_context);
+
+	// Read input
+	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
+		(*i)->pre_process(_process_context);
+
+	// Run root patch
+	if (_root_patch)
+		_root_patch->process(_process_context);
+
+	// Signal message context to run if necessary
+	if (_engine.message_context()->has_requests())
+		_engine.message_context()->signal(_process_context);
+
+	// Write output
+	for (Raul::List<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i)
+		(*i)->post_process(_process_context);
+
+	_engine.post_processor()->set_end_time(_process_context.end());
+
+	return 0;
+}
+
+
+void
+JackAudioDriver::_thread_init_cb()
+{
+	// Initialize thread specific data
+	_jack_thread = Thread::create_for_this_thread("Jack");
+	assert(&Thread::get() == _jack_thread);
+	_jack_thread->set_context(THREAD_PROCESS);
+	assert(ThreadManager::current_thread_id() == THREAD_PROCESS);
+}
+
+void
+JackAudioDriver::_shutdown_cb()
+{
+	cout << "[JackAudioDriver] Jack shutdown.  Exiting." << endl;
+	_is_activated = false;
+	delete _jack_thread;
+	_jack_thread = NULL;
+	_client = NULL;
+}
+
+
+int
+JackAudioDriver::_sample_rate_cb(jack_nframes_t nframes)
+{
+	if (_is_activated) {
+		cerr << "[JackAudioDriver] On-the-fly sample rate changing not supported (yet).  Aborting." << endl;
+		exit(EXIT_FAILURE);
+	} else {
+		_sample_rate = nframes;
+	}
+	return 0;
+}
+
+
+int
+JackAudioDriver::_buffer_size_cb(jack_nframes_t nframes)
+{
+	if (_root_patch) {
+		_buffer_size = nframes * sizeof(Sample);
+		_root_patch->set_buffer_size(*_engine.buffer_factory(), _buffer_size);
+	}
+	return 0;
+}
+
+
+} // namespace Ingen
diff --git a/src/engine/JackDriver.hpp b/src/engine/JackDriver.hpp
new file mode 100644
index 00000000..5ff6bf64
--- /dev/null
+++ b/src/engine/JackDriver.hpp
@@ -0,0 +1,173 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007-2009 Dave Robillard <http://drobilla.net>
+ *
+ * Ingen 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.
+ *
+ * Ingen 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef JACKAUDIODRIVER_H
+#define JACKAUDIODRIVER_H
+
+#include <jack/jack.h>
+#include <jack/transport.h>
+#include "raul/AtomicInt.hpp"
+#include "raul/List.hpp"
+#include "raul/Semaphore.hpp"
+#include "raul/Thread.hpp"
+#include "Driver.hpp"
+#include "Buffer.hpp"
+#include "ProcessContext.hpp"
+
+namespace Raul { class Path; }
+
+namespace Ingen {
+
+class Engine;
+class PatchImpl;
+class PortImpl;
+class DuplexPort;
+class JackAudioDriver;
+typedef jack_default_audio_sample_t jack_sample_t;
+
+
+/** Used internally by JackAudioDriver to represent a Jack port.
+ *
+ * A Jack port always has a one-to-one association with a Patch port.
+ */
+class JackAudioPort : public DriverPort, public Raul::List<JackAudioPort*>::Node
+{
+public:
+	JackAudioPort(JackAudioDriver* driver, DuplexPort* patch_port);
+	~JackAudioPort();
+
+	void create();
+	void destroy();
+
+	void move(const Raul::Path& path);
+
+	void pre_process(ProcessContext& context);
+	void post_process(ProcessContext& context);
+
+	jack_port_t*  jack_port() const  { return _jack_port; }
+
+private:
+	JackAudioDriver* _driver;
+	jack_port_t*     _jack_port;
+};
+
+
+
+/** The Jack Driver.
+ *
+ * The process callback here drives the entire audio thread by "pulling"
+ * events from queues, processing them, running the patches, and passing
+ * events along to the PostProcessor.
+ *
+ * \ingroup engine
+ */
+class JackAudioDriver : public Driver
+{
+public:
+	JackAudioDriver(Engine& engine);
+	~JackAudioDriver();
+
+	bool supports(Shared::PortType port_type, Shared::EventType event_type);
+
+	bool attach(const std::string& server_name,
+	            const std::string& client_name,
+	            void*              jack_client);
+
+	void activate();
+	void deactivate();
+	void enable();
+	void disable();
+
+	DriverPort* port(const Raul::Path& path);
+	DriverPort* create_port(DuplexPort* patch_port);
+
+	void        add_port(DriverPort* port);
+	DriverPort* driver_port(const Raul::Path& path);
+
+	Raul::List<DriverPort*>::Node* remove_port(const Raul::Path& path);
+
+	PatchImpl* root_patch()                     { return _root_patch; }
+	void       set_root_patch(PatchImpl* patch) { _root_patch = patch; }
+
+	ProcessContext& context() { return _process_context; }
+
+	/** Transport state for this frame.
+	 * Intended to only be called from the audio thread. */
+	inline const jack_position_t* position()        { return &_position; }
+	inline jack_transport_state_t transport_state() { return _transport_state; }
+
+	bool is_realtime() const { return jack_is_realtime(_client); }
+
+	jack_client_t* jack_client()  const { return _client; }
+	SampleCount    buffer_size()  const { return _buffer_size; }
+	SampleCount    sample_rate()  const { return _sample_rate; }
+	bool           is_activated() const { return _is_activated; }
+
+	inline SampleCount frame_time() const { return _client ? jack_frame_time(_client) : 0; }
+
+	class PortRegistrationFailedException : public std::exception {};
+
+private:
+	friend class JackAudioPort;
+
+	// Static JACK callbacks which call the non-static callbacks (methods)
+	inline static void thread_init_cb(void* const jack_driver) {
+		return ((JackAudioDriver*)jack_driver)->_thread_init_cb();
+	}
+	inline static void shutdown_cb(void* const jack_driver) {
+		return ((JackAudioDriver*)jack_driver)->_shutdown_cb();
+	}
+	inline static int process_cb(jack_nframes_t nframes, void* const jack_driver) {
+		return ((JackAudioDriver*)jack_driver)->_process_cb(nframes);
+	}
+	inline static int buffer_size_cb(jack_nframes_t nframes, void* const jack_driver) {
+		return ((JackAudioDriver*)jack_driver)->_buffer_size_cb(nframes);
+	}
+	inline static int sample_rate_cb(jack_nframes_t nframes, void* const jack_driver) {
+		return ((JackAudioDriver*)jack_driver)->_sample_rate_cb(nframes);
+	}
+
+	// Non static callbacks (methods)
+	void _thread_init_cb();
+	void _shutdown_cb();
+	int  _process_cb(jack_nframes_t nframes);
+	int  _buffer_size_cb(jack_nframes_t nframes);
+	int  _sample_rate_cb(jack_nframes_t nframes);
+
+	Engine&                _engine;
+	Raul::Thread*          _jack_thread;
+	Raul::Semaphore        _sem;
+	Raul::AtomicInt        _flag;
+	jack_client_t*         _client;
+	jack_nframes_t         _buffer_size;
+	jack_nframes_t         _sample_rate;
+	uint32_t               _midi_event_type;
+	bool                   _is_activated;
+	bool                   _local_client; ///< Whether _client should be closed on destruction
+	jack_position_t        _position;
+	jack_transport_state_t _transport_state;
+
+	Raul::List<JackAudioPort*> _ports;
+	ProcessContext             _process_context;
+
+	PatchImpl* _root_patch;
+};
+
+
+} // namespace Ingen
+
+#endif // JACKAUDIODRIVER_H
diff --git a/src/engine/ingen_jack.cpp b/src/engine/ingen_jack.cpp
index d9020ec8..4bc88281 100644
--- a/src/engine/ingen_jack.cpp
+++ b/src/engine/ingen_jack.cpp
@@ -18,7 +18,7 @@
 #include <iostream>
 #include "module/Module.hpp"
 #include "module/World.hpp"
-#include "JackAudioDriver.hpp"
+#include "JackDriver.hpp"
 #include "Engine.hpp"
 
 using namespace std;
diff --git a/src/engine/wscript b/src/engine/wscript
index 752fd316..6e146d3c 100644
--- a/src/engine/wscript
+++ b/src/engine/wscript
@@ -109,7 +109,7 @@ def build(bld):
 
 	if bld.env['HAVE_JACK'] == 1:
 		obj = bld.new_task_gen('cxx', 'shlib')
-		obj.source = 'JackAudioDriver.cpp ingen_jack.cpp'
+		obj.source = 'JackDriver.cpp ingen_jack.cpp'
 		obj.export_incdirs = ['.']
 		obj.includes     = ['.', '..', '../..', '../common', '../engine']
 		obj.name         = 'libingen_jack'
-- 
cgit v1.2.1