X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_diskstream.cc;h=708884900ca48c2880bac091a1b66cb1433505a3;hb=3b7cb8275a447773b48bea093bdc801c3f484ad6;hp=229c0ad7688aea940eec7fd8042dcd5bb2e901df;hpb=46c83693284ece4a732d26e62113ea4ac584d539;p=ardour.git diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 229c0ad768..708884900c 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -16,7 +16,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include #include @@ -59,7 +58,7 @@ #include "midi++/types.h" -#include "i18n.h" +#include "pbd/i18n.h" #include using namespace std; @@ -361,7 +360,8 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t framepos_t loop_start = 0; framepos_t loop_end = 0; framepos_t loop_length = 0; - get_location_times(loop_loc, &loop_start, &loop_end, &loop_length); + + get_location_times (loop_loc, &loop_start, &loop_end, &loop_length); adjust_capture_position = 0; @@ -458,7 +458,7 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t 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. @@ -507,9 +507,9 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t playback_distance = nframes; } - if (need_disk_signal) { + if (need_disk_signal && !_session.declick_out_pending()) { /* copy the diskstream data to all output buffers */ - + MidiBuffer& mbuf (bufs.get_midi (0)); get_playback (mbuf, playback_distance); @@ -583,7 +583,7 @@ MidiDiskstream::commit (framecnt_t playback_distance) * but we do need to check so that the decision on whether or not we * need the butler is done correctly. */ - + /* furthermore.. * * Doing heavy GUI operations[1] can stall also the butler. @@ -704,42 +704,48 @@ int MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed) { framecnt_t this_read = 0; - bool reloop = false; framepos_t loop_end = 0; framepos_t loop_start = 0; framecnt_t loop_length = 0; - Location* loc = 0; + Location* loc = loop_location; + framepos_t effective_start = start; + Evoral::Range* loop_range (0); MidiTrack* mt = dynamic_cast(_track); MidiChannelFilter* filter = mt ? &mt->playback_filter() : NULL; - if (!reversed) { - - loc = loop_location; - get_location_times(loc, &loop_start, &loop_end, &loop_length); - - /* if we are looping, ensure that the first frame we read is at the correct - position within the loop. - */ + frameoffset_t loop_offset = 0; - if (loc && (start >= loop_end)) { - //cerr << "start adjusted from " << start; - start = loop_start + ((start - loop_start) % loop_length); - //cerr << "to " << start << endl; - } - // cerr << "start is " << start << " end " << start+dur << " loopstart: " << loop_start << " loopend: " << loop_end << endl; + if (!reversed && loc) { + get_location_times (loc, &loop_start, &loop_end, &loop_length); } while (dur) { /* take any loop into account. we can't read past the end of the loop. */ - if (loc && (loop_end - start <= dur)) { - this_read = loop_end - start; - // cerr << "reloop true: thisread: " << this_read << " dur: " << dur << endl; - reloop = true; + if (loc && !reversed) { + + if (!loop_range) { + loop_range = new Evoral::Range (loop_start, loop_end-1); // inclusive semantics require -1 + } + + /* if we are (seamlessly) looping, ensure that the first frame we read is at the correct + position within the loop. + */ + + effective_start = loop_range->squish (effective_start); + + if ((loop_end - effective_start) <= dur) { + /* too close to end of loop to read "dur", so + shorten it. + */ + this_read = loop_end - effective_start; + } else { + this_read = dur; + } + } else { - reloop = false; this_read = dur; } @@ -747,15 +753,17 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed) break; } - this_read = min(dur,this_read); + this_read = min (dur,this_read); - if (midi_playlist()->read (*_playback_buf, start, this_read, 0, filter) != this_read) { + DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS ::read at %1 for %2 loffset %3\n", effective_start, this_read, loop_offset)); + + if (midi_playlist()->read (*_playback_buf, effective_start, this_read, loop_range, 0, filter) != this_read) { error << string_compose( _("MidiDiskstream %1: cannot read %2 from playlist at frame %3"), id(), this_read, start) << endmsg; return -1; } - + g_atomic_int_add (&_frames_written_to_ringbuffer, this_read); if (reversed) { @@ -766,14 +774,16 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed) } else { - /* if we read to the end of the loop, go back to the beginning */ - if (reloop) { - // Synthesize LoopEvent here, because the next events - // written will have non-monotonic timestamps. - start = loop_start; - } else { - start += this_read; - } + /* adjust passed-by-reference argument (note: this is + monotonic and does not reflect looping. + */ + start += this_read; + + /* similarly adjust effective_start, but this may be + readjusted for seamless looping as we continue around + the loop. + */ + effective_start += this_read; } dur -= this_read; @@ -796,6 +806,10 @@ MidiDiskstream::do_refill () size_t write_space = _playback_buf->write_space(); bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f; + DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS refill, write space = %1 file frame = %2\n", + write_space, file_frame)); + + /* no space to write */ if (write_space == 0) { return 0; } @@ -809,22 +823,15 @@ MidiDiskstream::do_refill () return 0; } - /* no space to write */ - if (_playback_buf->write_space() == 0) { - return 0; - } - 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_read < frames_written) && (frames_written - frames_read) >= midi_readahead) { return 0; } framecnt_t to_read = midi_readahead - ((framecnt_t)frames_written - (framecnt_t)frames_read); - //cout << "MDS read for midi_readahead " << to_read << " rb_contains: " - // << frames_written - frames_read << endl; - to_read = min (to_read, (framecnt_t) (max_framepos - file_frame)); to_read = min (to_read, (framecnt_t) write_space); @@ -857,8 +864,8 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush) const framecnt_t total = g_atomic_int_get(const_cast (&_frames_pending_write)); - if (total == 0 || - _capture_buf->read_space() == 0 || + if (total == 0 || + _capture_buf->read_space() == 0 || (!force_flush && (total < disk_write_chunk_frames) && was_recording)) { goto out; } @@ -890,7 +897,7 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush) if (_write_source->midi_write (lm, *_capture_buf, get_capture_start_frame (0), to_write) != to_write) { error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg; return -1; - } + } g_atomic_int_add(const_cast (&_frames_pending_write), -to_write); } @@ -1078,7 +1085,7 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen _write_source->drop_references (); _write_source.reset(); } - } + } } @@ -1157,7 +1164,7 @@ MidiDiskstream::set_record_enabled (bool yn) } else { disengage_record_enable (); } - + RecordEnableChanged (); /* EMIT SIGNAL */ } } @@ -1168,18 +1175,18 @@ MidiDiskstream::set_record_safe (bool yn) if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_midi() == 0) { // REQUIRES REVIEW return; } - + /* yes, i know that this not proof against race conditions, but its good enough. i think. */ - + if (record_safe () != yn) { if (yn) { engage_record_safe (); } else { disengage_record_safe (); } - + RecordSafeChanged (); /* EMIT SIGNAL */ } } @@ -1194,7 +1201,7 @@ MidiDiskstream::prep_record_enable () bool const rolling = _session.transport_speed() != 0.0f; boost::shared_ptr sp = _source_port.lock (); - + if (sp && Config->get_monitoring_model() == HardwareMonitoring) { sp->request_input_monitoring (!(_session.config.get_auto_input() && rolling)); } @@ -1214,7 +1221,7 @@ MidiDiskstream::get_state () { XMLNode& node (Diskstream::get_state()); char buf[64]; - LocaleGuard lg (X_("C")); + LocaleGuard lg; if (_write_source && _session.get_record_enabled()) { @@ -1248,7 +1255,7 @@ MidiDiskstream::set_state (const XMLNode& node, int version) XMLNodeList nlist = node.children(); XMLNodeIterator niter; XMLNode* capture_pending_node = 0; - LocaleGuard lg (X_("C")); + LocaleGuard lg; /* prevent write sources from being created */ @@ -1321,14 +1328,14 @@ MidiDiskstream::steal_write_source_name () try { string new_path = _session.new_midi_source_path (name()); - + if (_write_source->rename (new_path)) { return string(); } } catch (...) { return string (); } - + return our_old_name; } @@ -1360,7 +1367,7 @@ void MidiDiskstream::ensure_input_monitoring (bool yn) { boost::shared_ptr sp = _source_port.lock (); - + if (sp) { sp->ensure_input_monitoring (yn); } @@ -1385,7 +1392,7 @@ float MidiDiskstream::playback_buffer_load () const { /* 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. @@ -1393,7 +1400,7 @@ MidiDiskstream::playback_buffer_load () const 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; } @@ -1401,7 +1408,7 @@ float MidiDiskstream::capture_buffer_load () const { /* We don't report playback buffer load, so don't report capture load either */ - + return 1; } @@ -1429,25 +1436,22 @@ MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes) Location* loc = loop_location; DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ( - "%1 MDS pre-read read %8 @ %4..%5 from %2 write to %3, LOOPED ? %6-%7\n", _name, - _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr(), playback_sample, playback_sample + nframes, - (loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes)); + "%1 MDS pre-read read %8 offset = %9 @ %4..%5 from %2 write to %3, LOOPED ? %6 .. %7\n", _name, + _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr(), playback_sample, playback_sample + nframes, + (loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes, Port::port_offset())); - // cerr << "================\n"; - // _playback_buf->dump (cerr); - // cerr << "----------------\n"; + //cerr << "======== PRE ========\n"; + //_playback_buf->dump (cerr); + //cerr << "----------------\n"; - size_t events_read = 0; + size_t events_read = 0; if (loc) { framepos_t effective_start; - if (playback_sample >= loc->end()) { - effective_start = loc->start() + ((playback_sample - loc->end()) % loc->length()); - } else { - effective_start = playback_sample; - } - + Evoral::Range loop_range (loc->start(), loc->end() - 1); + effective_start = loop_range.squish (playback_sample); + DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("looped, effective start adjusted to %1\n", effective_start)); if (effective_start == loc->start()) { @@ -1458,33 +1462,35 @@ MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes) _playback_buf->resolve_tracker (dst, 0); } - _playback_buf->skip_to (effective_start); + /* for split-cycles we need to offset the events */ if (loc->end() >= effective_start && loc->end() < effective_start + nframes) { + /* end of loop is within the range we are reading, so split the read in two, and lie about the location for the 2nd read */ + framecnt_t first, second; first = loc->end() - effective_start; second = nframes - first; - DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read for eff %1 end %2: %3 and %4\n", - effective_start, loc->end(), first, second)); + DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read for eff %1 end %2: %3 and %4, cycle offset %5\n", + effective_start, loc->end(), first, second)); if (first) { DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #1, from %1 for %2\n", effective_start, first)); events_read = _playback_buf->read (dst, effective_start, first); - } + } if (second) { DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #2, from %1 for %2\n", loc->start(), second)); events_read += _playback_buf->read (dst, loc->start(), second); } - + } else { DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #3, adjusted start as %1 for %2\n", effective_start, nframes)); @@ -1492,6 +1498,7 @@ MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes) } } else { _playback_buf->skip_to (playback_sample); + DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("playback buffer read, from %1 to %2 (%3)", playback_sample, playback_sample + nframes, nframes)); events_read = _playback_buf->read (dst, playback_sample, playback_sample + nframes); } @@ -1502,6 +1509,10 @@ MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes) _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr())); g_atomic_int_add (&_frames_read_from_ringbuffer, nframes); + + //cerr << "======== POST ========\n"; + //_playback_buf->dump (cerr); + //cerr << "----------------\n"; } bool @@ -1535,7 +1546,7 @@ 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;