X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_diskstream.cc;h=d27c2fa9c75f44b3c49f386c196f3f24ebfded45;hb=453ed61c4a23551c7ffeee5d972b29d6d92c1591;hp=c151b776c2fd34fdb1ec5f380bab5a75e732be45;hpb=d89f209f4a8e94c06de2895ff6fd1830c0e5419d;p=ardour.git diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index c151b776c2..d27c2fa9c7 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -27,9 +27,9 @@ #include #include #include -#include #include "pbd/error.h" +#include "pbd/ffs.h" #include "pbd/basename.h" #include #include "pbd/xml++.h" @@ -73,6 +73,8 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F , _note_mode(Sustained) , _frames_written_to_ringbuffer(0) , _frames_read_from_ringbuffer(0) + , _frames_pending_write(0) + , _num_captured_loops(0) , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) { in_set_state = true; @@ -83,7 +85,9 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F in_set_state = false; - assert(!destructive()); + if (destructive()) { + throw failed_constructor(); + } } MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node) @@ -93,6 +97,8 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node) , _note_mode(Sustained) , _frames_written_to_ringbuffer(0) , _frames_read_from_ringbuffer(0) + , _frames_pending_write(0) + , _num_captured_loops(0) , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI)) { in_set_state = true; @@ -125,8 +131,6 @@ MidiDiskstream::init () _capture_buf = new MidiRingBuffer(size); _n_channels = ChanCount(DataType::MIDI, 1); - - assert(recordable()); } MidiDiskstream::~MidiDiskstream () @@ -197,9 +201,8 @@ MidiDiskstream::non_realtime_input_change () seek (_session.transport_frame()); } - if (_write_source) { - _write_source->set_last_write_end (_session.transport_frame()); - } + g_atomic_int_set(&_frames_pending_write, 0); + g_atomic_int_set(&_num_captured_loops, 0); } int @@ -212,7 +215,7 @@ MidiDiskstream::find_and_use_playlist (const string& name) } if (!playlist) { - error << string_compose(_("MidiDiskstream: Playlist \"%1\" isn't an midi playlist"), name) << endmsg; + error << string_compose(_("MidiDiskstream: Playlist \"%1\" isn't a midi playlist"), name) << endmsg; return -1; } @@ -222,9 +225,9 @@ MidiDiskstream::find_and_use_playlist (const string& name) int MidiDiskstream::use_playlist (boost::shared_ptr playlist) { - assert(boost::dynamic_pointer_cast(playlist)); - - Diskstream::use_playlist(playlist); + if (boost::dynamic_pointer_cast(playlist)) { + Diskstream::use_playlist(playlist); + } return 0; } @@ -258,8 +261,6 @@ MidiDiskstream::use_new_playlist () int MidiDiskstream::use_copy_playlist () { - assert(midi_playlist()); - if (destructive()) { return 0; } @@ -286,10 +287,7 @@ MidiDiskstream::use_copy_playlist () int MidiDiskstream::set_destructive (bool yn) { - yn = 0; // stop pedantic gcc complaints about unused parameter - assert( ! destructive()); - assert( ! yn); - return -1; + return yn ? -1 : 0; } void @@ -301,8 +299,28 @@ MidiDiskstream::set_note_mode (NoteMode m) _write_source->model()->set_note_mode(m); } +/** Get the start, end, and length of a location "atomically". + * + * Note: Locations don't get deleted, so all we care about when I say "atomic" + * is that we are always pointing to the same one and using start/length values + * obtained just once. Use this function to achieve this since location being + * a parameter achieves this. + */ +static void +get_location_times(const Location* location, + framepos_t* start, + framepos_t* end, + framepos_t* length) +{ + if (location) { + *start = location->start(); + *end = location->end(); + *length = *end - *start; + } +} + int -MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance) +MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal) { framecnt_t rec_offset = 0; framecnt_t rec_nframes = 0; @@ -332,6 +350,12 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt return 1; } + const Location* const loop_loc = loop_location; + 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); + adjust_capture_position = 0; if (nominally_recording || (re && was_recording && _session.get_record_enabled() && _session.config.get_punch_in())) { @@ -340,8 +364,19 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset); if (rec_nframes && !was_recording) { - _write_source->mark_write_starting_now (); - capture_captured = 0; + if (loop_loc) { + /* Loop recording, so pretend the capture started at the loop + start rgardless of what time it is now, so the source starts + at the loop start and can handle time wrapping around. + Otherwise, start the source right now as usual. + */ + capture_captured = transport_frame - loop_start; + capture_start_frame = loop_start; + } + _write_source->mark_write_starting_now( + capture_start_frame, capture_captured, loop_length); + g_atomic_int_set(&_frames_pending_write, 0); + g_atomic_int_set(&_num_captured_loops, 0); was_recording = true; } } @@ -354,9 +389,11 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt // Pump entire port buffer into the ring buffer (FIXME: split cycles?) MidiBuffer& buf = sp->get_midi_buffer(nframes); + ChannelMode mode = AllChannels; // _track->get_capture_channel_mode (); + uint32_t mask = 0xffff; // _track->get_capture_channel_mask (); + for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { - const Evoral::MIDIEvent ev(*i, false); - assert(ev.buffer()); + Evoral::MIDIEvent ev(*i, false); #ifndef NDEBUG if (DEBUG::MidiIO & PBD::debug_bits) { const uint8_t* __data = ev.buffer(); @@ -372,8 +409,43 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str()); } #endif - _capture_buf->write(ev.time() + transport_frame, ev.type(), ev.size(), ev.buffer()); + /* Write events to the capture buffer in frames from session start, + but ignoring looping so event time progresses monotonically. + The source knows the loop length so it knows exactly where the + event occurs in the series of recorded loops and can implement + any desirable behaviour. We don't want to send event with + transport time here since that way the source can not + reconstruct their actual time; future clever MIDI looping should + probabl be implemented in the source instead of here. + */ + const framecnt_t loop_offset = _num_captured_loops * loop_length; + + switch (mode) { + case AllChannels: + _capture_buf->write(transport_frame + loop_offset + ev.time(), + ev.type(), ev.size(), ev.buffer()); + break; + case FilterChannels: + if (ev.is_channel_event()) { + if ((1<write(transport_frame + loop_offset + ev.time(), + ev.type(), ev.size(), ev.buffer()); + } + } else { + _capture_buf->write(transport_frame + loop_offset + ev.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(transport_frame + loop_offset + ev.time(), + ev.type(), ev.size(), ev.buffer()); + break; + } } + g_atomic_int_add(&_frames_pending_write, nframes); if (buf.size() != 0) { Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK); @@ -429,6 +501,18 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt } + if (need_disk_signal) { + /* copy the diskstream data to all output buffers */ + + MidiBuffer& mbuf (bufs.get_midi (0)); + get_playback (mbuf, nframes); + + /* leave the audio count alone */ + ChanCount cnt (DataType::MIDI, 1); + cnt.set (DataType::AUDIO, bufs.count().n_audio()); + bufs.set_count (cnt); + } + return 0; } @@ -448,8 +532,8 @@ MidiDiskstream::commit (framecnt_t playback_distance) adjust_capture_position = 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); + uint32_t frames_read = g_atomic_int_get(const_cast(&_frames_read_from_ringbuffer)); + uint32_t frames_written = g_atomic_int_get(const_cast(&_frames_written_to_ringbuffer)); /* cerr << name() << " MDS written: " << frames_written << " - read: " << frames_read << @@ -552,29 +636,17 @@ MidiDiskstream::internal_playback_seek (framecnt_t distance) 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; - Location *loc = 0; + 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; if (!reversed) { - framecnt_t loop_length = 0; - - /* Make the use of a Location atomic for this read operation. - - Note: Locations don't get deleted, so all we care about - when I say "atomic" is that we are always pointing to - the same one and using a start/length values obtained - just once. - */ - - if ((loc = loop_location) != 0) { - loop_start = loc->start(); - loop_end = loc->end(); - loop_length = loop_end - loop_start; - } + 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. @@ -667,12 +739,11 @@ MidiDiskstream::do_refill () return 0; } - // At this point we... - assert(_playback_buf->write_space() > 0); // ... have something to write to, and - assert(file_frame <= max_framepos); // ... something to write + /* no space to write */ + if (_playback_buf->write_space() == 0) { + return 0; + } - // now calculate how much time is in the ringbuffer. - // and lets write as much as we need to get this to be midi_readahead; 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) >= midi_readahead) { @@ -707,16 +778,13 @@ int MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush) { framecnt_t to_write; - framecnt_t total; int32_t ret = 0; if (!_write_source) { return 0; } - assert (!destructive()); - - total = _session.transport_frame() - _write_source->last_write_end(); + const framecnt_t total = g_atomic_int_get(&_frames_pending_write); if (total == 0 || _capture_buf->read_space() == 0 || @@ -751,6 +819,7 @@ MidiDiskstream::do_flush (RunContext /*context*/, bool force_flush) error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg; return -1; } + g_atomic_int_add(&_frames_pending_write, -to_write); } out: @@ -805,8 +874,6 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen } else { - assert(_write_source); - framecnt_t total_capture = 0; for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { total_capture += (*ci)->frames; @@ -952,35 +1019,17 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen } void -MidiDiskstream::transport_looped (framepos_t transport_frame) +MidiDiskstream::transport_looped (framepos_t) { + /* Here we only keep track of the number of captured loops so monotonic + event times can be delivered to the write source in process(). Trying + to be clever here is a world of trouble, it is better to simply record + the input in a straightforward non-destructive way. In the future when + we want to implement more clever MIDI looping modes it should be done in + the Source and/or entirely after the capture is finished. + */ if (was_recording) { - - // adjust the capture length knowing that the data will be recorded to disk - // only necessary after the first loop where we're recording - if (capture_info.size() == 0) { - capture_captured += _capture_offset; - - if (_alignment_style == ExistingMaterial) { - capture_captured += _session.worst_output_latency(); - } else { - capture_captured += _roll_delay; - } - } - - finish_capture (); - - // the next region will start recording via the normal mechanism - // we'll set the start position to the current transport pos - // no latency adjustment or capture offset needs to be made, as that already happened the first time - capture_start_frame = transport_frame; - first_recordable_frame = transport_frame; // mild lie - last_recordable_frame = max_framepos; - was_recording = true; - } - - if (!Config->get_seamless_loop()) { - reset_tracker (); + g_atomic_int_add(&_num_captured_loops, 1); } } @@ -993,9 +1042,6 @@ MidiDiskstream::finish_capture () return; } - // Why must we destroy? - assert(!destructive()); - CaptureInfo* ci = new CaptureInfo; ci->start = capture_start_frame; @@ -1023,8 +1069,6 @@ MidiDiskstream::set_record_enabled (bool yn) return; } - assert(!destructive()); - /* yes, i know that this not proof against race conditions, but its good enough. i think. */ @@ -1072,10 +1116,6 @@ MidiDiskstream::get_state () char buf[64]; LocaleGuard lg (X_("POSIX")); - node.add_property("channel-mode", enum_2_string(get_channel_mode())); - snprintf (buf, sizeof(buf), "0x%x", get_channel_mask()); - node.add_property("channel-mask", buf); - if (_write_source && _session.get_record_enabled()) { XMLNode* cs_child = new XMLNode (X_("CapturingSources")); @@ -1105,7 +1145,6 @@ MidiDiskstream::get_state () int MidiDiskstream::set_state (const XMLNode& node, int version) { - const XMLProperty* prop; XMLNodeList nlist = node.children(); XMLNodeIterator niter; XMLNode* capture_pending_node = 0; @@ -1116,8 +1155,6 @@ MidiDiskstream::set_state (const XMLNode& node, int version) in_set_state = true; for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - assert ((*niter)->name() != IO::state_node_name); - if ((*niter)->name() == X_("CapturingSources")) { capture_pending_node = *niter; } @@ -1127,26 +1164,10 @@ MidiDiskstream::set_state (const XMLNode& node, int version) return -1; } - ChannelMode channel_mode = AllChannels; - if ((prop = node.property ("channel-mode")) != 0) { - channel_mode = ChannelMode (string_2_enum(prop->value(), channel_mode)); - } - - unsigned int channel_mask = 0xFFFF; - if ((prop = node.property ("channel-mask")) != 0) { - sscanf (prop->value().c_str(), "0x%x", &channel_mask); - if (channel_mask & (~0xFFFF)) { - warning << _("MidiDiskstream: XML property channel-mask out of range") << endmsg; - } - } - - if (capture_pending_node) { use_pending_capture_data (*capture_pending_node); } - set_channel_mode (channel_mode, channel_mask); - in_set_state = false; return 0; @@ -1159,8 +1180,6 @@ MidiDiskstream::use_new_write_source (uint32_t n) return 1; } - assert(n == 0); - _write_source.reset(); try { @@ -1293,7 +1312,6 @@ void MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes) { dst.clear(); - assert(dst.size() == 0); Location* loc = loop_location;