2 Copyright (C) 2006 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #include <glibmm/fileutils.h>
31 #include <glibmm/miscutils.h>
33 #include "pbd/xml++.h"
34 #include "pbd/pthread_utils.h"
35 #include "pbd/basename.h"
37 #include "evoral/Control.hpp"
38 #include "evoral/EventSink.hpp"
40 #include "ardour/debug.h"
41 #include "ardour/file_source.h"
42 #include "ardour/midi_channel_filter.h"
43 #include "ardour/midi_cursor.h"
44 #include "ardour/midi_model.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/midi_state_tracker.h"
47 #include "ardour/session.h"
48 #include "ardour/session_directory.h"
49 #include "ardour/source_factory.h"
50 #include "ardour/tempo.h"
54 namespace ARDOUR { template <typename T> class MidiRingBuffer; }
57 using namespace ARDOUR;
60 MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
61 : Source(s, DataType::MIDI, name, flags)
65 , _capture_loop_length(0)
69 MidiSource::MidiSource (Session& s, const XMLNode& node)
74 , _capture_loop_length(0)
76 if (set_state (node, Stateful::loading_state_version)) {
77 throw failed_constructor();
81 MidiSource::~MidiSource ()
83 /* invalidate any existing iterators */
88 MidiSource::get_state ()
90 XMLNode& node (Source::get_state());
92 if (_captured_for.length()) {
93 node.add_property ("captured-for", _captured_for);
96 for (InterpolationStyleMap::const_iterator i = _interpolation_style.begin(); i != _interpolation_style.end(); ++i) {
97 XMLNode* child = node.add_child (X_("InterpolationStyle"));
98 child->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
99 child->add_property (X_("style"), enum_2_string (i->second));
102 for (AutomationStateMap::const_iterator i = _automation_state.begin(); i != _automation_state.end(); ++i) {
103 XMLNode* child = node.add_child (X_("AutomationState"));
104 child->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
105 child->add_property (X_("state"), enum_2_string (i->second));
112 MidiSource::set_state (const XMLNode& node, int /*version*/)
114 XMLProperty const * prop;
115 if ((prop = node.property ("captured-for")) != 0) {
116 _captured_for = prop->value();
119 XMLNodeList children = node.children ();
120 for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
121 if ((*i)->name() == X_("InterpolationStyle")) {
122 if ((prop = (*i)->property (X_("parameter"))) == 0) {
123 error << _("Missing parameter property on InterpolationStyle") << endmsg;
126 Evoral::Parameter p = EventTypeMap::instance().from_symbol (prop->value());
128 if ((prop = (*i)->property (X_("style"))) == 0) {
129 error << _("Missing style property on InterpolationStyle") << endmsg;
132 Evoral::ControlList::InterpolationStyle s = static_cast<Evoral::ControlList::InterpolationStyle>(
133 string_2_enum (prop->value(), s));
134 set_interpolation_of (p, s);
136 } else if ((*i)->name() == X_("AutomationState")) {
137 if ((prop = (*i)->property (X_("parameter"))) == 0) {
138 error << _("Missing parameter property on AutomationState") << endmsg;
141 Evoral::Parameter p = EventTypeMap::instance().from_symbol (prop->value());
143 if ((prop = (*i)->property (X_("state"))) == 0) {
144 error << _("Missing state property on AutomationState") << endmsg;
147 AutoState s = static_cast<AutoState> (string_2_enum (prop->value(), s));
148 set_automation_state_of (p, s);
156 MidiSource::empty () const
158 return !_length_beats;
162 MidiSource::length (framepos_t pos) const
164 if (!_length_beats) {
168 BeatsFramesConverter converter(_session.tempo_map(), pos);
169 return converter.to(_length_beats);
173 MidiSource::update_length (framecnt_t)
175 // You're not the boss of me!
179 MidiSource::invalidate (const Lock& lock)
181 Invalidated(_session.transport_rolling());
185 MidiSource::midi_read (const Lock& lm,
186 Evoral::EventSink<framepos_t>& dst,
187 framepos_t source_start,
190 Evoral::Range<framepos_t>* loop_range,
192 MidiStateTracker* tracker,
193 MidiChannelFilter* filter,
194 const std::set<Evoral::Parameter>& filtered,
195 const double pos_beats,
196 const double start_beats) const
198 BeatsFramesConverter converter(_session.tempo_map(), source_start);
200 const double start_qn = pos_beats - start_beats;
202 DEBUG_TRACE (DEBUG::MidiSourceIO,
203 string_compose ("MidiSource::midi_read() %5 sstart %1 start %2 cnt %3 tracker %4\n",
204 source_start, start, cnt, tracker, name()));
207 return read_unlocked (lm, dst, source_start, start, cnt, loop_range, tracker, filter);
210 // Find appropriate model iterator
211 Evoral::Sequence<Evoral::Beats>::const_iterator& i = cursor.iter;
212 const bool linear_read = cursor.last_read_end != 0 && start == cursor.last_read_end;
213 if (!linear_read || !i.valid()) {
214 /* Cached iterator is invalid, search for the first event past start.
215 Note that multiple tracks can use a MidiSource simultaneously, so
216 all playback state must be in parameters (the cursor) and must not
217 be cached in the source of model itself.
218 See http://tracker.ardour.org/view.php?id=6541
220 cursor.connect(Invalidated);
221 cursor.iter = _model->begin(converter.from(start), false, filtered, &cursor.active_notes);
222 cursor.active_notes.clear();
225 cursor.last_read_end = start + cnt;
227 // Copy events in [start, start + cnt) into dst
228 for (; i != _model->end(); ++i) {
230 // Offset by source start to convert event time to session time
232 framepos_t time_frames = _session.tempo_map().frame_at_quarter_note (i->time().to_double() + start_qn);
234 if (time_frames < start + source_start) {
235 /* event too early */
239 } else if (time_frames >= start + cnt + source_start) {
241 DEBUG_TRACE (DEBUG::MidiSourceIO,
242 string_compose ("%1: reached end with event @ %2 vs. %3\n",
243 _name, time_frames, start+cnt));
251 time_frames = loop_range->squish (time_frames);
254 const uint8_t status = i->buffer()[0];
255 const bool is_channel_event = (0x80 <= (status & 0xF0)) && (status <= 0xE0);
256 if (filter && is_channel_event) {
257 /* Copy event so the filter can modify the channel. I'm not
258 sure if this is necessary here (channels are mapped later in
259 buffers anyway), but it preserves existing behaviour without
260 destroying events in the model during read. */
261 Evoral::Event<Evoral::Beats> ev(*i, true);
262 if (!filter->filter(ev.buffer(), ev.size())) {
263 dst.write(time_frames, ev.event_type(), ev.size(), ev.buffer());
265 DEBUG_TRACE (DEBUG::MidiSourceIO,
266 string_compose ("%1: filter event @ %2 type %3 size %4\n",
267 _name, time_frames, i->event_type(), i->size()));
270 dst.write (time_frames, i->event_type(), i->size(), i->buffer());
274 if (DEBUG_ENABLED(DEBUG::MidiSourceIO)) {
276 DEBUG_STR_APPEND(a, string_compose ("%1 added event @ %2 sz %3 within %4 .. %5 ",
277 _name, time_frames, i->size(),
278 start + source_start, start + cnt + source_start));
279 for (size_t n=0; n < i->size(); ++n) {
280 DEBUG_STR_APPEND(a,hex);
281 DEBUG_STR_APPEND(a,"0x");
282 DEBUG_STR_APPEND(a,(int)i->buffer()[n]);
283 DEBUG_STR_APPEND(a,' ');
285 DEBUG_STR_APPEND(a,'\n');
286 DEBUG_TRACE (DEBUG::MidiSourceIO, DEBUG_STR(a).str());
300 MidiSource::midi_write (const Lock& lm,
301 MidiRingBuffer<framepos_t>& source,
302 framepos_t source_start,
305 const framecnt_t ret = write_unlocked (lm, source, source_start, cnt);
307 if (cnt == max_framecnt) {
310 _capture_length += cnt;
317 MidiSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
320 _model->set_note_mode (mode);
321 _model->start_write ();
328 MidiSource::mark_write_starting_now (framecnt_t position,
329 framecnt_t capture_length,
330 framecnt_t loop_length)
332 /* I'm not sure if this is the best way to approach this, but
333 _capture_length needs to be set up with the transport frame
334 when a record actually starts, as it is used by
335 SMFSource::write_unlocked to decide whether incoming notes
336 are within the correct time range.
337 mark_streaming_midi_write_started (perhaps a more logical
338 place to do this) is not called at exactly the time when
339 record starts, and I don't think it necessarily can be
340 because it is not RT-safe.
343 set_timeline_position(position);
344 _capture_length = capture_length;
345 _capture_loop_length = loop_length;
347 TempoMap& map (_session.tempo_map());
348 BeatsFramesConverter converter(map, position);
349 _length_beats = converter.from(capture_length);
353 MidiSource::mark_streaming_write_started (const Lock& lock)
355 NoteMode note_mode = _model ? _model->note_mode() : Sustained;
356 mark_streaming_midi_write_started (lock, note_mode);
360 MidiSource::mark_midi_streaming_write_completed (const Lock& lock,
361 Evoral::Sequence<Evoral::Beats>::StuckNoteOption option,
365 _model->end_write (option, end);
367 /* Make captured controls discrete to play back user input exactly. */
368 for (MidiModel::Controls::iterator i = _model->controls().begin(); i != _model->controls().end(); ++i) {
369 if (i->second->list()) {
370 i->second->list()->set_interpolation(Evoral::ControlList::Discrete);
371 _interpolation_style.insert(std::make_pair(i->second->parameter(), Evoral::ControlList::Discrete));
381 MidiSource::mark_streaming_write_completed (const Lock& lock)
383 mark_midi_streaming_write_completed (lock, Evoral::Sequence<Evoral::Beats>::DeleteStuckNotes);
387 MidiSource::export_write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Evoral::Beats begin, Evoral::Beats end)
389 Lock newsrc_lock (newsrc->mutex ());
392 error << string_compose (_("programming error: %1"), X_("no model for MidiSource during export"));
396 _model->write_section_to (newsrc, newsrc_lock, begin, end, true);
398 newsrc->flush_midi(newsrc_lock);
404 MidiSource::write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Evoral::Beats begin, Evoral::Beats end)
406 Lock newsrc_lock (newsrc->mutex ());
408 newsrc->set_timeline_position (_timeline_position);
409 newsrc->copy_interpolation_from (this);
410 newsrc->copy_automation_state_from (this);
413 if (begin == Evoral::MinBeats && end == Evoral::MaxBeats) {
414 _model->write_to (newsrc, newsrc_lock);
416 _model->write_section_to (newsrc, newsrc_lock, begin, end);
419 error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
423 newsrc->flush_midi(newsrc_lock);
425 /* force a reload of the model if the range is partial */
427 if (begin != Evoral::MinBeats || end != Evoral::MaxBeats) {
428 newsrc->load_model (newsrc_lock, true);
430 newsrc->set_model (newsrc_lock, _model);
433 /* this file is not removable (but since it is MIDI, it is mutable) */
435 boost::dynamic_pointer_cast<FileSource> (newsrc)->prevent_deletion ();
441 MidiSource::session_saved()
445 /* this writes a copy of the data to disk.
446 XXX do we need to do this every time?
449 if (_model && _model->edited()) {
450 /* The model is edited, write its contents into the current source
451 file (overwiting previous contents). */
453 /* Temporarily drop our reference to the model so that as the model
454 pushes its current state to us, we don't try to update it. */
455 boost::shared_ptr<MidiModel> mm = _model;
458 /* Flush model contents to disk. */
459 mm->sync_to_source (lm);
461 /* Reacquire model. */
470 MidiSource::set_note_mode(const Lock& lock, NoteMode mode)
473 _model->set_note_mode(mode);
478 MidiSource::drop_model (const Lock& lock)
482 ModelChanged (); /* EMIT SIGNAL */
486 MidiSource::set_model (const Lock& lock, boost::shared_ptr<MidiModel> m)
490 ModelChanged (); /* EMIT SIGNAL */
493 Evoral::ControlList::InterpolationStyle
494 MidiSource::interpolation_of (Evoral::Parameter p) const
496 InterpolationStyleMap::const_iterator i = _interpolation_style.find (p);
497 if (i == _interpolation_style.end()) {
498 return EventTypeMap::instance().interpolation_of (p);
505 MidiSource::automation_state_of (Evoral::Parameter p) const
507 AutomationStateMap::const_iterator i = _automation_state.find (p);
508 if (i == _automation_state.end()) {
509 /* default to `play', otherwise if MIDI is recorded /
510 imported with controllers etc. they are by default
511 not played back, which is a little surprising.
519 /** Set interpolation style to be used for a given parameter. This change will be
520 * propagated to anyone who needs to know.
523 MidiSource::set_interpolation_of (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
525 if (interpolation_of (p) == s) {
529 if (EventTypeMap::instance().interpolation_of (p) == s) {
530 /* interpolation type is being set to the default, so we don't need a note in our map */
531 _interpolation_style.erase (p);
533 _interpolation_style[p] = s;
536 InterpolationChanged (p, s); /* EMIT SIGNAL */
540 MidiSource::set_automation_state_of (Evoral::Parameter p, AutoState s)
542 if (automation_state_of (p) == s) {
547 /* automation state is being set to the default, so we don't need a note in our map */
548 _automation_state.erase (p);
550 _automation_state[p] = s;
553 AutomationStateChanged (p, s); /* EMIT SIGNAL */
557 MidiSource::copy_interpolation_from (boost::shared_ptr<MidiSource> s)
559 copy_interpolation_from (s.get ());
563 MidiSource::copy_automation_state_from (boost::shared_ptr<MidiSource> s)
565 copy_automation_state_from (s.get ());
569 MidiSource::copy_interpolation_from (MidiSource* s)
571 _interpolation_style = s->_interpolation_style;
573 /* XXX: should probably emit signals here */
577 MidiSource::copy_automation_state_from (MidiSource* s)
579 _automation_state = s->_automation_state;
581 /* XXX: should probably emit signals here */