/* Copyright (C) 2006 Paul Davis Author: David Robillard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "pbd/xml++.h" #include "pbd/pthread_utils.h" #include "pbd/basename.h" #include "evoral/Control.hpp" #include "evoral/EventSink.hpp" #include "ardour/debug.h" #include "ardour/file_source.h" #include "ardour/midi_channel_filter.h" #include "ardour/midi_model.h" #include "ardour/midi_source.h" #include "ardour/midi_state_tracker.h" #include "ardour/session.h" #include "ardour/session_directory.h" #include "ardour/source_factory.h" #include "i18n.h" namespace ARDOUR { template class MidiRingBuffer; } using namespace std; using namespace ARDOUR; using namespace PBD; PBD::Signal1 MidiSource::MidiSourceCreated; MidiSource::MidiSource (Session& s, string name, Source::Flag flags) : Source(s, DataType::MIDI, name, flags) , _writing(false) , _model_iter_valid(false) , _length_beats(0.0) , _last_read_end(0) , _capture_length(0) , _capture_loop_length(0) { } MidiSource::MidiSource (Session& s, const XMLNode& node) : Source(s, node) , _writing(false) , _model_iter_valid(false) , _length_beats(0.0) , _last_read_end(0) , _capture_length(0) , _capture_loop_length(0) { if (set_state (node, Stateful::loading_state_version)) { throw failed_constructor(); } } MidiSource::~MidiSource () { } XMLNode& MidiSource::get_state () { XMLNode& node (Source::get_state()); if (_captured_for.length()) { node.add_property ("captured-for", _captured_for); } for (InterpolationStyleMap::const_iterator i = _interpolation_style.begin(); i != _interpolation_style.end(); ++i) { XMLNode* child = node.add_child (X_("InterpolationStyle")); 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; } int MidiSource::set_state (const XMLNode& node, int /*version*/) { const XMLProperty* prop; if ((prop = node.property ("captured-for")) != 0) { _captured_for = prop->value(); } XMLNodeList children = node.children (); for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) { if ((*i)->name() == X_("InterpolationStyle")) { if ((prop = (*i)->property (X_("parameter"))) == 0) { error << _("Missing parameter property on InterpolationStyle") << endmsg; return -1; } Evoral::Parameter p = EventTypeMap::instance().from_symbol (prop->value()); if ((prop = (*i)->property (X_("style"))) == 0) { error << _("Missing style property on InterpolationStyle") << endmsg; return -1; } Evoral::ControlList::InterpolationStyle s = static_cast( string_2_enum (prop->value(), s)); set_interpolation_of (p, s); } else if ((*i)->name() == X_("AutomationState")) { if ((prop = (*i)->property (X_("parameter"))) == 0) { error << _("Missing parameter property on AutomationState") << endmsg; return -1; } Evoral::Parameter p = EventTypeMap::instance().from_symbol (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); } } return 0; } bool MidiSource::empty () const { return !_length_beats; } framecnt_t MidiSource::length (framepos_t pos) const { if (!_length_beats) { return 0; } BeatsFramesConverter converter(_session.tempo_map(), pos); return converter.to(_length_beats); } void MidiSource::update_length (framecnt_t) { // You're not the boss of me! } void MidiSource::invalidate (const Lock& lock, std::set::WeakNotePtr>* notes) { _model_iter_valid = false; _model_iter.invalidate(notes); } framecnt_t MidiSource::midi_read (const Lock& lm, Evoral::EventSink& dst, framepos_t source_start, framepos_t start, framecnt_t cnt, MidiStateTracker* tracker, MidiChannelFilter* filter, const std::set& filtered) const { BeatsFramesConverter converter(_session.tempo_map(), source_start); DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("MidiSource::midi_read() %5 sstart %1 start %2 cnt %3 tracker %4\n", source_start, start, cnt, tracker, name())); if (_model) { // Find appropriate model iterator Evoral::Sequence::const_iterator& i = _model_iter; const bool linear_read = _last_read_end != 0 && start == _last_read_end; if (!linear_read || !_model_iter_valid) { #if 0 // Cached iterator is invalid, search for the first event past start i = _model->begin(converter.from(start), false, filtered, linear_read ? &_model->active_notes() : NULL); _model_iter_valid = true; if (!linear_read) { _model->active_notes().clear(); } #else /* hot-fix http://tracker.ardour.org/view.php?id=6541 * "parallel playback of linked midi regions -> no note-offs" * * A midi source can be used by multiple tracks simultaneously, * in which case midi_read() can called from different tracks for * overlapping time-ranges. * * However there is only a single iterator for a given midi-source. * this results in every midi_read() performing a seek. * * if seeking is performed with * _model->begin(converter.from(start),...) * the model is used for seeking. That method seeks to the first * *note-on* event after 'start'. * * _model->begin(conveter.from( ) ,..) eventually calls * Sequence