X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_port.cc;h=9383bb237b84f711cceaed9ad5d22afb3fe8f7d0;hb=b9a9d8d0471c28e80e350d6e49cde965f87b986b;hp=83322dedb6f7d179ef59fc1e68531cf680e3d4a3;hpb=0b572cdd84151335594965a3f0ed16f1665dfa56;p=ardour.git diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index 83322dedb6..9383bb237b 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2006 Paul Davis + Copyright (C) 2006 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,86 +17,200 @@ */ #include -#include -#include #include +#include "ardour/midi_port.h" +#include "ardour/data_type.h" +#include "ardour/audioengine.h" + using namespace ARDOUR; using namespace std; -MidiPort::MidiPort(jack_port_t* p) - : Port(p) - , _buffer(NULL) - , _nframes_this_cycle(0) +MidiPort::MidiPort (const std::string& name, Flags flags) + : Port (name, DataType::MIDI, flags) + , _has_been_mixed_down (false) + , _resolve_required (false) + , _input_active (true) { - DataType dt(_type); - assert(dt == DataType::MIDI); - - reset(); - - _buffer = new MidiBuffer(4096); // FIXME FIXME FIXME + _buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)); } - MidiPort::~MidiPort() { delete _buffer; } void -MidiPort::cycle_start (jack_nframes_t nframes) +MidiPort::cycle_start (pframes_t nframes) { - _nframes_this_cycle = nframes; + Port::cycle_start (nframes); - if (_flags & JackPortIsOutput) { - _buffer->set_size(0); - return; - } + _buffer->clear (); - // We're an input - copy Jack events to internal buffer - - void* jack_buffer = jack_port_get_buffer(_port, nframes); + assert (_buffer->size () == 0); - const jack_nframes_t event_count - = jack_midi_port_get_info(jack_buffer, nframes)->event_count; + if (sends_output ()) { + jack_midi_clear_buffer (jack_port_get_buffer (_jack_port, nframes)); + } +} - assert(event_count < _buffer->capacity()); +MidiBuffer & +MidiPort::get_midi_buffer (pframes_t nframes) +{ + if (_has_been_mixed_down) { + return *_buffer; + } - for (jack_nframes_t i=0; i < event_count; ++i) { - jack_midi_event_t* const ev = &_buffer->data()[i]; - jack_midi_event_get(ev, jack_buffer, i, nframes); + if (receives_input ()) { + + if (_input_active) { + + void* jack_buffer = jack_port_get_buffer (_jack_port, nframes); + const pframes_t event_count = jack_midi_get_event_count (jack_buffer); + + assert (event_count < _buffer->capacity()); + + /* suck all relevant MIDI events from the JACK MIDI port buffer + into our MidiBuffer + */ + + for (pframes_t i = 0; i < event_count; ++i) { + + jack_midi_event_t ev; + + jack_midi_event_get (&ev, jack_buffer, i); + + if (ev.buffer[0] == 0xfe) { + /* throw away active sensing */ + continue; + } + + /* check that the event is in the acceptable time range */ + + if ((ev.time >= (_global_port_buffer_offset + _port_buffer_offset)) && + (ev.time < (_global_port_buffer_offset + _port_buffer_offset + nframes))) { + _buffer->push_back (ev); + } else { + cerr << "Dropping incoming MIDI at time " << ev.time << "; offset=" + << _global_port_buffer_offset << " limit=" + << (_global_port_buffer_offset + _port_buffer_offset + nframes) << "\n"; + } + } + + } else { + _buffer->silence (nframes); + } + + } else { + _buffer->silence (nframes); + } - // Convert note ons with velocity 0 to proper note offs - // FIXME: Jack MIDI should guarantee this - does it? - //if (ev->buffer[0] == MIDI_CMD_NOTE_ON && ev->buffer[2] == 0) - // ev->buffer[0] = MIDI_CMD_NOTE_OFF; + if (nframes) { + _has_been_mixed_down = true; } - _buffer->set_size(event_count); - - //if (_buffer->size() > 0) - // cerr << "MIDIPort got " << event_count << " events." << endl; + return *_buffer; +} + + +void +MidiPort::cycle_end (pframes_t /*nframes*/) +{ + _has_been_mixed_down = false; } void -MidiPort::cycle_end() +MidiPort::cycle_split () { - if (_flags & JackPortIsInput) { - _nframes_this_cycle = 0; // catch any oopses - return; + _has_been_mixed_down = false; +} + +void +MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when) +{ + + for (uint8_t channel = 0; channel <= 0xF; channel++) { + + uint8_t ev[3] = { MIDI_CMD_CONTROL | channel, MIDI_CTL_SUSTAIN, 0 }; + + /* we need to send all notes off AND turn the + * sustain/damper pedal off to handle synths + * that prioritize sustain over AllNotesOff + */ + + if (jack_midi_event_write (jack_buffer, when, ev, 3) != 0) { + cerr << "failed to deliver sustain-zero on channel " << channel << " on port " << name() << endl; + } + + ev[1] = MIDI_CTL_ALL_NOTES_OFF; + + if (jack_midi_event_write (jack_buffer, 0, ev, 3) != 0) { + cerr << "failed to deliver ALL NOTES OFF on channel " << channel << " on port " << name() << endl; + } } +} - // We're an output - copy events from internal buffer to Jack buffer - - void* jack_buffer = jack_port_get_buffer(_port, _nframes_this_cycle); +void +MidiPort::flush_buffers (pframes_t nframes, framepos_t /*time*/) +{ + if (sends_output ()) { + + void* jack_buffer = jack_port_get_buffer (_jack_port, nframes); + + if (_resolve_required) { + /* resolve all notes at the start of the buffer */ + resolve_notes (jack_buffer, 0); + _resolve_required = false; + } + + for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) { + + const Evoral::MIDIEvent ev (*i, false); + + // event times are in frames, relative to cycle start - const jack_nframes_t event_count = _buffer->size(); + assert (ev.time() < (nframes + _global_port_buffer_offset + _port_buffer_offset)); - jack_midi_clear_buffer(jack_buffer, _nframes_this_cycle); - for (jack_nframes_t i=0; i < event_count; ++i) { - const jack_midi_event_t& ev = _buffer->data()[i]; - jack_midi_event_write(jack_buffer, ev.time, ev.buffer, ev.size, _nframes_this_cycle); + if (ev.event_type() == LoopEventType) { + resolve_notes (jack_buffer, ev.time()); + continue; + } + + if (ev.time() >= _global_port_buffer_offset + _port_buffer_offset) { + if (jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size()) != 0) { + cerr << "write failed, drop flushed note off on the floor, time " + << ev.time() << " > " << _global_port_buffer_offset + _port_buffer_offset << endl; + } + } else { + cerr << "drop flushed event on the floor, time " << ev.time() + << " < " << _global_port_buffer_offset + _port_buffer_offset << endl; + } + } } - - _nframes_this_cycle = 0; // catch oopses +} + +void +MidiPort::transport_stopped () +{ + _resolve_required = true; +} + +void +MidiPort::realtime_locate () +{ + _resolve_required = true; +} + +void +MidiPort::reset () +{ + Port::reset (); + delete _buffer; + _buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)); +} + +void +MidiPort::set_input_active (bool yn) +{ + _input_active = yn; }