X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_track.cc;h=f88c331c2ca8a0e7b171b7f1a4aea9a5f9ac7ba9;hb=d90e2b42211ead2a38afd5590e2937992312795e;hp=8847bf13bcbff96b35369960c479b7ffd319aa99;hpb=add52f1c0ef787a580c44d719bc6e4c9c5ae09a4;p=ardour.git diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 8847bf13bc..f88c331c2c 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -17,6 +17,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include // for ffs(3) + #include "pbd/enumwriter.h" #include "pbd/convert.h" #include "evoral/midi_util.h" @@ -55,6 +57,8 @@ MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mo , _note_mode(Sustained) , _step_editing (false) , _input_active (true) + , _playback_channel_mask(0x0000ffff) + , _capture_channel_mask(0x0000ffff) { } @@ -79,7 +83,7 @@ MidiTrack::create_diskstream () { MidiDiskstream::Flag dflags = MidiDiskstream::Flag (0); - if (_flags & Hidden) { + if (_flags & Auditioner) { dflags = MidiDiskstream::Flag (dflags | MidiDiskstream::Hidden); } else { dflags = MidiDiskstream::Flag (dflags | MidiDiskstream::Recordable); @@ -158,6 +162,38 @@ MidiTrack::set_state (const XMLNode& node, int version) set_input_active (string_is_affirmative (prop->value())); } + ChannelMode playback_channel_mode = AllChannels; + ChannelMode capture_channel_mode = AllChannels; + + if ((prop = node.property ("playback-channel-mode")) != 0) { + playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode)); + } + if ((prop = node.property ("capture-channel-mode")) != 0) { + capture_channel_mode = ChannelMode (string_2_enum(prop->value(), capture_channel_mode)); + } + if ((prop = node.property ("channel-mode")) != 0) { + /* 3.0 behaviour where capture and playback modes were not separated */ + playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode)); + capture_channel_mode = playback_channel_mode; + } + + unsigned int playback_channel_mask = 0xffff; + unsigned int capture_channel_mask = 0xffff; + + if ((prop = node.property ("playback-channel-mask")) != 0) { + sscanf (prop->value().c_str(), "0x%x", &playback_channel_mask); + } + if ((prop = node.property ("capture-channel-mask")) != 0) { + sscanf (prop->value().c_str(), "0x%x", &capture_channel_mask); + } + if ((prop = node.property ("channel-mask")) != 0) { + sscanf (prop->value().c_str(), "0x%x", &playback_channel_mask); + capture_channel_mask = playback_channel_mask; + } + + set_playback_channel_mode (playback_channel_mode, playback_channel_mask); + set_capture_channel_mode (capture_channel_mode, capture_channel_mask); + pending_state = const_cast (&node); if (_session.state_of_the_state() & Session::Loading) { @@ -196,10 +232,15 @@ MidiTrack::state(bool full_state) root.add_child_nocopy (*freeze_node); } - root.add_property (X_("note-mode"), enum_2_string (_note_mode)); + root.add_property("playback_channel-mode", enum_2_string(get_playback_channel_mode())); + root.add_property("capture_channel-mode", enum_2_string(get_capture_channel_mode())); + snprintf (buf, sizeof(buf), "0x%x", get_playback_channel_mask()); + root.add_property("playback-channel-mask", buf); + snprintf (buf, sizeof(buf), "0x%x", get_capture_channel_mask()); + root.add_property("capture-channel-mask", buf); - root.add_property ("step-editing", (_step_editing ? "yes" : "no")); root.add_property ("note-mode", enum_2_string (_note_mode)); + root.add_property ("step-editing", (_step_editing ? "yes" : "no")); root.add_property ("input-active", (_input_active ? "yes" : "no")); return root; @@ -276,21 +317,28 @@ MidiTrack::set_state_part_two () int MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler) { - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked()) { + boost::shared_ptr diskstream = midi_diskstream(); + framecnt_t playback_distance = diskstream->calculate_playback_distance(nframes); + if (can_internal_playback_seek(llabs(playback_distance))) { + /* TODO should declick, and/or note-off */ + internal_playback_seek(playback_distance); + } return 0; } boost::shared_ptr diskstream = midi_diskstream(); - automation_snapshot (start_frame); - if (n_outputs().n_total() == 0 && _processors.empty()) { return 0; } if (!_active) { silence (nframes); + if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + _meter->reset(); + } return 0; } @@ -304,24 +352,36 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame playback distance to zero, thus causing diskstream::commit to do nothing. */ - dret = diskstream->process (transport_frame, 0, playback_distance); + BufferSet bufs; /* empty set - is OK, since nothing will happen */ + + dret = diskstream->process (bufs, transport_frame, 0, playback_distance, false); need_butler = diskstream->commit (playback_distance); return dret; } + BufferSet& bufs = _session.get_route_buffers (n_process_buffers()); + + fill_buffers_with_input (bufs, _input, nframes); + + if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + _meter->run (bufs, start_frame, end_frame, nframes, true); + } + + /* filter captured data before the diskstream sees it */ + + filter_channels (bufs, get_capture_channel_mode(), get_capture_channel_mask()); + _silent = false; - if ((dret = diskstream->process (transport_frame, nframes, playback_distance)) != 0) { + if ((dret = diskstream->process (bufs, transport_frame, nframes, playback_distance, (monitoring_state() == MonitoringDisk))) != 0) { need_butler = diskstream->commit (playback_distance); silence (nframes); return dret; } - /* special condition applies */ - - if (_meter_point == MeterInput) { - _input->process_input (_meter, start_frame, end_frame, nframes); - } + /* filter playback data before we do anything else */ + + filter_channels (bufs, get_playback_channel_mode(), get_playback_channel_mask ()); if (monitoring_state() == MonitoringInput) { @@ -329,43 +389,25 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame at least potentially (depending on monitoring options) */ - passthru (start_frame, end_frame, nframes, 0); - - } else { - /* - XXX is it true that the earlier test on n_outputs() - means that we can avoid checking it again here? i think - so, because changing the i/o configuration of an IO - requires holding the AudioEngine lock, which we hold - while in the process() tree. - */ - - - /* copy the diskstream data to all output buffers */ - - //const size_t limit = n_process_buffers().n_audio(); - BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); - MidiBuffer& mbuf (bufs.get_midi (0)); - - /* we are a MIDI track, so we always start the chain with a single-channel diskstream */ - ChanCount c; - c.set_audio (0); - c.set_midi (1); - bufs.set_count (c); - - diskstream->get_playback (mbuf, nframes); - - /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */ + /* because the playback buffer is event based and not a + * continuous stream, we need to make sure that we empty + * it of events every cycle to avoid it filling up with events + * read from disk, while we are actually monitoring input + */ - write_out_of_band_data (bufs, start_frame, end_frame, nframes); + diskstream->flush_playback (start_frame, end_frame); - /* final argument: don't waste time with automation if we're recording or we've just stopped (yes it can happen) */ + } - process_output_buffers ( - bufs, start_frame, end_frame, nframes, - declick, (!diskstream->record_enabled() && !_session.transport_stopped()) - ); - } + + /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */ + + write_out_of_band_data (bufs, start_frame, end_frame, nframes); + + /* final argument: don't waste time with automation if we're not recording or rolling */ + + process_output_buffers (bufs, start_frame, end_frame, nframes, + declick, (!diskstream->record_enabled() && !_session.transport_stopped())); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr d = boost::dynamic_pointer_cast (*i); @@ -394,7 +436,7 @@ MidiTrack::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fr void MidiTrack::realtime_locate () { - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked ()) { return; @@ -410,7 +452,7 @@ MidiTrack::realtime_locate () void MidiTrack::realtime_handle_transport_stopped () { - Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); if (!lm.locked ()) { return; @@ -448,6 +490,43 @@ MidiTrack::push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes) } } +void +MidiTrack::filter_channels (BufferSet& bufs, ChannelMode mode, uint32_t mask) +{ + if (mode == AllChannels) { + return; + } + + MidiBuffer& buf (bufs.get_midi (0)); + + for (MidiBuffer::iterator e = buf.begin(); e != buf.end(); ) { + + Evoral::MIDIEvent ev(*e, false); + + if (ev.is_channel_event()) { + switch (mode) { + case FilterChannels: + if (0 == ((1<set_note_mode(m); } +std::string +MidiTrack::describe_parameter (Evoral::Parameter param) +{ + const std::string str(instrument_info().get_controller_name(param)); + return str.empty() ? Automatable::describe_parameter(param) : str; +} + void MidiTrack::midi_panic() { @@ -621,21 +707,53 @@ MidiTrack::write_source (uint32_t) } void -MidiTrack::set_channel_mode (ChannelMode mode, uint16_t mask) +MidiTrack::set_playback_channel_mode(ChannelMode mode, uint16_t mask) { - midi_diskstream()->set_channel_mode (mode, mask); + ChannelMode old = get_playback_channel_mode (); + uint16_t old_mask = get_playback_channel_mask (); + + if (old != mode || mask != old_mask) { + _set_playback_channel_mode (mode, mask); + PlaybackChannelModeChanged (); + _session.set_dirty (); + } } -ChannelMode -MidiTrack::get_channel_mode () +void +MidiTrack::set_capture_channel_mode(ChannelMode mode, uint16_t mask) { - return midi_diskstream()->get_channel_mode (); + ChannelMode old = get_capture_channel_mode (); + uint16_t old_mask = get_capture_channel_mask (); + + if (old != mode || mask != old_mask) { + _set_capture_channel_mode (mode, mask); + CaptureChannelModeChanged (); + _session.set_dirty (); + } +} + +void +MidiTrack::set_playback_channel_mask (uint16_t mask) +{ + uint16_t old = get_playback_channel_mask(); + + if (old != mask) { + _set_playback_channel_mask (mask); + PlaybackChannelMaskChanged (); + _session.set_dirty (); + } } -uint16_t -MidiTrack::get_channel_mask () +void +MidiTrack::set_capture_channel_mask (uint16_t mask) { - return midi_diskstream()->get_channel_mask (); + uint16_t old = get_capture_channel_mask(); + + if (old != mask) { + _set_capture_channel_mask (mask); + CaptureChannelMaskChanged (); + _session.set_dirty (); + } } boost::shared_ptr @@ -713,10 +831,17 @@ MidiTrack::act_on_mute () XXX we should should also stop all relevant note trackers. */ + /* If we haven't got a diskstream yet, there's nothing to worry about, + and we can't call get_channel_mask() anyway. + */ + if (!midi_diskstream()) { + return; + } + if (muted()) { /* only send messages for channels we are using */ - uint16_t mask = get_channel_mask(); + uint16_t mask = get_playback_channel_mask(); for (uint8_t channel = 0; channel <= 0xF; channel++) { @@ -735,37 +860,38 @@ MidiTrack::act_on_mute () void MidiTrack::set_monitoring (MonitorChoice mc) { - Track::set_monitoring (mc); + if (mc != _monitoring) { - boost::shared_ptr md (midi_diskstream()); + Track::set_monitoring (mc); + + /* monitoring state changed, so flush out any on notes at the + * port level. + */ - if (md) { - md->reset_tracker (); + PortSet& ports (_output->ports()); + + for (PortSet::iterator p = ports.begin(); p != ports.end(); ++p) { + boost::shared_ptr mp = boost::dynamic_pointer_cast (*p); + if (mp) { + mp->require_resolve (); + } + } + + boost::shared_ptr md (midi_diskstream()); + + if (md) { + md->reset_tracker (); + } } } MonitorState MidiTrack::monitoring_state () const { - /* Explicit requests */ - - if (_monitoring & MonitorInput) { + MonitorState ms = Track::monitoring_state(); + if (ms == MonitoringSilence) { return MonitoringInput; - } - - if (_monitoring & MonitorDisk) { - return MonitoringDisk; - } - - if (_session.transport_rolling()) { - return MonitoringDisk; } - - /* the return value here doesn't mean that we're actually monitoring - * input, let alone input *audio*. but it means that we are NOT - * monitoring silence. this allows us to still hear any audio generated - * by using internal generation techniques - */ - - return MonitoringInput; + return ms; } +