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);
127 case MidiCCAutomation:
128 case MidiPgmChangeAutomation: break;
129 case MidiChannelPressureAutomation: break;
130 case MidiNotePressureAutomation: break;
131 case MidiPitchBenderAutomation: break;
132 case MidiSystemExclusiveAutomation:
133 cerr << "Parameter \"" << str << "\" is system exclusive - no automation possible!\n";
136 cerr << "Parameter \"" << str << "\" found for MIDI source ... not legal; ignoring this parameter\n";
140 if (!(*i)->get_property (X_("style"), str)) {
141 error << _("Missing style property on InterpolationStyle") << endmsg;
144 Evoral::ControlList::InterpolationStyle s =
145 static_cast<Evoral::ControlList::InterpolationStyle>(string_2_enum (str, s));
146 set_interpolation_of (p, s);
148 } else if ((*i)->name() == X_("AutomationState")) {
149 if (!(*i)->get_property (X_("parameter"), str)) {
150 error << _("Missing parameter property on AutomationState") << endmsg;
153 Evoral::Parameter p = EventTypeMap::instance().from_symbol (str);
155 if (!(*i)->get_property (X_("state"), str)) {
156 error << _("Missing state property on AutomationState") << endmsg;
159 AutoState s = static_cast<AutoState>(string_2_enum (str, s));
160 set_automation_state_of (p, s);
168 MidiSource::empty () const
170 return !_length_beats;
174 MidiSource::length (samplepos_t pos) const
176 if (!_length_beats) {
180 BeatsSamplesConverter converter(_session.tempo_map(), pos);
181 return converter.to(_length_beats);
185 MidiSource::update_length (samplecnt_t)
187 // You're not the boss of me!
191 MidiSource::invalidate (const Lock& lock)
193 Invalidated(_session.transport_rolling());
197 MidiSource::midi_read (const Lock& lm,
198 Evoral::EventSink<samplepos_t>& dst,
199 samplepos_t source_start,
202 Evoral::Range<samplepos_t>* loop_range,
204 MidiStateTracker* tracker,
205 MidiChannelFilter* filter,
206 const std::set<Evoral::Parameter>& filtered,
207 const double pos_beats,
208 const double start_beats) const
210 BeatsSamplesConverter converter(_session.tempo_map(), source_start);
212 const double start_qn = pos_beats - start_beats;
214 DEBUG_TRACE (DEBUG::MidiSourceIO,
215 string_compose ("MidiSource::midi_read() %5 sstart %1 start %2 cnt %3 tracker %4\n",
216 source_start, start, cnt, tracker, name()));
219 return read_unlocked (lm, dst, source_start, start, cnt, loop_range, tracker, filter);
222 // Find appropriate model iterator
223 Evoral::Sequence<Temporal::Beats>::const_iterator& i = cursor.iter;
224 const bool linear_read = cursor.last_read_end != 0 && start == cursor.last_read_end;
225 if (!linear_read || !i.valid()) {
226 /* Cached iterator is invalid, search for the first event past start.
227 Note that multiple tracks can use a MidiSource simultaneously, so
228 all playback state must be in parameters (the cursor) and must not
229 be cached in the source of model itself.
230 See http://tracker.ardour.org/view.php?id=6541
232 cursor.connect(Invalidated);
233 cursor.iter = _model->begin(converter.from(start), false, filtered, &cursor.active_notes);
234 cursor.active_notes.clear();
237 cursor.last_read_end = start + cnt;
239 // Copy events in [start, start + cnt) into dst
240 for (; i != _model->end(); ++i) {
242 // Offset by source start to convert event time to session time
244 samplepos_t time_samples = _session.tempo_map().sample_at_quarter_note (i->time().to_double() + start_qn);
246 if (time_samples < start + source_start) {
247 /* event too early */
251 } else if (time_samples >= start + cnt + source_start) {
253 DEBUG_TRACE (DEBUG::MidiSourceIO,
254 string_compose ("%1: reached end with event @ %2 vs. %3\n",
255 _name, time_samples, start+cnt));
263 time_samples = loop_range->squish (time_samples);
266 const uint8_t status = i->buffer()[0];
267 const bool is_channel_event = (0x80 <= (status & 0xF0)) && (status <= 0xE0);
268 if (filter && is_channel_event) {
269 /* Copy event so the filter can modify the channel. I'm not
270 sure if this is necessary here (channels are mapped later in
271 buffers anyway), but it preserves existing behaviour without
272 destroying events in the model during read. */
273 Evoral::Event<Temporal::Beats> ev(*i, true);
274 if (!filter->filter(ev.buffer(), ev.size())) {
275 dst.write(time_samples, ev.event_type(), ev.size(), ev.buffer());
277 DEBUG_TRACE (DEBUG::MidiSourceIO,
278 string_compose ("%1: filter event @ %2 type %3 size %4\n",
279 _name, time_samples, i->event_type(), i->size()));
282 dst.write (time_samples, i->event_type(), i->size(), i->buffer());
286 if (DEBUG_ENABLED(DEBUG::MidiSourceIO)) {
288 DEBUG_STR_APPEND(a, string_compose ("%1 added event @ %2 sz %3 within %4 .. %5 ",
289 _name, time_samples, i->size(),
290 start + source_start, start + cnt + source_start));
291 for (size_t n=0; n < i->size(); ++n) {
292 DEBUG_STR_APPEND(a,hex);
293 DEBUG_STR_APPEND(a,"0x");
294 DEBUG_STR_APPEND(a,(int)i->buffer()[n]);
295 DEBUG_STR_APPEND(a,' ');
297 DEBUG_STR_APPEND(a,'\n');
298 DEBUG_TRACE (DEBUG::MidiSourceIO, DEBUG_STR(a).str());
312 MidiSource::midi_write (const Lock& lm,
313 MidiRingBuffer<samplepos_t>& source,
314 samplepos_t source_start,
317 const samplecnt_t ret = write_unlocked (lm, source, source_start, cnt);
319 if (cnt == max_samplecnt) {
322 _capture_length += cnt;
329 MidiSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
332 _model->set_note_mode (mode);
333 _model->start_write ();
340 MidiSource::mark_write_starting_now (samplecnt_t position,
341 samplecnt_t capture_length,
342 samplecnt_t loop_length)
344 /* I'm not sure if this is the best way to approach this, but
345 _capture_length needs to be set up with the transport sample
346 when a record actually starts, as it is used by
347 SMFSource::write_unlocked to decide whether incoming notes
348 are within the correct time range.
349 mark_streaming_midi_write_started (perhaps a more logical
350 place to do this) is not called at exactly the time when
351 record starts, and I don't think it necessarily can be
352 because it is not RT-safe.
355 set_timeline_position(position);
356 _capture_length = capture_length;
357 _capture_loop_length = loop_length;
359 TempoMap& map (_session.tempo_map());
360 BeatsSamplesConverter converter(map, position);
361 _length_beats = converter.from(capture_length);
365 MidiSource::mark_streaming_write_started (const Lock& lock)
367 NoteMode note_mode = _model ? _model->note_mode() : Sustained;
368 mark_streaming_midi_write_started (lock, note_mode);
372 MidiSource::mark_midi_streaming_write_completed (const Lock& lock,
373 Evoral::Sequence<Temporal::Beats>::StuckNoteOption option,
377 _model->end_write (option, end);
379 /* Make captured controls discrete to play back user input exactly. */
380 for (MidiModel::Controls::iterator i = _model->controls().begin(); i != _model->controls().end(); ++i) {
381 if (i->second->list()) {
382 i->second->list()->set_interpolation(Evoral::ControlList::Discrete);
383 _interpolation_style.insert(std::make_pair(i->second->parameter(), Evoral::ControlList::Discrete));
393 MidiSource::mark_streaming_write_completed (const Lock& lock)
395 mark_midi_streaming_write_completed (lock, Evoral::Sequence<Temporal::Beats>::DeleteStuckNotes);
399 MidiSource::export_write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Temporal::Beats begin, Temporal::Beats end)
401 Lock newsrc_lock (newsrc->mutex ());
404 error << string_compose (_("programming error: %1"), X_("no model for MidiSource during export"));
408 _model->write_section_to (newsrc, newsrc_lock, begin, end, true);
410 newsrc->flush_midi(newsrc_lock);
416 MidiSource::write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Temporal::Beats begin, Temporal::Beats end)
418 Lock newsrc_lock (newsrc->mutex ());
420 newsrc->set_timeline_position (_timeline_position);
421 newsrc->copy_interpolation_from (this);
422 newsrc->copy_automation_state_from (this);
425 if (begin == Temporal::Beats() && end == std::numeric_limits<Temporal::Beats>::max()) {
426 _model->write_to (newsrc, newsrc_lock);
428 _model->write_section_to (newsrc, newsrc_lock, begin, end);
431 error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
435 newsrc->flush_midi(newsrc_lock);
437 /* force a reload of the model if the range is partial */
439 if (begin != Temporal::Beats() || end != std::numeric_limits<Temporal::Beats>::max()) {
440 newsrc->load_model (newsrc_lock, true);
442 newsrc->set_model (newsrc_lock, _model);
445 /* this file is not removable (but since it is MIDI, it is mutable) */
447 boost::dynamic_pointer_cast<FileSource> (newsrc)->prevent_deletion ();
453 MidiSource::session_saved()
457 /* this writes a copy of the data to disk.
458 XXX do we need to do this every time?
461 if (_model && _model->edited()) {
462 /* The model is edited, write its contents into the current source
463 file (overwiting previous contents). */
465 /* Temporarily drop our reference to the model so that as the model
466 pushes its current state to us, we don't try to update it. */
467 boost::shared_ptr<MidiModel> mm = _model;
470 /* Flush model contents to disk. */
471 mm->sync_to_source (lm);
473 /* Reacquire model. */
482 MidiSource::set_note_mode(const Lock& lock, NoteMode mode)
485 _model->set_note_mode(mode);
490 MidiSource::drop_model (const Lock& lock)
494 ModelChanged (); /* EMIT SIGNAL */
498 MidiSource::set_model (const Lock& lock, boost::shared_ptr<MidiModel> m)
502 ModelChanged (); /* EMIT SIGNAL */
505 Evoral::ControlList::InterpolationStyle
506 MidiSource::interpolation_of (Evoral::Parameter p) const
508 InterpolationStyleMap::const_iterator i = _interpolation_style.find (p);
509 if (i == _interpolation_style.end()) {
510 return EventTypeMap::instance().interpolation_of (p);
517 MidiSource::automation_state_of (Evoral::Parameter p) const
519 AutomationStateMap::const_iterator i = _automation_state.find (p);
520 if (i == _automation_state.end()) {
521 /* default to `play', otherwise if MIDI is recorded /
522 imported with controllers etc. they are by default
523 not played back, which is a little surprising.
531 /** Set interpolation style to be used for a given parameter. This change will be
532 * propagated to anyone who needs to know.
535 MidiSource::set_interpolation_of (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
537 if (interpolation_of (p) == s) {
541 if (EventTypeMap::instance().interpolation_of (p) == s) {
542 /* interpolation type is being set to the default, so we don't need a note in our map */
543 _interpolation_style.erase (p);
545 _interpolation_style[p] = s;
548 InterpolationChanged (p, s); /* EMIT SIGNAL */
552 MidiSource::set_automation_state_of (Evoral::Parameter p, AutoState s)
554 if (automation_state_of (p) == s) {
559 /* automation state is being set to the default, so we don't need a note in our map */
560 _automation_state.erase (p);
562 _automation_state[p] = s;
565 AutomationStateChanged (p, s); /* EMIT SIGNAL */
569 MidiSource::copy_interpolation_from (boost::shared_ptr<MidiSource> s)
571 copy_interpolation_from (s.get ());
575 MidiSource::copy_automation_state_from (boost::shared_ptr<MidiSource> s)
577 copy_automation_state_from (s.get ());
581 MidiSource::copy_interpolation_from (MidiSource* s)
583 _interpolation_style = s->_interpolation_style;
585 /* XXX: should probably emit signals here */
589 MidiSource::copy_automation_state_from (MidiSource* s)
591 _automation_state = s->_automation_state;
593 /* XXX: should probably emit signals here */