X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libs%2Fardour%2Fmidi_diskstream.cc;h=64f02b0ad9c59a520c0d4c973c179d26f7ed4964;hb=7b818e9a7f3d999eb6bcc90c961d2b42531c3917;hp=d13f638fa4e4497afcf916317831a37be635c36d;hpb=0bc8832e208154a0f172d34182ed6b0bc8bba52f;p=ardour.git diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index d13f638fa4..64f02b0ad9 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -31,18 +31,15 @@ #include "pbd/error.h" #include "pbd/basename.h" -#include +#include #include "pbd/xml++.h" #include "pbd/memento_command.h" #include "pbd/enumwriter.h" #include "pbd/stateful_diff_command.h" #include "pbd/stacktrace.h" -#include "ardour/ardour.h" #include "ardour/audioengine.h" #include "ardour/butler.h" -#include "ardour/configuration.h" -#include "ardour/cycle_timer.h" #include "ardour/debug.h" #include "ardour/io.h" #include "ardour/midi_diskstream.h" @@ -52,11 +49,10 @@ #include "ardour/midi_region.h" #include "ardour/playlist_factory.h" #include "ardour/region_factory.h" -#include "ardour/route.h" -#include "ardour/send.h" #include "ardour/session.h" #include "ardour/session_playlists.h" #include "ardour/smf_source.h" +#include "ardour/types.h" #include "ardour/utils.h" #include "midi++/types.h" @@ -74,10 +70,10 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F : Diskstream(sess, name, flag) , _playback_buf(0) , _capture_buf(0) - , _source_port(0) , _note_mode(Sustained) , _frames_written_to_ringbuffer(0) , _frames_read_from_ringbuffer(0) + , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) { in_set_state = true; @@ -94,10 +90,10 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node) : Diskstream(sess, node) , _playback_buf(0) , _capture_buf(0) - , _source_port(0) , _note_mode(Sustained) , _frames_written_to_ringbuffer(0) , _frames_read_from_ringbuffer(0) + , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) { in_set_state = true; @@ -135,7 +131,7 @@ MidiDiskstream::init () MidiDiskstream::~MidiDiskstream () { - Glib::Mutex::Lock lm (state_lock); + Glib::Threads::Mutex::Lock lm (state_lock); } @@ -153,7 +149,7 @@ void MidiDiskstream::non_realtime_input_change () { { - Glib::Mutex::Lock lm (state_lock); + Glib::Threads::Mutex::Lock lm (state_lock); if (input_change_pending.type == IOChange::NoChange) { return; @@ -171,7 +167,7 @@ MidiDiskstream::non_realtime_input_change () } if (ni == 0) { - _source_port = 0; + _source_port.reset (); } else { _source_port = _io->midi(0); } @@ -252,7 +248,6 @@ MidiDiskstream::use_new_playlist () if ((playlist = boost::dynamic_pointer_cast (PlaylistFactory::create ( DataType::MIDI, _session, newname, hidden()))) != 0) { - playlist->set_orig_diskstream_id (id()); return use_playlist (playlist); } else { @@ -280,7 +275,6 @@ MidiDiskstream::use_copy_playlist () newname = Playlist::bump_name (_playlist->name(), _session); if ((playlist = boost::dynamic_pointer_cast(PlaylistFactory::create (midi_playlist(), newname))) != 0) { - playlist->set_orig_diskstream_id (id()); return use_playlist (playlist); } else { return -1; @@ -307,183 +301,14 @@ MidiDiskstream::set_note_mode (NoteMode m) _write_source->model()->set_note_mode(m); } -#if 0 -static void -trace_midi (ostream& o, MIDI::byte *msg, size_t len) -{ - using namespace MIDI; - eventType type; - const char trace_prefix = ':'; - - type = (eventType) (msg[0]&0xF0); - - switch (type) { - case off: - o << trace_prefix - << "Channel " - << (msg[0]&0xF)+1 - << " NoteOff NoteNum " - << (int) msg[1] - << " Vel " - << (int) msg[2] - << endl; - break; - - case on: - o << trace_prefix - << "Channel " - << (msg[0]&0xF)+1 - << " NoteOn NoteNum " - << (int) msg[1] - << " Vel " - << (int) msg[2] - << endl; - break; - - case polypress: - o << trace_prefix - << "Channel " - << (msg[0]&0xF)+1 - << " PolyPressure" - << (int) msg[1] - << endl; - break; - - case MIDI::controller: - o << trace_prefix - << "Channel " - << (msg[0]&0xF)+1 - << " Controller " - << (int) msg[1] - << " Value " - << (int) msg[2] - << endl; - break; - - case program: - o << trace_prefix - << "Channel " - << (msg[0]&0xF)+1 - << " Program Change ProgNum " - << (int) msg[1] - << endl; - break; - - case chanpress: - o << trace_prefix - << "Channel " - << (msg[0]&0xF)+1 - << " Channel Pressure " - << (int) msg[1] - << endl; - break; - - case MIDI::pitchbend: - o << trace_prefix - << "Channel " - << (msg[0]&0xF)+1 - << " Pitch Bend " - << ((msg[2]<<7)|msg[1]) - << endl; - break; - - case MIDI::sysex: - if (len == 1) { - switch (msg[0]) { - case 0xf8: - o << trace_prefix - << "Clock" - << endl; - break; - case 0xfa: - o << trace_prefix - << "Start" - << endl; - break; - case 0xfb: - o << trace_prefix - << "Continue" - << endl; - break; - case 0xfc: - o << trace_prefix - << "Stop" - << endl; - break; - case 0xfe: - o << trace_prefix - << "Active Sense" - << endl; - break; - case 0xff: - o << trace_prefix - << "System Reset" - << endl; - break; - default: - o << trace_prefix - << "System Exclusive (1 byte : " << hex << (int) *msg << dec << ')' - << endl; - break; - } - } else { - o << trace_prefix - << "System Exclusive (" << len << ") = [ " << hex; - for (unsigned int i = 0; i < len; ++i) { - o << (int) msg[i] << ' '; - } - o << dec << ']' << endl; - - } - break; - - case MIDI::song: - o << trace_prefix << "Song" << endl; - break; - - case MIDI::tune: - o << trace_prefix << "Tune" << endl; - break; - - case MIDI::eox: - o << trace_prefix << "End-of-System Exclusive" << endl; - break; - - case MIDI::timing: - o << trace_prefix << "Timing" << endl; - break; - - case MIDI::start: - o << trace_prefix << "Start" << endl; - break; - - case MIDI::stop: - o << trace_prefix << "Stop" << endl; - break; - - case MIDI::contineu: - o << trace_prefix << "Continue" << endl; - break; - - case active: - o << trace_prefix << "Active Sense" << endl; - break; - - default: - o << trace_prefix << "Unrecognized MIDI message" << endl; - break; - } -} -#endif - int -MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, bool can_record, bool& need_butler) +MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance) { - int ret = -1; framecnt_t rec_offset = 0; framecnt_t rec_nframes = 0; bool nominally_recording; bool re = record_enabled (); + bool can_record = _session.actively_recording (); playback_distance = 0; @@ -495,11 +320,13 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, bool can return 0; } - if (_source_port == 0) { + boost::shared_ptr sp = _source_port.lock (); + + if (sp == 0) { return 1; } - Glib::Mutex::Lock sm (state_lock, Glib::TRY_LOCK); + Glib::Threads::Mutex::Lock sm (state_lock, Glib::Threads::TRY_LOCK); if (!sm.locked()) { return 1; @@ -508,7 +335,7 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, bool can adjust_capture_position = 0; if (nominally_recording || (re && was_recording && _session.get_record_enabled() && _session.config.get_punch_in())) { - OverlapType ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); + Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset); @@ -526,7 +353,7 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, bool can if (nominally_recording || rec_nframes) { // Pump entire port buffer into the ring buffer (FIXME: split cycles?) - MidiBuffer& buf = _source_port->get_midi_buffer(nframes); + MidiBuffer& buf = sp->get_midi_buffer(nframes); for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { const Evoral::MIDIEvent ev(*i, false); assert(ev.buffer()); @@ -549,17 +376,23 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, bool can } if (buf.size() != 0) { - /* XXX this needs fixing - realtime new() call for - every time we get MIDI data in a process callback! - */ - - /* Make a copy of this data and emit it for the GUI to see */ - boost::shared_ptr copy (new MidiBuffer (buf.capacity ())); - for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { - copy->push_back ((*i).time() + transport_frame, (*i).size(), (*i).buffer()); + Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK); + + if (lm.locked ()) { + /* Copy this data into our GUI feed buffer and tell the GUI + that it can read it if it likes. + */ + _gui_feed_buffer.clear (); + + for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { + /* This may fail if buf is larger than _gui_feed_buffer, but it's not really + the end of the world if it does. + */ + _gui_feed_buffer.push_back ((*i).time() + transport_frame, (*i).size(), (*i).buffer()); + } } - DataRecorded (copy, _write_source); /* EMIT SIGNAL */ + DataRecorded (_write_source); /* EMIT SIGNAL */ } } else { @@ -596,17 +429,11 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, bool can } - ret = 0; - - if (commit (nframes)) { - need_butler = true; - } - - return ret; + return 0; } bool -MidiDiskstream::commit (framecnt_t nframes) +MidiDiskstream::commit (framecnt_t playback_distance) { bool need_butler = false; @@ -623,13 +450,26 @@ MidiDiskstream::commit (framecnt_t nframes) uint32_t frames_read = g_atomic_int_get(&_frames_read_from_ringbuffer); uint32_t frames_written = g_atomic_int_get(&_frames_written_to_ringbuffer); - if ((frames_written - frames_read) + nframes < midi_readahead) { - need_butler = true; + + /* + cerr << name() << " MDS written: " << frames_written << " - read: " << frames_read << + " = " << frames_written - frames_read + << " + " << playback_distance << " < " << midi_readahead << " = " << need_butler << ")" << endl; + */ + + /* frames_read will generally be less than frames_written, but + * immediately after an overwrite, we can end up having read some data + * before we've written any. we don't need to trip an assert() on this, + * but we do need to check so that the decision on whether or not we + * need the butler is done correctly. + */ + + if (frames_read <= frames_written) { + if ((frames_written - frames_read) + playback_distance < midi_readahead) { + need_butler = true; + } } - /*cerr << "MDS written: " << frames_written << " - read: " << frames_read << - " = " << frames_written - frames_read - << " + " << nframes << " < " << midi_readahead << " = " << need_butler << ")" << endl;*/ return need_butler; } @@ -663,9 +503,17 @@ MidiDiskstream::overwrite_existing_buffers () int MidiDiskstream::seek (framepos_t frame, bool complete_refill) { - Glib::Mutex::Lock lm (state_lock); + Glib::Threads::Mutex::Lock lm (state_lock); int ret = -1; + if (g_atomic_int_get (&_frames_read_from_ringbuffer) == 0) { + /* we haven't read anything since the last seek, + so flush all note trackers to prevent + wierdness + */ + reset_tracker (); + } + _playback_buf->reset(); _capture_buf->reset(); g_atomic_int_set(&_frames_read_from_ringbuffer, 0); @@ -737,16 +585,16 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed) start = loop_start + ((start - loop_start) % loop_length); //cerr << "to " << start << endl; } - //cerr << "start is " << start << " loopstart: " << loop_start << " loopend: " << loop_end << endl; + // cerr << "start is " << start << " end " << start+dur << " loopstart: " << loop_start << " loopend: " << loop_end << endl; } while (dur) { /* take any loop into account. we can't read past the end of the loop. */ - if (loc && (loop_end - start < dur)) { + if (loc && (loop_end - start <= dur)) { this_read = loop_end - start; - //cerr << "reloop true: thisread: " << this_read << " dur: " << dur << endl; + // cerr << "reloop true: thisread: " << this_read << " dur: " << dur << endl; reloop = true; } else { reloop = false; @@ -766,9 +614,7 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed) return -1; } - g_atomic_int_add(&_frames_written_to_ringbuffer, this_read); - - _read_data_count = _playlist->read_data_count(); + g_atomic_int_add (&_frames_written_to_ringbuffer, this_read); if (reversed) { @@ -872,8 +718,6 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush) assert (!destructive()); - _write_data_count = 0; - total = _session.transport_frame() - _write_source->last_write_end(); if (total == 0 || @@ -945,10 +789,10 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen } /* XXX is there anything we can do if err != 0 ? */ - Glib::Mutex::Lock lm (capture_info_lock); + Glib::Threads::Mutex::Lock lm (capture_info_lock); if (capture_info.empty()) { - return; + goto no_capture_stuff_to_do; } if (abort_capture) { @@ -1101,12 +945,12 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen delete *ci; } - if (_playlist) { - midi_playlist()->clear_note_trackers (); - } - capture_info.clear (); capture_start_frame = 0; + + no_capture_stuff_to_do: + + reset_tracker (); } void @@ -1136,6 +980,10 @@ MidiDiskstream::transport_looped (framepos_t transport_frame) last_recordable_frame = max_framepos; was_recording = true; } + + if (!Config->get_seamless_loop()) { + reset_tracker (); + } } void @@ -1199,8 +1047,10 @@ MidiDiskstream::engage_record_enable () g_atomic_int_set (&_record_enabled, 1); - if (_source_port && Config->get_monitoring_model() == HardwareMonitoring) { - _source_port->request_monitor_input (!(_session.config.get_auto_input() && rolling)); + boost::shared_ptr sp = _source_port.lock (); + + if (sp && Config->get_monitoring_model() == HardwareMonitoring) { + sp->request_jack_monitors_input (!(_session.config.get_auto_input() && rolling)); } RecordEnableChanged (); /* EMIT SIGNAL */ @@ -1373,10 +1223,13 @@ MidiDiskstream::allocate_temporary_buffers () } void -MidiDiskstream::monitor_input (bool yn) +MidiDiskstream::ensure_jack_monitors_input (bool yn) { - if (_source_port) - _source_port->ensure_monitor_input (yn); + boost::shared_ptr sp = _source_port.lock (); + + if (sp) { + sp->ensure_jack_monitors_input (yn); + } } void @@ -1397,15 +1250,25 @@ MidiDiskstream::set_align_style_from_io () float MidiDiskstream::playback_buffer_load () const { - return (float) ((double) _playback_buf->read_space()/ - (double) _playback_buf->capacity()); + /* For MIDI it's not trivial to differentiate the following two cases: + + 1. The playback buffer is empty because the system has run out of time to fill it. + 2. The playback buffer is empty because there is no more data on the playlist. + + If we use a simple buffer load computation, we will report that the MIDI diskstream + cannot keep up when #2 happens, when in fact it can. Since MIDI data rates + are so low compared to audio, just give a pretend answer here. + */ + + return 1; } float MidiDiskstream::capture_buffer_load () const { - return (float) ((double) _capture_buf->write_space()/ - (double) _capture_buf->capacity()); + /* We don't report playback buffer load, so don't report capture load either */ + + return 1; } int @@ -1414,43 +1277,41 @@ MidiDiskstream::use_pending_capture_data (XMLNode& /*node*/) return 0; } -/** Writes playback events in the given range to \a dst, translating time stamps - * so that an event at \a start has time = 0 +void +MidiDiskstream::flush_playback (framepos_t start, framepos_t end) +{ + _playback_buf->flush (start, end); + g_atomic_int_add (&_frames_read_from_ringbuffer, end - start); +} + +/** Writes playback events from playback_sample for nframes to dst, translating time stamps + * so that an event at playback_sample has time = 0 */ void -MidiDiskstream::get_playback (MidiBuffer& dst, framepos_t start, framepos_t end) +MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes) { dst.clear(); assert(dst.size() == 0); - // Reverse. ... We just don't do reverse, ok? Back off. - if (end <= start) { - return; - } - - // Translate stamps to be relative to start - - #ifndef NDEBUG DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ( "%1 MDS pre-read read %4..%5 from %2 write to %3\n", _name, - _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr(), start, end)); + _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr(), playback_sample, playback_sample + nframes)); // cerr << "================\n"; // _playback_buf->dump (cerr); // cerr << "----------------\n"; - const size_t events_read = _playback_buf->read(dst, start, end); + const size_t events_read = _playback_buf->read (dst, playback_sample, playback_sample + nframes); DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ( "%1 MDS events read %2 range %3 .. %4 rspace %5 wspace %6 r@%7 w@%8\n", - _name, events_read, start, end, + _name, events_read, playback_sample, playback_sample + nframes, _playback_buf->read_space(), _playback_buf->write_space(), - _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr())); + _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr())); #else - _playback_buf->read(dst, start, end); + _playback_buf->read (dst, playback_sample, playback_sample + nframes); #endif - gint32 frames_read = end - start; - g_atomic_int_add(&_frames_read_from_ringbuffer, frames_read); + g_atomic_int_add (&_frames_read_from_ringbuffer, nframes); } bool @@ -1464,3 +1325,24 @@ MidiDiskstream::set_name (string const & name) return true; } +boost::shared_ptr +MidiDiskstream::get_gui_feed_buffer () const +{ + boost::shared_ptr b (new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI))); + + Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex); + b->copy (_gui_feed_buffer); + return b; +} + +void +MidiDiskstream::reset_tracker () +{ + _playback_buf->reset_tracker (); + + boost::shared_ptr mp (midi_playlist()); + + if (mp) { + mp->clear_note_trackers (); + } +}