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

218 lines
5.5 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 "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