~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to libs/ardour/midi_playlist.cc

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    Copyright (C) 2006 Paul Davis
 
3
    Author: David Robillard
 
4
 
 
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.
 
9
 
 
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.
 
14
 
 
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.
 
18
*/
 
19
 
 
20
#include <cassert>
 
21
 
 
22
#include <algorithm>
 
23
#include <iostream>
 
24
#include <utility>
 
25
 
 
26
#include <stdlib.h>
 
27
 
 
28
#include "evoral/EventList.hpp"
 
29
 
 
30
#include "ardour/debug.h"
 
31
#include "ardour/midi_model.h"
 
32
#include "ardour/midi_playlist.h"
 
33
#include "ardour/midi_region.h"
 
34
#include "ardour/types.h"
 
35
 
 
36
#include "i18n.h"
 
37
 
 
38
using namespace ARDOUR;
 
39
using namespace PBD;
 
40
using namespace std;
 
41
 
 
42
MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
 
43
        : Playlist (session, node, DataType::MIDI, hidden)
 
44
        , _note_mode(Sustained)
 
45
{
 
46
#ifndef NDEBUG
 
47
        const XMLProperty* prop = node.property("type");
 
48
        assert(prop && DataType(prop->value()) == DataType::MIDI);
 
49
#endif
 
50
 
 
51
        in_set_state++;
 
52
        if (set_state (node, Stateful::loading_state_version)) {
 
53
                throw failed_constructor ();
 
54
        }
 
55
        in_set_state--;
 
56
 
 
57
        relayer ();
 
58
}
 
59
 
 
60
MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
 
61
        : Playlist (session, name, DataType::MIDI, hidden)
 
62
        , _note_mode(Sustained)
 
63
{
 
64
}
 
65
 
 
66
MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
 
67
        : Playlist (other, name, hidden)
 
68
        , _note_mode(other->_note_mode)
 
69
{
 
70
}
 
71
 
 
72
MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, framepos_t start, framecnt_t dur, string name, bool hidden)
 
73
        : Playlist (other, start, dur, name, hidden)
 
74
        , _note_mode(other->_note_mode)
 
75
{
 
76
        /* this constructor does NOT notify others (session) */
 
77
}
 
78
 
 
79
MidiPlaylist::~MidiPlaylist ()
 
80
{
 
81
}
 
82
 
 
83
template<typename Time>
 
84
struct EventsSortByTimeAndType {
 
85
    bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
 
86
            if (a->time() == b->time()) {
 
87
                    if (EventTypeMap::instance().type_is_midi (a->event_type()) && EventTypeMap::instance().type_is_midi (b->event_type())) {
 
88
                            /* negate return value since we must return whether
 
89
                             * or not a should sort before b, not b before a
 
90
                             */
 
91
                            return !MidiBuffer::second_simultaneous_midi_byte_is_first (a->buffer()[0], b->buffer()[0]);
 
92
                    }
 
93
            }
 
94
            return a->time() < b->time();
 
95
    }
 
96
};
 
97
 
 
98
/** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
 
99
framecnt_t
 
100
MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framecnt_t dur, unsigned chan_n)
 
101
{
 
102
        /* this function is never called from a realtime thread, so
 
103
           its OK to block (for short intervals).
 
104
        */
 
105
 
 
106
        Playlist::RegionReadLock rl (this);
 
107
 
 
108
        DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("++++++ %1 .. %2  +++++++ %3 trackers +++++++++++++++++\n", 
 
109
                                                            start, start + dur, _note_trackers.size()));
 
110
 
 
111
        framepos_t end = start + dur - 1;
 
112
 
 
113
        // relevent regions overlapping start <--> end
 
114
        vector< boost::shared_ptr<Region> > regs;
 
115
        vector< boost::shared_ptr<Region> > ended;
 
116
        typedef pair<MidiStateTracker*,framepos_t> TrackerInfo;
 
117
        vector<TrackerInfo> tracker_info;
 
118
        NoteTrackers::iterator t;
 
119
 
 
120
        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
 
121
 
 
122
                /* in this call to coverage, the return value indicates the
 
123
                 * overlap status of the read range (start...end) WRT to 
 
124
                 * the region.
 
125
                 */
 
126
 
 
127
                switch ((*i)->coverage (start, end)) {
 
128
                case Evoral::OverlapStart:
 
129
                case Evoral::OverlapInternal:
 
130
                case Evoral::OverlapExternal:
 
131
                        regs.push_back (*i);
 
132
                        break;
 
133
 
 
134
                case Evoral::OverlapEnd:
 
135
                        /* this region ends within the read range */
 
136
                        regs.push_back (*i);
 
137
                        ended.push_back (*i);
 
138
                        break;
 
139
                default:
 
140
                        /* we don't care */
 
141
                        break;
 
142
                }
 
143
        }
 
144
 
 
145
        if (regs.size() == 1 && 
 
146
            (ended.empty() || (ended.size() == 1 && ended.front() == regs.front()))) {
 
147
 
 
148
                /* just a single region - read directly into dst */
 
149
 
 
150
                DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Single region (%1) read, ended during this read %2\n", regs.front()->name(),
 
151
                                                                    ended.size()));
 
152
 
 
153
                boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(regs.front());
 
154
 
 
155
                if (mr) {
 
156
 
 
157
                        NoteTrackers::iterator t = _note_trackers.find (mr.get());
 
158
                        MidiStateTracker* tracker;
 
159
                        bool new_tracker = false;
 
160
 
 
161
                        if (t == _note_trackers.end()) {
 
162
                                tracker = new MidiStateTracker;
 
163
                                new_tracker = true;
 
164
                                DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
 
165
                        } else {
 
166
                                tracker = t->second;
 
167
                                DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
 
168
                        }
 
169
 
 
170
                        mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
 
171
                        DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
 
172
 
 
173
                        if (!ended.empty()) {
 
174
                                DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1 ended in this read, resolve notes and delete (%2) tracker\n",
 
175
                                                                                    mr->name(), ((new_tracker) ? "new" : "old")));
 
176
                                tracker->resolve_notes (dst, mr->last_frame());
 
177
                                delete tracker;
 
178
                                if (!new_tracker) {
 
179
                                        _note_trackers.erase (t);
 
180
                                }
 
181
                        } else {
 
182
                                if (new_tracker) {
 
183
                                        pair<Region*,MidiStateTracker*> newpair;
 
184
                                        newpair.first = mr.get();
 
185
                                        newpair.second = tracker;
 
186
                                        _note_trackers.insert (newpair);
 
187
                                        DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
 
188
                                }
 
189
                        }
 
190
                }
 
191
 
 
192
        } else {
 
193
 
 
194
                /* multiple regions and/or note resolution: sort by layer, read into a temporary non-monotonically
 
195
                   sorted EventSink, sort and then insert into dst.
 
196
                */
 
197
 
 
198
                DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("%1 regions to read, plus %2 trackers\n", regs.size(), tracker_info.size()));
 
199
 
 
200
                Evoral::EventList<framepos_t> evlist;
 
201
 
 
202
                DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("for %1 .. %2 we have %3 to consider\n", start, start+dur-1, regs.size()));
 
203
 
 
204
                for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
 
205
 
 
206
                        boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
 
207
 
 
208
                        if (!mr) {
 
209
                                continue;
 
210
                        }
 
211
 
 
212
                        NoteTrackers::iterator t = _note_trackers.find (mr.get());
 
213
                        MidiStateTracker* tracker;
 
214
                        bool new_tracker = false;
 
215
 
 
216
                        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()));
 
217
 
 
218
                        if (t == _note_trackers.end()) {
 
219
                                tracker = new MidiStateTracker;
 
220
                                new_tracker = true;
 
221
                                DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tBEFORE: new tracker\n");
 
222
                        } else {
 
223
                                tracker = t->second;
 
224
                                DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tBEFORE: tracker says there are %1 on notes\n", tracker->on()));
 
225
                        }
 
226
 
 
227
 
 
228
                        mr->read_at (evlist, start, dur, chan_n, _note_mode, tracker);
 
229
 
 
230
#ifndef NDEBUG
 
231
                        DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("After %1 (%2 .. %3) we now have %4\n", mr->name(), mr->position(), mr->last_frame(), evlist.size()));
 
232
                        for (Evoral::EventList<framepos_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
 
233
                                DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
 
234
                        }
 
235
                        DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\tAFTER: tracker says there are %1 on notes\n", tracker->on()));
 
236
#endif
 
237
                        if (find (ended.begin(), ended.end(), *i) != ended.end()) {
 
238
 
 
239
                                /* the region ended within the read range, so
 
240
                                 * resolve any dangling notes (i.e. notes whose
 
241
                                 * end is beyond the end of the region).
 
242
                                 */
 
243
                                
 
244
                                DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1 ended in this read, resolve notes and delete (%2) tracker\n",
 
245
                                                                                    mr->name(), ((new_tracker) ? "new" : "old")));
 
246
 
 
247
                                tracker->resolve_notes (evlist, (*i)->last_frame());
 
248
                                delete tracker;
 
249
                                if (!new_tracker) {
 
250
                                        _note_trackers.erase (t);
 
251
                                }
 
252
 
 
253
                        } else {
 
254
 
 
255
                                if (new_tracker) {
 
256
                                        _note_trackers.insert (make_pair (mr.get(), tracker));
 
257
                                        DEBUG_TRACE (DEBUG::MidiPlaylistIO, "\tadded tracker to trackers\n");
 
258
                                }
 
259
                        }
 
260
                }
 
261
 
 
262
                if (!evlist.empty()) {
 
263
 
 
264
                        /* sort the event list */
 
265
                        EventsSortByTimeAndType<framepos_t> cmp;
 
266
                        evlist.sort (cmp);
 
267
 
 
268
#ifndef NDEBUG
 
269
                        DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("Final we now have %1 events\n",  evlist.size()));
 
270
                        for (Evoral::EventList<framepos_t>::iterator x = evlist.begin(); x != evlist.end(); ++x) {
 
271
                                DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("\t%1\n", **x));
 
272
                        }
 
273
#endif
 
274
                        /* write into dst */
 
275
                        for (Evoral::EventList<framepos_t>::iterator e = evlist.begin(); e != evlist.end(); ++e) {
 
276
                                Evoral::Event<framepos_t>* ev (*e);
 
277
                                dst.write (ev->time(), ev->event_type(), ev->size(), ev->buffer());
 
278
                                delete ev;
 
279
                        }
 
280
 
 
281
                }
 
282
        }
 
283
 
 
284
        DEBUG_TRACE (DEBUG::MidiPlaylistIO, "-------------------------------------------------------------\n");
 
285
        return dur;
 
286
}
 
287
 
 
288
void
 
289
MidiPlaylist::clear_note_trackers ()
 
290
{
 
291
        Playlist::RegionWriteLock rl (this, false);
 
292
 
 
293
        for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
 
294
                delete n->second;
 
295
        }
 
296
        DEBUG_TRACE (DEBUG::MidiTrackers, string_compose ("%1 clears all note trackers\n", name()));
 
297
        _note_trackers.clear ();
 
298
}
 
299
 
 
300
void
 
301
MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
 
302
{
 
303
        /* MIDI regions have no dependents (crossfades) but we might be tracking notes */
 
304
        NoteTrackers::iterator t = _note_trackers.find (region.get());
 
305
 
 
306
        /* GACK! THREAD SAFETY! */
 
307
 
 
308
        if (t != _note_trackers.end()) {
 
309
                delete t->second;
 
310
                _note_trackers.erase (t);
 
311
        }
 
312
}
 
313
 
 
314
int
 
315
MidiPlaylist::set_state (const XMLNode& node, int version)
 
316
{
 
317
        in_set_state++;
 
318
        freeze ();
 
319
 
 
320
        if (Playlist::set_state (node, version)) {
 
321
                return -1;
 
322
        }
 
323
 
 
324
        thaw();
 
325
        in_set_state--;
 
326
 
 
327
        return 0;
 
328
}
 
329
 
 
330
void
 
331
MidiPlaylist::dump () const
 
332
{
 
333
        boost::shared_ptr<Region> r;
 
334
 
 
335
        cerr << "Playlist \"" << _name << "\" " << endl
 
336
        << regions.size() << " regions "
 
337
        << endl;
 
338
 
 
339
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
 
340
                r = *i;
 
341
                cerr << "  " << r->name() << " @ " << r << " ["
 
342
                << r->start() << "+" << r->length()
 
343
                << "] at "
 
344
                << r->position()
 
345
                << " on layer "
 
346
                << r->layer ()
 
347
                << endl;
 
348
        }
 
349
}
 
350
 
 
351
bool
 
352
MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
 
353
{
 
354
        boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
 
355
 
 
356
        if (!r) {
 
357
                return false;
 
358
        }
 
359
 
 
360
        bool changed = false;
 
361
 
 
362
        {
 
363
                RegionWriteLock rlock (this);
 
364
                RegionList::iterator i;
 
365
                RegionList::iterator tmp;
 
366
 
 
367
                for (i = regions.begin(); i != regions.end(); ) {
 
368
 
 
369
                        tmp = i;
 
370
                        ++tmp;
 
371
 
 
372
                        if ((*i) == region) {
 
373
                                regions.erase (i);
 
374
                                changed = true;
 
375
                        }
 
376
 
 
377
                        i = tmp;
 
378
                }
 
379
        }
 
380
 
 
381
 
 
382
        if (changed) {
 
383
                /* overload this, it normally means "removed", not destroyed */
 
384
                notify_region_removed (region);
 
385
        }
 
386
 
 
387
        return changed;
 
388
}
 
389
 
 
390
set<Evoral::Parameter>
 
391
MidiPlaylist::contained_automation()
 
392
{
 
393
        /* this function is never called from a realtime thread, so
 
394
           its OK to block (for short intervals).
 
395
        */
 
396
 
 
397
        Playlist::RegionReadLock rl (this);
 
398
        set<Evoral::Parameter> ret;
 
399
 
 
400
        for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
 
401
                boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
 
402
 
 
403
                for (Automatable::Controls::iterator c = mr->model()->controls().begin();
 
404
                                c != mr->model()->controls().end(); ++c) {
 
405
                        ret.insert(c->first);
 
406
                }
 
407
        }
 
408
 
 
409
        return ret;
 
410
}
 
411
 
 
412
 
 
413
bool
 
414
MidiPlaylist::region_changed (const PBD::PropertyChange& what_changed, boost::shared_ptr<Region> region)
 
415
{
 
416
        if (in_flush || in_set_state) {
 
417
                return false;
 
418
        }
 
419
 
 
420
        PBD::PropertyChange our_interests;
 
421
        our_interests.add (Properties::midi_data);
 
422
 
 
423
        bool parent_wants_notify = Playlist::region_changed (what_changed, region);
 
424
 
 
425
        if (parent_wants_notify || what_changed.contains (our_interests)) {
 
426
                notify_contents_changed ();
 
427
        }
 
428
 
 
429
        return true;
 
430
}
 
431