summaryrefslogtreecommitdiffstats
path: root/src/libs/engine/JackMidiDriver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/engine/JackMidiDriver.cpp')
-rw-r--r--src/libs/engine/JackMidiDriver.cpp217
1 files changed, 217 insertions, 0 deletions
diff --git a/src/libs/engine/JackMidiDriver.cpp b/src/libs/engine/JackMidiDriver.cpp
new file mode 100644
index 00000000..40054b8a
--- /dev/null
+++ b/src/libs/engine/JackMidiDriver.cpp
@@ -0,0 +1,217 @@
+/* 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 "JackMidiDriver.h"
+#include <iostream>
+#include <cstdlib>
+#include <pthread.h>
+#include "Om.h"
+#include "OmApp.h"
+#include "util/types.h"
+#include "midi.h"
+#include "OmApp.h"
+#include "Maid.h"
+#include "AudioDriver.h"
+#include "MidiInputNode.h"
+#include "PortInfo.h"
+#include "MidiMessage.h"
+#include "PortBase.h"
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+using std::cout; using std::cerr; using std::endl;
+
+namespace Om {
+
+
+//// JackMidiPort ////
+
+JackMidiPort::JackMidiPort(JackMidiDriver* driver, PortBase<MidiMessage>* patch_port)
+: DriverPort(),
+ ListNode<JackMidiPort*>(this),
+ m_driver(driver),
+ m_jack_port(NULL),
+ m_patch_port(patch_port)
+{
+ assert(patch_port->poly() == 1);
+
+ m_jack_port = jack_port_register(m_driver->jack_client(),
+ patch_port->path().c_str(), JACK_DEFAULT_MIDI_TYPE,
+ (patch_port->port_info()->is_input()) ? JackPortIsInput : JackPortIsOutput,
+ 0);
+
+ patch_port->buffer(0)->clear();
+ patch_port->fixed_buffers(true);
+}
+
+
+JackMidiPort::~JackMidiPort()
+{
+ jack_port_unregister(m_driver->jack_client(), m_jack_port);
+}
+
+
+void
+JackMidiPort::add_to_driver()
+{
+ m_driver->add_port(this);
+}
+
+
+void
+JackMidiPort::remove_from_driver()
+{
+ m_driver->remove_port(this);
+}
+
+
+/** Prepare events for a block.
+ *
+ * This is basically trivial (as opposed to AlsaMidiPort) since Jack MIDI
+ * data is in-band with the audio thread.
+ *
+ * Prepares all events that occurred during the time interval passed
+ * (which ideally are the events from the previous cycle with an exact
+ * 1 cycle delay).
+ */
+void
+JackMidiPort::prepare_block(const samplecount block_start, const samplecount block_end)
+{
+ assert(block_end >= block_start);
+
+ const samplecount nframes = block_end - block_start;
+ void* jack_buffer = jack_port_get_buffer(m_jack_port, nframes);
+ const jack_nframes_t event_count = jack_midi_port_get_info(jack_buffer, nframes)->event_count;
+
+ assert(event_count < m_patch_port->buffer_size());
+
+ // 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_t*)&m_patch_port->buffer(0)->value_at(i);
+ jack_midi_event_get(ev, jack_buffer, i, nframes);
+
+ // Convert note ons with velocity 0 to proper note offs
+ if (ev->buffer[0] == MIDI_CMD_NOTE_ON && ev->buffer[2] == 0)
+ ev->buffer[0] = MIDI_CMD_NOTE_OFF;
+
+ // MidiMessage and jack_midi_event_t* are the same thing :/
+ MidiMessage* const message = &m_patch_port->buffer(0)->data()[i];
+ message->time = ev->time;
+ message->size = ev->size;
+ message->buffer = ev->buffer;
+ }
+
+ //cerr << "Jack MIDI got " << event_count << " events." << endl;
+
+ m_patch_port->buffer(0)->filled_size(event_count);
+ m_patch_port->tied_port()->buffer(0)->filled_size(event_count);
+}
+
+
+
+//// JackMidiDriver ////
+
+
+bool JackMidiDriver::m_midi_thread_exit_flag = true;
+
+
+JackMidiDriver::JackMidiDriver(jack_client_t* client)
+: m_client(client),
+ m_is_activated(false),
+ m_is_enabled(false)
+{
+}
+
+
+JackMidiDriver::~JackMidiDriver()
+{
+ deactivate();
+}
+
+
+/** Launch and start the MIDI thread.
+ */
+void
+JackMidiDriver::activate()
+{
+ m_is_activated = true;
+}
+
+
+/** Terminate the MIDI thread.
+ */
+void
+JackMidiDriver::deactivate()
+{
+ m_is_activated = false;
+}
+
+
+/** Build flat arrays of events for DSSI plugins for each Port.
+ */
+void
+JackMidiDriver::prepare_block(const samplecount block_start, const samplecount block_end)
+{
+ for (List<JackMidiPort*>::iterator i = m_in_ports.begin(); i != m_in_ports.end(); ++i)
+ (*i)->prepare_block(block_start, block_end);
+}
+
+
+/** 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 create_port() and remove_port().
+ */
+void
+JackMidiDriver::add_port(JackMidiPort* port)
+{
+ if (port->patch_port()->port_info()->is_input())
+ m_in_ports.push_back(port);
+ else
+ m_out_ports.push_back(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.
+ */
+JackMidiPort*
+JackMidiDriver::remove_port(JackMidiPort* port)
+{
+ if (port->patch_port()->port_info()->is_input()) {
+ for (List<JackMidiPort*>::iterator i = m_in_ports.begin(); i != m_in_ports.end(); ++i)
+ if ((*i) == (JackMidiPort*)port)
+ return m_in_ports.remove(i)->elem();
+ } else {
+ for (List<JackMidiPort*>::iterator i = m_out_ports.begin(); i != m_out_ports.end(); ++i)
+ if ((*i) == port)
+ return m_out_ports.remove(i)->elem();
+ }
+
+ cerr << "[JackMidiDriver::remove_input] WARNING: Failed to find Jack port to remove!" << endl;
+ return NULL;
+}
+
+
+} // namespace Om
+