Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <fstream>
#include <cstdio>
#include <unistd.h>
#include <cmath>
#include "midi++/types.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
#include <locale.h>
using namespace std;
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;
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.
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);
* 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.
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<framepos_t>* loop_range (0);
MidiTrack* mt = dynamic_cast<MidiTrack*>(_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<framepos_t> (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;
}
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) {
} 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;
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;
}
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);
const framecnt_t total = g_atomic_int_get(const_cast<gint*> (&_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;
}
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<gint*> (&_frames_pending_write), -to_write);
}
_write_source->drop_references ();
_write_source.reset();
}
- }
+ }
}
} else {
disengage_record_enable ();
}
-
+
RecordEnableChanged (); /* EMIT SIGNAL */
}
}
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 */
}
}
bool const rolling = _session.transport_speed() != 0.0f;
boost::shared_ptr<MidiPort> sp = _source_port.lock ();
-
+
if (sp && Config->get_monitoring_model() == HardwareMonitoring) {
sp->request_input_monitoring (!(_session.config.get_auto_input() && rolling));
}
{
XMLNode& node (Diskstream::get_state());
char buf[64];
- LocaleGuard lg (X_("C"));
+ LocaleGuard lg;
if (_write_source && _session.get_record_enabled()) {
XMLNodeList nlist = node.children();
XMLNodeIterator niter;
XMLNode* capture_pending_node = 0;
- LocaleGuard lg (X_("C"));
+ LocaleGuard lg;
/* prevent write sources from being created */
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;
}
MidiDiskstream::ensure_input_monitoring (bool yn)
{
boost::shared_ptr<MidiPort> sp = _source_port.lock ();
-
+
if (sp) {
sp->ensure_input_monitoring (yn);
}
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.
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;
}
MidiDiskstream::capture_buffer_load () const
{
/* We don't report playback buffer load, so don't report capture load either */
-
+
return 1;
}
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<framepos_t> 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()) {
_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));
}
} 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);
}
_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
MidiDiskstream::get_gui_feed_buffer () const
{
boost::shared_ptr<MidiBuffer> 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;