X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_port.cc;h=1377938f2e7363a734ab28169fd3b32933db0aed;hb=0c4869dc35a15757915ebbddcdd7077d788ca91e;hp=ba8d1aa841b87cc43088ee23f9cee3d1ce7359a7;hpb=7ff7f4013dfbbf18d08e397230ad2486fa7ff58f;p=ardour.git diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index ba8d1aa841..1377938f2e 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 @@ -19,93 +19,264 @@ #include #include -#include -#include +#include "pbd/compose.h" +#include "pbd/debug.h" + +#include "ardour/midi_port.h" +#include "ardour/data_type.h" +#include "ardour/audioengine.h" +#include "ardour/debug.h" -using namespace ARDOUR; using namespace std; +using namespace ARDOUR; +using namespace PBD; -MidiPort::MidiPort(jack_port_t* p) - : Port(p) - , _buffer(4096) // FIXME FIXME FIXME Jack needs to tell us this - , _nframes_this_cycle(0) +#define port_engine AudioEngine::instance()->port_engine() + +MidiPort::MidiPort (const std::string& name, PortFlags flags) + : Port (name, DataType::MIDI, flags) + , _has_been_mixed_down (false) + , _resolve_required (false) + , _input_active (true) + , _always_parse (false) { - DataType dt(_type); - assert(dt == DataType::MIDI); + _buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)); +} - reset(); +MidiPort::~MidiPort() +{ + delete _buffer; +} +void +MidiPort::cycle_start (pframes_t nframes) +{ + framepos_t now = AudioEngine::instance()->sample_time_at_cycle_start(); + + Port::cycle_start (nframes); + + _buffer->clear (); + + if (sends_output ()) { + port_engine.midi_clear (port_engine.get_buffer (_port_handle, nframes)); + } + + if (_always_parse) { + MidiBuffer& mb (get_midi_buffer (nframes)); + /* dump incoming MIDI to parser */ + + for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) { + uint8_t* buf = (*b).buffer(); + + _self_parser.set_timestamp (now + (*b).time()); + + uint32_t limit = (*b).size(); + + for (size_t n = 0; n < limit; ++n) { + _self_parser.scanner (buf[n]); + } + } + } } +MidiBuffer & +MidiPort::get_midi_buffer (pframes_t nframes) +{ + if (_has_been_mixed_down) { + return *_buffer; + } -MidiPort::~MidiPort() + if (receives_input ()) { + + if (_input_active) { + + void* buffer = port_engine.get_buffer (_port_handle, nframes); + const pframes_t event_count = port_engine.get_midi_event_count (buffer); + + /* suck all relevant MIDI events from the MIDI port buffer + into our MidiBuffer + */ + + for (pframes_t i = 0; i < event_count; ++i) { + + pframes_t timestamp; + size_t size; + uint8_t* buf; + + port_engine.midi_event_get (timestamp, size, &buf, buffer, i); + + if (buf[0] == 0xfe) { + /* throw away active sensing */ + continue; + } + + /* check that the event is in the acceptable time range */ + + if ((timestamp >= (_global_port_buffer_offset + _port_buffer_offset)) && + (timestamp < (_global_port_buffer_offset + _port_buffer_offset + nframes))) { + _buffer->push_back (timestamp, size, buf); + } else { + cerr << "Dropping incoming MIDI at time " << timestamp << "; offset=" + << _global_port_buffer_offset << " limit=" + << (_global_port_buffer_offset + _port_buffer_offset + nframes) << "\n"; + } + } + + } else { + _buffer->silence (nframes); + } + + } else { + _buffer->silence (nframes); + } + + if (nframes) { + _has_been_mixed_down = true; + } + + return *_buffer; +} + +void +MidiPort::cycle_end (pframes_t /*nframes*/) +{ + _has_been_mixed_down = false; +} + +void +MidiPort::cycle_split () { + _has_been_mixed_down = false; } void -MidiPort::cycle_start (nframes_t nframes) +MidiPort::resolve_notes (void* port_buffer, MidiBuffer::TimeType when) { - _buffer.clear(); - assert(_buffer.size() == 0); + for (uint8_t channel = 0; channel <= 0xF; channel++) { + + uint8_t ev[3] = { ((uint8_t) (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 + */ - _nframes_this_cycle = nframes; + if (port_engine.midi_event_put (port_buffer, when, ev, 3) != 0) { + cerr << "failed to deliver sustain-zero on channel " << (int)channel << " on port " << name() << endl; + } - if (_flags & JackPortIsOutput) { - _buffer.silence(nframes); - assert(_buffer.size() == 0); - return; + ev[1] = MIDI_CTL_ALL_NOTES_OFF; + + if (port_engine.midi_event_put (port_buffer, 0, ev, 3) != 0) { + cerr << "failed to deliver ALL NOTES OFF on channel " << (int)channel << " on port " << name() << endl; + } } +} - // We're an input - copy Jack events to internal buffer - - void* jack_buffer = jack_port_get_buffer(_port, nframes); +void +MidiPort::flush_buffers (pframes_t nframes) +{ + if (sends_output ()) { - const nframes_t event_count - = jack_midi_get_event_count(jack_buffer); + void* port_buffer = 0; + + if (_resolve_required) { + port_buffer = port_engine.get_buffer (_port_handle, nframes); + /* resolve all notes at the start of the buffer */ + resolve_notes (port_buffer, 0); + _resolve_required = false; + } + + if (_buffer->empty()) { + return; + } - assert(event_count < _buffer.capacity()); + if (!port_buffer) { + port_buffer = port_engine.get_buffer (_port_handle, nframes); + } - jack_midi_event_t ev; + for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) { - for (nframes_t i=0; i < event_count; ++i) { + const Evoral::MIDIEvent ev (*i, false); - jack_midi_event_get(&ev, jack_buffer, i); + // event times are in frames, relative to cycle start - _buffer.push_back(ev); +#ifndef NDEBUG + if (DEBUG::MidiIO & PBD::debug_bits) { + DEBUG_STR_DECL(a); + DEBUG_STR_APPEND(a, string_compose ("MidiPort %1 pop event @ %2 sz %3 ", this, ev.time(), ev.size())); + for (size_t i=0; i < ev.size(); ++i) { + DEBUG_STR_APPEND(a,hex); + DEBUG_STR_APPEND(a,"0x"); + DEBUG_STR_APPEND(a,(int)(ev.buffer()[i])); + DEBUG_STR_APPEND(a,' '); + } + DEBUG_STR_APPEND(a,'\n'); + DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str()); } +#endif - assert(_buffer.size() == event_count); + assert (ev.time() < (nframes + _global_port_buffer_offset + _port_buffer_offset)); - //if (_buffer.size() > 0) - // cerr << "MIDIPort got " << event_count << " events." << endl; + if (ev.time() >= _global_port_buffer_offset + _port_buffer_offset) { + if (port_engine.midi_event_put (port_buffer, (pframes_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() + << " too early for " << _global_port_buffer_offset + << " + " << _port_buffer_offset; + for (size_t xx = 0; xx < ev.size(); ++xx) { + cerr << ' ' << hex << (int) ev.buffer()[xx]; + } + cerr << dec << endl; + } + } + + /* done.. the data has moved to the port buffer, mark it so + */ + + _buffer->clear (); + } } void -MidiPort::cycle_end() +MidiPort::require_resolve () { - if (_flags & JackPortIsInput) { - _nframes_this_cycle = 0; - return; - } + _resolve_required = true; +} - // We're an output - copy events from internal buffer to Jack buffer - - void* jack_buffer = jack_port_get_buffer(_port, _nframes_this_cycle); - - const nframes_t event_count = _buffer.size(); - - //if (event_count > 0) - // cerr << "MIDIPort writing " << event_count << " events." << endl; - - jack_midi_clear_buffer(jack_buffer); - for (nframes_t i=0; i < event_count; ++i) { - const MidiEvent& ev = _buffer[i]; - // event times should be frames, relative to cycle start - assert(ev.time >= 0); - assert(ev.time < _nframes_this_cycle); - jack_midi_event_write(jack_buffer, (jack_nframes_t)ev.time, ev.buffer, ev.size); - } - - _nframes_this_cycle = 0; +void +MidiPort::transport_stopped () +{ + _resolve_required = true; +} + +void +MidiPort::realtime_locate () +{ + _resolve_required = true; +} + +void +MidiPort::reset () +{ + Port::reset (); + delete _buffer; + cerr << name() << " new MIDI buffer of size " << AudioEngine::instance()->raw_buffer_size (DataType::MIDI) << endl; + _buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)); +} + +void +MidiPort::set_input_active (bool yn) +{ + _input_active = yn; +} + +void +MidiPort::set_always_parse (bool yn) +{ + _always_parse = yn; }