13
0
livetrax/libs/ardour/midi_playlist.cc
David Robillard 6fa6514cfd Remove over 500 unnecessary includes (including 54 of session.h).
It's slightly possible that this causes trivial build failures on different
configurations, but otherwise shouldn't cause any problems (i.e. no actual
changes other than include/naming/namespace stuff).  I deliberately avoided
removing libardour-config.h since this can mysteriously break things, though a
few of those do seem to be unnecessary.

This commit only targets includes of ardour/*.h.  There is also a very large
number of unnecessary includes of stuff in gtk2_ardour; tackling that should
also give a big improvement in build time when things are modified.


git-svn-id: svn://localhost/ardour2/branches/3.0@12420 d708f5d6-7413-0410-9779-e7cbd77b26cf
2012-05-24 06:09:29 +00:00

432 lines
12 KiB
C++

/*
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 <cassert>
#include <algorithm>
#include <iostream>
#include <utility>
#include <stdlib.h>
#include "evoral/EventList.hpp"
#include "ardour/debug.h"
#include "ardour/midi_model.h"
#include "ardour/midi_playlist.h"
#include "ardour/midi_region.h"
#include "ardour/types.h"
#include "i18n.h"
using namespace ARDOUR;
using namespace PBD;
using namespace std;
MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
: Playlist (session, node, DataType::MIDI, hidden)
, _note_mode(Sustained)
{
#ifndef NDEBUG
const XMLProperty* prop = node.property("type");
assert(prop && DataType(prop->value()) == DataType::MIDI);
#endif
in_set_state++;
if (set_state (node, Stateful::loading_state_version)) {
throw failed_constructor ();
}
in_set_state--;
relayer ();
}
MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
: Playlist (session, name, DataType::MIDI, hidden)
, _note_mode(Sustained)
{
}
MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
: Playlist (other, name, hidden)
, _note_mode(other->_note_mode)
{
}
MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, framepos_t start, framecnt_t dur, string name, bool hidden)
: Playlist (other, start, dur, name, hidden)
, _note_mode(other->_note_mode)
{
/* this constructor does NOT notify others (session) */
}
MidiPlaylist::~MidiPlaylist ()
{
}
template<typename Time>
struct EventsSortByTimeAndType {
bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
if (a->time() == b->time()) {
if (EventTypeMap::instance().type_is_midi (a->event_type()) && EventTypeMap::instance().type_is_midi (b->event_type())) {
/* negate return value since we must return whether
* or not a should sort before b, not b before a
*/
return !MidiBuffer::second_simultaneous_midi_byte_is_first (a->buffer()[0], b->buffer()[0]);
}
}
return a->time() < b->time();
}
};
/** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
framecnt_t
MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framecnt_t dur, unsigned chan_n)
{
/* this function is never called from a realtime thread, so
its OK to block (for short intervals).
*/
Playlist::RegionReadLock rl (this);
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2 +++++++ %3 trackers +++++++++++++++++\n",
start, start + dur, _note_trackers.size()));
framepos_t end = start + dur - 1;
// relevent regions overlapping start <--> end
vector< boost::shared_ptr<Region> > regs;
vector< boost::shared_ptr<Region> > ended;
typedef pair<MidiStateTracker*,framepos_t> TrackerInfo;
vector<TrackerInfo> tracker_info;
NoteTrackers::iterator t;
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
/* in this call to coverage, the return value indicates the
* overlap status of the read range (start...end) WRT to
* the region.
*/
switch ((*i)->coverage (start, end)) {
case Evoral::OverlapStart:
case Evoral::OverlapInternal:
case Evoral::OverlapExternal:
regs.push_back (*i);
break;
case Evoral::OverlapEnd:
/* this region ends within the read range */
regs.push_back (*i);
ended.push_back (*i);
break;
default:
/* we don't care */
break;
}
}
if (regs.size() == 1 &&
(ended.empty() || (ended.size() == 1 && ended.front() == regs.front()))) {
/* just a single region - read directly into dst */
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, ended during this read %2\n", regs.front()->name(),
ended.size()));
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
if (mr) {
NoteTrackers::iterator t = _note_trackers.find (mr.get());
MidiStateTracker* tracker;
bool new_tracker = false;
if (t == _note_trackers.end()) {
tracker = new MidiStateTracker;
new_tracker = true;
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
} else {
tracker = t->second;
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
}
mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
if (!ended.empty()) {
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1 ended in this read, resolve notes and delete (%2) tracker\n",
mr->name(), ((new_tracker) ? "new" : "old")));
tracker->resolve_notes (dst, mr->last_frame());
delete tracker;
if (!new_tracker) {
_note_trackers.erase (t);
}
} else {
if (new_tracker) {
pair<Region*,MidiStateTracker*> newpair;
newpair.first = mr.get();
newpair.second = tracker;
_note_trackers.insert (newpair);
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
}
}
}
} else {
/* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
sorted EventSink, sort and then insert into dst.
*/
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
Evoral::EventList<framepos_t> evlist;
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
if (!mr) {
continue;
}
NoteTrackers::iterator t = _note_trackers.find (mr.get());
MidiStateTracker* tracker;
bool new_tracker = false;
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Before %1 (%2 .. %3) we now have %4 events\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
if (t == _note_trackers.end()) {
tracker = new MidiStateTracker;
new_tracker = true;
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
} else {
tracker = t->second;
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
}
mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
#ifndef NDEBUG
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
for (Evoral::EventList<framepos_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
}
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
#endif
if (find (ended.begin(), ended.end(), *i) != ended.end()) {
/* the region ended within the read range, so
* resolve any dangling notes (i.e. notes whose
* end is beyond the end of the region).
*/
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1 ended in this read, resolve notes and delete (%2) tracker\n",
mr->name(), ((new_tracker) ? "new" : "old")));
tracker->resolve_notes (evlist, (*i)->last_frame());
delete tracker;
if (!new_tracker) {
_note_trackers.erase (t);
}
} else {
if (new_tracker) {
_note_trackers.insert (make_pair (mr.get(), tracker));
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
}
}
}
if (!evlist.empty()) {
/* sort the event list */
EventsSortByTimeAndType<framepos_t> cmp;
evlist.sort (cmp);
#ifndef NDEBUG
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n", evlist.size()));
for (Evoral::EventList<framepos_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
}
#endif
/* write into dst */
for (Evoral::EventList<framepos_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
Evoral::Event<framepos_t>* ev (*e);
dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
delete ev;
}
}
}
DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
return dur;
}
void
MidiPlaylist::clear_note_trackers ()
{
Playlist::RegionWriteLock rl (this, false);
for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
delete n->second;
}
DEBUG_TRACE (DEBUG::MidiTrackers, string_compose ("%1 clears all note trackers\n", name()));
_note_trackers.clear ();
}
void
MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
{
/* MIDI regions have no dependents (crossfades) but we might be tracking notes */
NoteTrackers::iterator t = _note_trackers.find (region.get());
/* GACK! THREAD SAFETY! */
if (t != _note_trackers.end()) {
delete t->second;
_note_trackers.erase (t);
}
}
int
MidiPlaylist::set_state (const XMLNode& node, int version)
{
in_set_state++;
freeze ();
if (Playlist::set_state (node, version)) {
return -1;
}
thaw();
in_set_state--;
return 0;
}
void
MidiPlaylist::dump () const
{
boost::shared_ptr<Region> r;
cerr << "Playlist \"" << _name << "\" " << endl
<< regions.size() << " regions "
<< endl;
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
r = *i;
cerr << " " << r->name() << " @ " << r << " ["
<< r->start() << "+" << r->length()
<< "] at "
<< r->position()
<< " on layer "
<< r->layer ()
<< endl;
}
}
bool
MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
{
boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
if (!r) {
return false;
}
bool changed = false;
{
RegionWriteLock rlock (this);
RegionList::iterator i;
RegionList::iterator tmp;
for (i = regions.begin(); i != regions.end(); ) {
tmp = i;
++tmp;
if ((*i) == region) {
regions.erase (i);
changed = true;
}
i = tmp;
}
}
if (changed) {
/* overload this, it normally means "removed", not destroyed */
notify_region_removed (region);
}
return changed;
}
set<Evoral::Parameter>
MidiPlaylist::contained_automation()
{
/* this function is never called from a realtime thread, so
its OK to block (for short intervals).
*/
Playlist::RegionReadLock rl (this);
set<Evoral::Parameter> ret;
for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
for (Automatable::Controls::iterator c = mr->model()->controls().begin();
c != mr->model()->controls().end(); ++c) {
ret.insert(c->first);
}
}
return ret;
}
bool
MidiPlaylist::region_changed (const PBD::PropertyChange& what_changed, boost::shared_ptr<Region> region)
{
if (in_flush || in_set_state) {
return false;
}
PBD::PropertyChange our_interests;
our_interests.add (Properties::midi_data);
bool parent_wants_notify = Playlist::region_changed (what_changed, region);
if (parent_wants_notify || what_changed.contains (our_interests)) {
notify_contents_changed ();
}
return true;
}