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.set_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->set_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
99 child->set_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->set_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
105 child->set_property (X_("state"), enum_2_string (i->second));
112 MidiSource::set_state (const XMLNode& node, int /*version*/)
114 node.get_property ("captured-for", _captured_for);
117 XMLNodeList children = node.children ();
118 for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
119 if ((*i)->name() == X_("InterpolationStyle")) {
120 if (!(*i)->get_property (X_("parameter"), str)) {
121 error << _("Missing parameter property on InterpolationStyle") << endmsg;
124 Evoral::Parameter p = EventTypeMap::instance().from_symbol (str);
126 if (!(*i)->get_property (X_("style"), str)) {
127 error << _("Missing style property on InterpolationStyle") << endmsg;
130 Evoral::ControlList::InterpolationStyle s =
131 static_cast<Evoral::ControlList::InterpolationStyle>(string_2_enum (str, s));
132 set_interpolation_of (p, s);
134 } else if ((*i)->name() == X_("AutomationState")) {
135 if (!(*i)->get_property (X_("parameter"), str)) {
136 error << _("Missing parameter property on AutomationState") << endmsg;
139 Evoral::Parameter p = EventTypeMap::instance().from_symbol (str);
141 if (!(*i)->get_property (X_("state"), str)) {
142 error << _("Missing state property on AutomationState") << endmsg;
145 AutoState s = static_cast<AutoState>(string_2_enum (str, s));
146 set_automation_state_of (p, s);
154 MidiSource::empty () const
156 return !_length_beats;
160 MidiSource::length (samplepos_t pos) const
162 if (!_length_beats) {
166 BeatsSamplesConverter converter(_session.tempo_map(), pos);
167 return converter.to(_length_beats);
171 MidiSource::update_length (samplecnt_t)
173 // You're not the boss of me!
177 MidiSource::invalidate (const Lock& lock)
179 Invalidated(_session.transport_rolling());
183 MidiSource::midi_read (const Lock& lm,
184 Evoral::EventSink<samplepos_t>& dst,
185 samplepos_t source_start,
188 Evoral::Range<samplepos_t>* loop_range,
190 MidiStateTracker* tracker,
191 MidiChannelFilter* filter,
192 const std::set<Evoral::Parameter>& filtered,
193 const double pos_beats,
194 const double start_beats) const
196 BeatsSamplesConverter converter(_session.tempo_map(), source_start);
198 const double start_qn = pos_beats - start_beats;
200 DEBUG_TRACE (DEBUG::MidiSourceIO,
201 string_compose ("MidiSource::midi_read() %5 sstart %1 start %2 cnt %3 tracker %4\n",
202 source_start, start, cnt, tracker, name()));
205 return read_unlocked (lm, dst, source_start, start, cnt, loop_range, tracker, filter);
208 // Find appropriate model iterator
209 Evoral::Sequence<Temporal::Beats>::const_iterator& i = cursor.iter;
210 const bool linear_read = cursor.last_read_end != 0 && start == cursor.last_read_end;
211 if (!linear_read || !i.valid()) {
212 /* Cached iterator is invalid, search for the first event past start.
213 Note that multiple tracks can use a MidiSource simultaneously, so
214 all playback state must be in parameters (the cursor) and must not
215 be cached in the source of model itself.
216 See http://tracker.ardour.org/view.php?id=6541
218 cursor.connect(Invalidated);
219 cursor.iter = _model->begin(converter.from(start), false, filtered, &cursor.active_notes);
220 cursor.active_notes.clear();
223 cursor.last_read_end = start + cnt;
225 // Copy events in [start, start + cnt) into dst
226 for (; i != _model->end(); ++i) {
228 // Offset by source start to convert event time to session time
230 samplepos_t time_samples = _session.tempo_map().sample_at_quarter_note (i->time().to_double() + start_qn);
232 if (time_samples < start + source_start) {
233 /* event too early */
237 } else if (time_samples >= start + cnt + source_start) {
239 DEBUG_TRACE (DEBUG::MidiSourceIO,
240 string_compose ("%1: reached end with event @ %2 vs. %3\n",
241 _name, time_samples, start+cnt));
249 time_samples = loop_range->squish (time_samples);
252 const uint8_t status = i->buffer()[0];
253 const bool is_channel_event = (0x80 <= (status & 0xF0)) && (status <= 0xE0);
254 if (filter && is_channel_event) {
255 /* Copy event so the filter can modify the channel. I'm not
256 sure if this is necessary here (channels are mapped later in
257 buffers anyway), but it preserves existing behaviour without
258 destroying events in the model during read. */
259 Evoral::Event<Temporal::Beats> ev(*i, true);
260 if (!filter->filter(ev.buffer(), ev.size())) {
261 dst.write(time_samples, ev.event_type(), ev.size(), ev.buffer());
263 DEBUG_TRACE (DEBUG::MidiSourceIO,
264 string_compose ("%1: filter event @ %2 type %3 size %4\n",
265 _name, time_samples, i->event_type(), i->size()));
268 dst.write (time_samples, i->event_type(), i->size(), i->buffer());
272 if (DEBUG_ENABLED(DEBUG::MidiSourceIO)) {
274 DEBUG_STR_APPEND(a, string_compose ("%1 added event @ %2 sz %3 within %4 .. %5 ",
275 _name, time_samples, i->size(),
276 start + source_start, start + cnt + source_start));
277 for (size_t n=0; n < i->size(); ++n) {
278 DEBUG_STR_APPEND(a,hex);
279 DEBUG_STR_APPEND(a,"0x");
280 DEBUG_STR_APPEND(a,(int)i->buffer()[n]);
281 DEBUG_STR_APPEND(a,' ');
283 DEBUG_STR_APPEND(a,'\n');
284 DEBUG_TRACE (DEBUG::MidiSourceIO, DEBUG_STR(a).str());
298 MidiSource::midi_write (const Lock& lm,
299 MidiRingBuffer<samplepos_t>& source,
300 samplepos_t source_start,
303 const samplecnt_t ret = write_unlocked (lm, source, source_start, cnt);
305 if (cnt == max_samplecnt) {
308 _capture_length += cnt;
315 MidiSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
318 _model->set_note_mode (mode);
319 _model->start_write ();
326 MidiSource::mark_write_starting_now (samplecnt_t position,
327 samplecnt_t capture_length,
328 samplecnt_t loop_length)
330 /* I'm not sure if this is the best way to approach this, but
331 _capture_length needs to be set up with the transport sample
332 when a record actually starts, as it is used by
333 SMFSource::write_unlocked to decide whether incoming notes
334 are within the correct time range.
335 mark_streaming_midi_write_started (perhaps a more logical
336 place to do this) is not called at exactly the time when
337 record starts, and I don't think it necessarily can be
338 because it is not RT-safe.
341 set_timeline_position(position);
342 _capture_length = capture_length;
343 _capture_loop_length = loop_length;
345 TempoMap& map (_session.tempo_map());
346 BeatsSamplesConverter converter(map, position);
347 _length_beats = converter.from(capture_length);
351 MidiSource::mark_streaming_write_started (const Lock& lock)
353 NoteMode note_mode = _model ? _model->note_mode() : Sustained;
354 mark_streaming_midi_write_started (lock, note_mode);
358 MidiSource::mark_midi_streaming_write_completed (const Lock& lock,
359 Evoral::Sequence<Temporal::Beats>::StuckNoteOption option,
363 _model->end_write (option, end);
365 /* Make captured controls discrete to play back user input exactly. */
366 for (MidiModel::Controls::iterator i = _model->controls().begin(); i != _model->controls().end(); ++i) {
367 if (i->second->list()) {
368 i->second->list()->set_interpolation(Evoral::ControlList::Discrete);
369 _interpolation_style.insert(std::make_pair(i->second->parameter(), Evoral::ControlList::Discrete));
379 MidiSource::mark_streaming_write_completed (const Lock& lock)
381 mark_midi_streaming_write_completed (lock, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes);
385 MidiSource::export_write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Temporal::Beats begin, Temporal::Beats end)
387 Lock newsrc_lock (newsrc->mutex ());
390 error << string_compose (_("programming error: %1"), X_("no model for MidiSource during export"));
394 _model->write_section_to (newsrc, newsrc_lock, begin, end, true);
396 newsrc->flush_midi(newsrc_lock);
402 MidiSource::write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Temporal::Beats begin, Temporal::Beats end)
404 Lock newsrc_lock (newsrc->mutex ());
406 newsrc->set_timeline_position (_timeline_position);
407 newsrc->copy_interpolation_from (this);
408 newsrc->copy_automation_state_from (this);
411 if (begin == Temporal::Beats() && end == std::numeric_limits<Temporal::Beats>::max()) {
412 _model->write_to (newsrc, newsrc_lock);
414 _model->write_section_to (newsrc, newsrc_lock, begin, end);
417 error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
421 newsrc->flush_midi(newsrc_lock);
423 /* force a reload of the model if the range is partial */
425 if (begin != Temporal::Beats() || end != std::numeric_limits<Temporal::Beats>::max()) {
426 newsrc->load_model (newsrc_lock, true);
428 newsrc->set_model (newsrc_lock, _model);
431 /* this file is not removable (but since it is MIDI, it is mutable) */
433 boost::dynamic_pointer_cast<FileSource> (newsrc)->prevent_deletion ();
439 MidiSource::session_saved()
443 /* this writes a copy of the data to disk.
444 XXX do we need to do this every time?
447 if (_model && _model->edited()) {
448 /* The model is edited, write its contents into the current source
449 file (overwiting previous contents). */
451 /* Temporarily drop our reference to the model so that as the model
452 pushes its current state to us, we don't try to update it. */
453 boost::shared_ptr<MidiModel> mm = _model;
456 /* Flush model contents to disk. */
457 mm->sync_to_source (lm);
459 /* Reacquire model. */
468 MidiSource::set_note_mode(const Lock& lock, NoteMode mode)
471 _model->set_note_mode(mode);
476 MidiSource::drop_model (const Lock& lock)
480 ModelChanged (); /* EMIT SIGNAL */
484 MidiSource::set_model (const Lock& lock, boost::shared_ptr<MidiModel> m)
488 ModelChanged (); /* EMIT SIGNAL */
491 Evoral::ControlList::InterpolationStyle
492 MidiSource::interpolation_of (Evoral::Parameter p) const
494 InterpolationStyleMap::const_iterator i = _interpolation_style.find (p);
495 if (i == _interpolation_style.end()) {
496 return EventTypeMap::instance().interpolation_of (p);
503 MidiSource::automation_state_of (Evoral::Parameter p) const
505 AutomationStateMap::const_iterator i = _automation_state.find (p);
506 if (i == _automation_state.end()) {
507 /* default to `play', otherwise if MIDI is recorded /
508 imported with controllers etc. they are by default
509 not played back, which is a little surprising.
517 /** Set interpolation style to be used for a given parameter. This change will be
518 * propagated to anyone who needs to know.
521 MidiSource::set_interpolation_of (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
523 if (interpolation_of (p) == s) {
527 if (EventTypeMap::instance().interpolation_of (p) == s) {
528 /* interpolation type is being set to the default, so we don't need a note in our map */
529 _interpolation_style.erase (p);
531 _interpolation_style[p] = s;
534 InterpolationChanged (p, s); /* EMIT SIGNAL */
538 MidiSource::set_automation_state_of (Evoral::Parameter p, AutoState s)
540 if (automation_state_of (p) == s) {
545 /* automation state is being set to the default, so we don't need a note in our map */
546 _automation_state.erase (p);
548 _automation_state[p] = s;
551 AutomationStateChanged (p, s); /* EMIT SIGNAL */
555 MidiSource::copy_interpolation_from (boost::shared_ptr<MidiSource> s)
557 copy_interpolation_from (s.get ());
561 MidiSource::copy_automation_state_from (boost::shared_ptr<MidiSource> s)
563 copy_automation_state_from (s.get ());
567 MidiSource::copy_interpolation_from (MidiSource* s)
569 _interpolation_style = s->_interpolation_style;
571 /* XXX: should probably emit signals here */
575 MidiSource::copy_automation_state_from (MidiSource* s)
577 _automation_state = s->_automation_state;
579 /* XXX: should probably emit signals here */