diff options
author | David Robillard <d@drobilla.net> | 2008-09-30 16:50:21 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2008-09-30 16:50:21 +0000 |
commit | 93850c202de8b073a1ce1dd8bd246d407bce4e2f (patch) | |
tree | 6910b135bf4eff12de1af116cef73f6e9c107cd0 /src/engine/JackMidiDriver.cpp | |
parent | a8bf5272d096de73507d2eab47f282c345f4ca8a (diff) | |
download | ingen-93850c202de8b073a1ce1dd8bd246d407bce4e2f.tar.gz ingen-93850c202de8b073a1ce1dd8bd246d407bce4e2f.tar.bz2 ingen-93850c202de8b073a1ce1dd8bd246d407bce4e2f.zip |
Flatten ingen source directory heirarchy a bit.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@1551 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/engine/JackMidiDriver.cpp')
-rw-r--r-- | src/engine/JackMidiDriver.cpp | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/src/engine/JackMidiDriver.cpp b/src/engine/JackMidiDriver.cpp new file mode 100644 index 00000000..9e236541 --- /dev/null +++ b/src/engine/JackMidiDriver.cpp @@ -0,0 +1,267 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 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 <pthread.h> +#include <raul/Maid.hpp> +#include <raul/midi_events.h> +#include "types.hpp" +#include "JackMidiDriver.hpp" +#include "JackAudioDriver.hpp" +#include "ThreadManager.hpp" +#include "AudioDriver.hpp" +#include "EventBuffer.hpp" +#include "DuplexPort.hpp" +#include "ProcessContext.hpp" +#include "jack_compat.h" +using namespace std; + +namespace Ingen { + + +//// JackMidiPort //// + +JackMidiPort::JackMidiPort(JackMidiDriver* driver, DuplexPort* patch_port) + : DriverPort(patch_port) + , Raul::List<JackMidiPort*>::Node(this) + , _driver(driver) + , _jack_port(NULL) +{ + assert(patch_port->poly() == 1); + + _jack_port = jack_port_register(_driver->jack_client(), + patch_port->path().c_str(), JACK_DEFAULT_MIDI_TYPE, + (patch_port->is_input()) ? JackPortIsInput : JackPortIsOutput, + 0); + + if (_jack_port == NULL) { + cerr << "[JackMidiPort] ERROR: Failed to register port " << patch_port->path() << endl; + throw JackAudioDriver::PortRegistrationFailedException(); + } + + patch_port->buffer(0)->clear(); +} + + +JackMidiPort::~JackMidiPort() +{ + jack_port_unregister(_driver->jack_client(), _jack_port); +} + + +/** Prepare input for a block before a cycle is run, in the audio thread. + * + * This is simple since Jack MIDI is in-band with audio. + */ +void +JackMidiPort::pre_process(ProcessContext& context) +{ + if ( ! is_input() ) + return; + + assert(_patch_port->poly() == 1); + + EventBuffer* patch_buf = dynamic_cast<EventBuffer*>(_patch_port->buffer(0)); + assert(patch_buf); + + void* jack_buffer = jack_port_get_buffer(_jack_port, context.nframes()); + const jack_nframes_t event_count = jack_midi_get_event_count(jack_buffer); + + patch_buf->prepare_write(context.start(), context.nframes()); + + // 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_buffer, i); + + // FIXME: type is hardcoded for now, we should get it from + // the type map instead + const bool success = patch_buf->append(ev.time, 0, 1, ev.size, ev.buffer); + if (!success) + cerr << "WARNING: Failed to write MIDI to port buffer, event(s) lost!" << endl; + } + + //if (event_count) + // cerr << "Jack MIDI got " << event_count << " events." << endl; +} + + +/** Prepare output for a block after a cycle is run, in the audio thread. + * + * This is simple since Jack MIDI is in-band with audio. + */ +void +JackMidiPort::post_process(ProcessContext& context) +{ + if (is_input()) + return; + + EventBuffer* patch_buf = dynamic_cast<EventBuffer*>(_patch_port->buffer(0)); + void* jack_buf = jack_port_get_buffer(_jack_port, context.nframes()); + + assert(_patch_port->poly() == 1); + assert(patch_buf); + + patch_buf->prepare_read(context.start(), context.nframes()); + 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); + } + + //if (event_count) + // cerr << "Jack MIDI wrote " << event_count << " events." << endl; +} + + + +//// JackMidiDriver //// + + +bool JackMidiDriver::_midi_thread_exit_flag = true; + + +JackMidiDriver::JackMidiDriver(jack_client_t* client) + : _client(client) + , _is_activated(false) + , _is_enabled(false) +{ +} + + +JackMidiDriver::~JackMidiDriver() +{ + deactivate(); +} + + +/** Launch and start the MIDI thread. + */ +void +JackMidiDriver::activate() +{ + _is_activated = true; +} + + +/** Terminate the MIDI thread. + */ +void +JackMidiDriver::deactivate() +{ + _is_activated = false; +} + + +/** Build flat arrays of events to be used as input for the given cycle. + */ +void +JackMidiDriver::pre_process(ProcessContext& context) +{ + for (Raul::List<JackMidiPort*>::iterator i = _in_ports.begin(); i != _in_ports.end(); ++i) + (*i)->pre_process(context); +} + + +/** Write the output from any (top-level, exported) MIDI output ports to Jack ports. + */ +void +JackMidiDriver::post_process(ProcessContext& context) +{ + for (Raul::List<JackMidiPort*>::iterator i = _out_ports.begin(); i != _out_ports.end(); ++i) + (*i)->post_process(context); +} + + +/** Add an Jack MIDI 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 new_port() and remove_port(). + */ +void +JackMidiDriver::add_port(DriverPort* port) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + assert(dynamic_cast<JackMidiPort*>(port)); + + if (port->is_input()) + _in_ports.push_back((JackMidiPort*)port); + else + _out_ports.push_back((JackMidiPort*)port); +} + + +/** Remove an Jack MIDI 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. + */ +DriverPort* +JackMidiDriver::remove_port(const Path& path) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + // FIXME: duplex? + + for (Raul::List<JackMidiPort*>::iterator i = _in_ports.begin(); i != _in_ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return _in_ports.erase(i)->elem(); // FIXME: LEAK + + for (Raul::List<JackMidiPort*>::iterator i = _out_ports.begin(); i != _out_ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return _out_ports.erase(i)->elem(); // FIXME: LEAK + + cerr << "[JackMidiDriver::remove_input] WARNING: Unable to find Jack port " << path << endl; + return NULL; +} + + +DriverPort* +JackMidiDriver::driver_port(const Path& path) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + // FIXME: duplex? + + for (Raul::List<JackMidiPort*>::iterator i = _in_ports.begin(); i != _in_ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return (*i); + + for (Raul::List<JackMidiPort*>::iterator i = _out_ports.begin(); i != _out_ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return (*i); + + return NULL; +} + + +} // namespace Ingen + |