X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_source.cc;h=d330eb2bcd766f67f0c334f7adc8b63c577c00d2;hb=ed72df29b79f9e2dc7482f07c39010b4523c4a8e;hp=dbc41c8ab63a6331bc8fb6980e3e0cc0595960d6;hpb=593b421180290f46f39efcb21ed8192b624bbc73;p=ardour.git diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index dbc41c8ab6..d330eb2bcd 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -102,7 +102,13 @@ MidiSource::get_state () child->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first)); child->add_property (X_("style"), enum_2_string (i->second)); } - + + for (AutomationStateMap::const_iterator i = _automation_state.begin(); i != _automation_state.end(); ++i) { + XMLNode* child = node.add_child (X_("AutomationState")); + child->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first)); + child->add_property (X_("state"), enum_2_string (i->second)); + } + return node; } @@ -134,6 +140,25 @@ MidiSource::set_state (const XMLNode& node, int /*version*/) Evoral::ControlList::InterpolationStyle s = static_cast (string_2_enum (prop->value(), s)); set_interpolation_of (p, s); + + } else if ((*i)->name() == X_("AutomationState")) { + + XMLProperty* prop; + + if ((prop = (*i)->property (X_("parameter"))) == 0) { + error << _("Missing parameter property on AutomationState") << endmsg; + return -1; + } + + Evoral::Parameter p = EventTypeMap::instance().new_parameter (prop->value()); + + if ((prop = (*i)->property (X_("state"))) == 0) { + error << _("Missing state property on AutomationState") << endmsg; + return -1; + } + + AutoState s = static_cast (string_2_enum (prop->value(), s)); + set_automation_state_of (p, s); } } @@ -143,22 +168,22 @@ MidiSource::set_state (const XMLNode& node, int /*version*/) bool MidiSource::empty () const { - return _length_beats == 0; + return _length_beats == 0; } framecnt_t MidiSource::length (framepos_t pos) const { - if (_length_beats == 0) { - return 0; - } + if (_length_beats == 0) { + return 0; + } BeatsFramesConverter converter(_session.tempo_map(), pos); return converter.to(_length_beats); } void -MidiSource::update_length (sframes_t /*pos*/, sframes_t /*cnt*/) +MidiSource::update_length (framepos_t /*pos*/, framecnt_t /*cnt*/) { // You're not the boss of me! } @@ -171,12 +196,11 @@ MidiSource::invalidate () } /** @param filtered A set of parameters whose MIDI messages will not be returned */ -nframes_t -MidiSource::midi_read (Evoral::EventSink& dst, sframes_t source_start, - sframes_t start, nframes_t cnt, - sframes_t stamp_offset, sframes_t negative_stamp_offset, +framecnt_t +MidiSource::midi_read (Evoral::EventSink& dst, framepos_t source_start, + framepos_t start, framecnt_t cnt, MidiStateTracker* tracker, - std::set const & filtered) const + std::set const & filtered) const { Glib::Mutex::Lock lm (_lock); @@ -202,14 +226,15 @@ MidiSource::midi_read (Evoral::EventSink& dst, sframes_t source_start // Read events up to end for (; i != _model->end(); ++i) { - const sframes_t time_frames = converter.to(i->time()); + const framecnt_t time_frames = converter.to(i->time()); if (time_frames < start + cnt) { - dst.write(time_frames + stamp_offset - negative_stamp_offset, - i->event_type(), i->size(), i->buffer()); + /* convert event times to session frames by adding on the source start position in session frames */ + dst.write (time_frames + source_start, i->event_type(), i->size(), i->buffer()); + if (tracker) { Evoral::MIDIEvent& ev (*(Evoral::MIDIEvent*) (&(*i))); if (ev.is_note_on()) { - DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("\t%1 add note on %2 @ %3\n", _name, ev.note(), time_frames)); + DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("\t%1 add note on %2 @ %3 velocity %4\n", _name, ev.note(), time_frames, (int) ev.velocity())); tracker->add (ev.note(), ev.channel()); } else if (ev.is_note_off()) { DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("\t%1 add note off %2 @ %3\n", _name, ev.note(), time_frames)); @@ -222,21 +247,25 @@ MidiSource::midi_read (Evoral::EventSink& dst, sframes_t source_start } return cnt; } else { - return read_unlocked (dst, source_start, start, cnt, stamp_offset, negative_stamp_offset, tracker); + return read_unlocked (dst, source_start, start, cnt, tracker); } } -nframes_t -MidiSource::midi_write (MidiRingBuffer& source, sframes_t source_start, nframes_t duration) +/** Write data from a MidiRingBuffer to this source. + * @param source Source to read from. + * @param source_start This source's start position in session frames. + */ +framecnt_t +MidiSource::midi_write (MidiRingBuffer& source, framepos_t source_start, framecnt_t duration) { Glib::Mutex::Lock lm (_lock); - const nframes_t ret = write_unlocked (source, source_start, duration); + const framecnt_t ret = write_unlocked (source, source_start, duration); _last_write_end += duration; return ret; } void -MidiSource::mark_streaming_midi_write_started (NoteMode mode, sframes_t start_frame) +MidiSource::mark_streaming_midi_write_started (NoteMode mode, framepos_t start_frame) { set_timeline_position(start_frame); @@ -269,87 +298,84 @@ MidiSource::mark_streaming_write_completed () boost::shared_ptr MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end) { - string newname = PBD::basename_nosuffix(_name.val()); - string newpath; + string newname = PBD::basename_nosuffix(_name.val()); + string newpath; - /* get a new name for the MIDI file we're going to write to - */ + /* get a new name for the MIDI file we're going to write to + */ - do { + do { - newname = bump_name_once (newname, '-'); - /* XXX build path safely */ - newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid"; + newname = bump_name_once (newname, '-'); + /* XXX build path safely */ + newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid"; - } while (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)); + } while (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)); - boost::shared_ptr newsrc = boost::dynamic_pointer_cast( - SourceFactory::createWritable(DataType::MIDI, _session, - newpath, false, _session.frame_rate())); - - newsrc->set_timeline_position(_timeline_position); + boost::shared_ptr newsrc = boost::dynamic_pointer_cast( + SourceFactory::createWritable(DataType::MIDI, _session, + newpath, string(), false, _session.frame_rate())); + + newsrc->set_timeline_position(_timeline_position); newsrc->copy_interpolation_from (this); + newsrc->copy_automation_state_from (this); - if (_model) { - if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) { - _model->write_to (newsrc); - } else { - _model->write_section_to (newsrc, begin, end); - } - } else { - error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()")); - return boost::shared_ptr(); - } + if (_model) { + if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) { + _model->write_to (newsrc); + } else { + _model->write_section_to (newsrc, begin, end); + } + } else { + error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()")); + return boost::shared_ptr(); + } - newsrc->flush_midi(); + newsrc->flush_midi(); - /* force a reload of the model if the range is partial */ + /* force a reload of the model if the range is partial */ - if (begin != Evoral::MinMusicalTime || end != Evoral::MaxMusicalTime) { - newsrc->load_model (true, true); - } else { + if (begin != Evoral::MinMusicalTime || end != Evoral::MaxMusicalTime) { + newsrc->load_model (true, true); + } else { newsrc->set_model (_model); } - return newsrc; + return newsrc; } void MidiSource::session_saved() { - /* this writes a copy of the data to disk. - XXX do we need to do this every time? - */ - - flush_midi(); + /* this writes a copy of the data to disk. + XXX do we need to do this every time? + */ if (_model && _model->edited()) { -#if 0 // old style: clone the source if necessary on every session save - // and switch to the new source - boost::shared_ptr newsrc = clone (); - - if (newsrc) { - _model->set_midi_source (newsrc); - Switched (newsrc); /* EMIT SIGNAL */ - } -#else - // new style: if the model is edited, write its contents into - // the current source file (overwiting previous contents. - - /* temporarily drop our reference to the model so that - as the model pushes its current state to us, we don't - try to update it. - */ - - boost::shared_ptr mm = _model ; - _model.reset (); - mm->sync_to_source (); - _model = mm; - /* data is in the file now, its not removable */ -#endif - } - - cerr << name() << " @ " << this << " length at save = " << _length_beats << endl; + + // if the model is edited, write its contents into + // the current source file (overwiting previous contents. + + /* temporarily drop our reference to the model so that + as the model pushes its current state to us, we don't + try to update it. + */ + + boost::shared_ptr mm = _model ; + _model.reset (); + + /* flush model contents to disk + */ + + mm->sync_to_source (); + + /* reacquire model */ + + _model = mm; + + } else { + flush_midi(); + } } void @@ -363,7 +389,7 @@ MidiSource::set_note_mode(NoteMode mode) void MidiSource::drop_model () { - _model.reset(); + _model.reset(); ModelChanged (); /* EMIT SIGNAL */ } @@ -386,6 +412,21 @@ MidiSource::interpolation_of (Evoral::Parameter p) const return i->second; } +AutoState +MidiSource::automation_state_of (Evoral::Parameter p) const +{ + AutomationStateMap::const_iterator i = _automation_state.find (p); + if (i == _automation_state.end()) { + /* default to `play', otherwise if MIDI is recorded / + imported with controllers etc. they are by default + not played back, which is a little surprising. + */ + return Play; + } + + return i->second; +} + /** Set interpolation style to be used for a given parameter. This change will be * propagated to anyone who needs to know. */ @@ -406,12 +447,35 @@ MidiSource::set_interpolation_of (Evoral::Parameter p, Evoral::ControlList::Inte InterpolationChanged (p, s); /* EMIT SIGNAL */ } +void +MidiSource::set_automation_state_of (Evoral::Parameter p, AutoState s) +{ + if (automation_state_of (p) == s) { + return; + } + + if (s == Play) { + /* automation state is being set to the default, so we don't need a note in our map */ + _automation_state.erase (p); + } else { + _automation_state[p] = s; + } + + AutomationStateChanged (p, s); /* EMIT SIGNAL */ +} + void MidiSource::copy_interpolation_from (boost::shared_ptr s) { copy_interpolation_from (s.get ()); } +void +MidiSource::copy_automation_state_from (boost::shared_ptr s) +{ + copy_automation_state_from (s.get ()); +} + void MidiSource::copy_interpolation_from (MidiSource* s) { @@ -419,3 +483,11 @@ MidiSource::copy_interpolation_from (MidiSource* s) /* XXX: should probably emit signals here */ } + +void +MidiSource::copy_automation_state_from (MidiSource* s) +{ + _automation_state = s->_automation_state; + + /* XXX: should probably emit signals here */ +}