Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <fstream>
#include <cstdio>
#include <unistd.h>
#include <cmath>
}
if (nominally_recording || rec_nframes) {
-
- // Pump entire port buffer into the ring buffer (FIXME: split cycles?)
- MidiBuffer& buf = sp->get_midi_buffer(nframes);
- ChannelMode mode = AllChannels;
- uint32_t mask = 0xffff;
-
- MidiTrack * mt = dynamic_cast<MidiTrack*> (_track);
- if (mt) {
- mode = mt->get_capture_channel_mode ();
- mask = mt->get_capture_channel_mask ();
- }
+ // Pump entire port buffer into the ring buffer (TODO: split cycles?)
+ MidiBuffer& buf = sp->get_midi_buffer(nframes);
+ MidiTrack* mt = dynamic_cast<MidiTrack*>(_track);
+ MidiChannelFilter* filter = mt ? &mt->capture_filter() : NULL;
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false);
break;
}
#ifndef NDEBUG
- if (DEBUG::MidiIO & PBD::debug_bits) {
+ if (DEBUG_ENABLED(DEBUG::MidiIO)) {
const uint8_t* __data = ev.buffer();
DEBUG_STR_DECL(a);
DEBUG_STR_APPEND(a, string_compose ("mididiskstream %1 capture event @ %2 + %3 sz %4 ", this, ev.time(), transport_frame, ev.size()));
const framecnt_t loop_offset = _num_captured_loops * loop_length;
const framepos_t event_time = transport_frame + loop_offset - _accumulated_capture_offset + ev.time();
if (event_time < 0 || event_time < first_recordable_frame) {
+ /* Event out of range, skip */
continue;
}
- switch (mode) {
- case AllChannels:
- _capture_buf->write(event_time,
- ev.type(), ev.size(), ev.buffer());
- break;
- case FilterChannels:
- if (ev.is_channel_event()) {
- if ((1<<ev.channel()) & mask) {
- _capture_buf->write(event_time,
- ev.type(), ev.size(), ev.buffer());
- }
- } else {
- _capture_buf->write(event_time,
- ev.type(), ev.size(), ev.buffer());
- }
- break;
- case ForceChannel:
- if (ev.is_channel_event()) {
- ev.set_channel (PBD::ffs(mask) - 1);
- }
- _capture_buf->write(event_time,
- ev.type(), ev.size(), ev.buffer());
- break;
+
+ if (!filter || !filter->filter(ev.buffer(), ev.size())) {
+ _capture_buf->write(event_time, ev.type(), ev.size(), ev.buffer());
}
}
g_atomic_int_add(const_cast<gint*>(&_frames_pending_write), nframes);
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.
if (need_disk_signal) {
/* 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.
framecnt_t loop_length = 0;
Location* loc = 0;
+ MidiTrack* mt = dynamic_cast<MidiTrack*>(_track);
+ MidiChannelFilter* filter = mt ? &mt->playback_filter() : NULL;
+
if (!reversed) {
loc = loop_location;
this_read = min(dur,this_read);
- if (midi_playlist()->read (*_playback_buf, start, this_read) != this_read) {
+ if (midi_playlist()->read (*_playback_buf, start, this_read, 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) {
}
int
-MidiDiskstream::do_refill_with_alloc ()
+MidiDiskstream::_do_refill_with_alloc (bool /* partial_fill */)
{
return do_refill();
}
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);
}
void
MidiDiskstream::set_record_enabled (bool yn)
{
- if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_midi() == 0) {
+ if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_midi() == 0 || record_safe ()) {
return;
}
} else {
disengage_record_enable ();
}
-
+
RecordEnableChanged (); /* EMIT SIGNAL */
}
}
+void
+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 */
+ }
+}
+
bool
MidiDiskstream::prep_record_enable ()
{
- if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_midi() == 0) {
+ if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_midi() == 0 || record_safe ()) { // REQUIRES REVIEW "|| record_safe ()"
return false;
}
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));
}
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;
}
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,
+ _playback_buf->get_read_ptr(), _playback_buf->get_write_ptr(), playback_sample, playback_sample + nframes,
(loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes));
// cerr << "================\n";
// _playback_buf->dump (cerr);
// cerr << "----------------\n";
- size_t events_read = 0;
+ size_t events_read = 0;
+ const size_t split_cycle_offset = Port::port_offset ();
if (loc) {
framepos_t effective_start;
} else {
effective_start = playback_sample;
}
-
+
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("looped, effective start adjusted to %1\n", effective_start));
if (effective_start == loc->start()) {
beyond the loop end.
*/
- _playback_buf->resolve_tracker (dst, 0);
+ _playback_buf->resolve_tracker (dst, split_cycle_offset);
}
_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
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);
- }
+ events_read = _playback_buf->read (dst, effective_start, first, split_cycle_offset);
+ }
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);
+ events_read += _playback_buf->read (dst, loc->start(), second, split_cycle_offset);
}
-
+
} else {
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("loop read #3, adjusted start as %1 for %2\n",
effective_start, nframes));
- events_read = _playback_buf->read (dst, effective_start, effective_start + nframes);
+ events_read = _playback_buf->read (dst, effective_start, effective_start + nframes, split_cycle_offset);
}
} else {
_playback_buf->skip_to (playback_sample);
- events_read = _playback_buf->read (dst, playback_sample, playback_sample + nframes);
+ events_read = _playback_buf->read (dst, playback_sample, playback_sample + nframes, split_cycle_offset);
}
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
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;
}
}
+void
+MidiDiskstream::resolve_tracker (Evoral::EventSink<framepos_t>& buffer, framepos_t time)
+{
+ _playback_buf->resolve_tracker(buffer, time);
+
+ boost::shared_ptr<MidiPlaylist> mp (midi_playlist());
+
+ if (mp) {
+ mp->reset_note_trackers ();
+ }
+}
+
+
boost::shared_ptr<MidiPlaylist>
MidiDiskstream::midi_playlist ()
{