1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2011 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 #define RG_MODULE_STRING "[AllocateChannels]"
19 #define RG_NO_DEBUG_PRINT 1
20 
21 #include "AllocateChannels.h"
22 #include "Instrument.h"
23 #include "MidiDevice.h"
24 #include "misc/Debug.h"
25 #include "sound/ControlBlock.h"
26 
27 #include <algorithm>
28 
29 namespace Rosegarden
30 {
31 
32 // Allocate a channel interval that encompasses startTime and endTime.
33 // @param startTime is the first instant sound is to be played on the channel.
34 // @param endTime is the last such instant.
35 // @author Tom Breton (Tehom)
36 ChannelInterval
37 FreeChannels::
allocateChannelInterval(RealTime startTime,RealTime endTime,Instrument * instrument,RealTime marginBefore,RealTime marginAfter)38 allocateChannelInterval(RealTime startTime, RealTime endTime,
39                         Instrument *instrument,
40                         RealTime marginBefore,
41                         RealTime marginAfter)
42 {
43     RG_DEBUG << "allocateChannelInterval";
44     iterator bestMatch = end();
45     // Scoring just minimizes wasted space by choosing the smallest
46     // piece that fits.
47 
48     // Initialize (leastOverflow, leastDuration) to longer than any
49     // interval can be.
50     RealTime leastDuration = ChannelInterval::m_afterLatestTime;
51     // leastDuration's overflow bit.  See comments on thisOverflow and
52     // thisDuration.
53     bool leastOverflow = true;
54 
55     // Scan segments backwards from the last one beginning at time
56     // `startTime'
57     if (!empty()) {
58                     RG_DEBUG
59                         << "Scanning for existing ChannelInterval";
60 
61         ChannelInterval dummy(startTime);
62         for (reverse_iterator i(upper_bound(dummy));
63              i != rend();
64              ++i) {
65 
66             const ChannelInterval &cs = (*i);
67             RG_DEBUG << "Considering" << cs;
68             cs.assertSane();
69 
70             // Consider each end of the proposed interval.  An end
71             // fits if either:
72             //
73             // * It is big enough to accomodate the respective margin.
74             //
75             // * It is big enough without the margin and the adjacent
76             //   (allocated) channel interval sounds on the same
77             //   instrument.
78 
79             // Reject complete non-fits early.
80             if (cs.m_start > startTime) {
81                 RG_DEBUG << "  Rejecting due to free channel's available start time (" << cs.m_start << ") after needed start (" << startTime << ")";
82                 continue;
83             }
84             if (cs.m_end < endTime) {
85                 RG_DEBUG << "  Rejecting due to free channel's available end time (" << cs.m_end << ") before needed end (" << endTime << ")";
86                 continue;
87             }
88 
89             // Reject if instrument changed and margin is
90             // insufficient.  This considers both the given margins
91             // and the adjacent instruments' margins recorded in
92             // ChannelInterval.  Its fields m_marginBefore and
93             // m_marginAfter refer to our own before/after
94             // orientation, not to the reversed orientation that the
95             // instruments playing before and after would have.
96             if (cs.m_instrumentBefore &&
97                 (cs.m_instrumentBefore != instrument) &&
98                 (((cs.m_start +      marginBefore) > startTime) ||
99                  ((cs.m_start + cs.m_marginBefore) > startTime)))
100                 { continue; }
101 
102             if (cs.m_instrumentAfter &&
103                 (cs.m_instrumentAfter != instrument) &&
104                 (((cs.m_end -      marginAfter) < endTime) ||
105                  ((cs.m_end - cs.m_marginAfter) < endTime)))
106                 { continue; }
107 
108             // We found an candidate, but is it the best so far?  Only
109             // if it wastes less space than all others we've seen,
110             // which is true if it's smaller than them.
111 
112             // Be careful of overflow.  This calculation ranges from 0
113             // to twice the maximum RealTime can hold.  Overflow can
114             // cause us to see huge waste as negative waste, which
115             // results in very inefficient allocation.  To avoid this,
116             // we keep an overflow bit (thisOverflow and
117             // leastOverflow) and treat it as the most significant
118             // bit.
119             RealTime thisDuration = cs.m_end - cs.m_start;
120             bool thisOverflow = (thisDuration < RealTime::zeroTime);
121 
122             RG_DEBUG << "Found a candidate that takes"
123                      << (thisOverflow ? "the maximum plus" : "only")
124                      << thisDuration;
125 
126             if ((thisOverflow < leastOverflow) ||
127                 ((thisOverflow == leastOverflow) &&
128                  (thisDuration < leastDuration))) {
129 
130                 RG_DEBUG << "Best candidate so far";
131                 // Decrement so forward iterator refers to the same
132                 // element we examined.
133                 bestMatch = --(i.base());
134                 leastDuration = thisDuration;
135                 leastOverflow = thisOverflow;
136             }
137         }
138     }
139     if (bestMatch != end()) {
140         RG_DEBUG << "  FreeChannels::allocateChannelInterval() SUCCESS!!!!";
141         return allocateChannelIntervalFrom(bestMatch,
142                                            startTime, endTime,
143                                            instrument,
144                                            marginBefore, marginAfter);
145     } else {
146         RG_DEBUG << "  FreeChannels::allocateChannelInterval() giving up.  FAIL";
147         // If we found nothing usable, return an unplayable dummy
148         // channel
149         return ChannelInterval();
150     }
151 }
152 
153 // Free the given channel interval, setting old's channel to unused.
154 // @author Tom Breton (Tehom)
155 void
156 FreeChannels::
freeChannelInterval(ChannelInterval & old)157 freeChannelInterval(ChannelInterval &old)
158 {
159     if (!old.validChannel()) { return; }
160     RG_DEBUG << "Freeing" << old;
161     // We are sometimes asked to free a zero-length interval.  If so,
162     // do nothing.
163     if (old.m_start == old.m_end) { return; }
164     old.assertSane();
165 
166     // The first element which is not considered to go before val
167     // (i.e., either it is equivalent or goes after).
168     iterator atOrAfter = lower_bound(ChannelInterval(old.m_start));
169     iterator prevIterator = end();
170     iterator nextIterator = end();
171 
172     for (iterator i = begin(); i != atOrAfter; ++i) {
173         const ChannelInterval &cs = (*i);
174         if (cs.getChannelId() == old.getChannelId() &&
175             (cs.m_end == old.m_start)) {
176             prevIterator = i;
177             break;
178         }
179     }
180 
181     for (iterator i = atOrAfter; i != end(); ++i) {
182         const ChannelInterval &cs = (*i);
183         if (cs.getChannelId() == old.getChannelId() &&
184             (cs.m_start == old.m_end)) {
185             nextIterator = i;
186             break;
187         }
188     }
189 
190     // Figure out the actual endpoints.
191     const ChannelInterval &ciBefore =
192         (prevIterator == end()) ? old : *prevIterator;
193 
194     const ChannelInterval &ciAfter =
195         (nextIterator == end()) ? old : *nextIterator;
196 
197     const ChannelInterval
198         newChannelInterval(old.getChannelId(),
199                            ciBefore.m_start,             ciAfter.m_end,
200                            ciBefore.m_instrumentBefore,  ciAfter.m_instrumentAfter,
201                            ciBefore.m_marginBefore,      ciAfter.m_marginAfter);
202 
203     // Physically remove the adjacent intervals that we are merging
204     // with.
205     if (prevIterator != end()) { erase(prevIterator); }
206     if (nextIterator != end()) { erase(nextIterator); }
207 
208     newChannelInterval.assertSane();
209 
210     // Add a channelsegment incorporating the whole contiguous time.
211     insert(newChannelInterval);
212 
213     old.clearChannelId();
214 }
215 
216 
217 
218 // Allocate a time interval
219 // @param i an iterator indexing a ChannelInterval that includes the
220 // interval from start to end.
221 // @param start is the first instant sound is to be played on the channel.
222 // @param end is the last such instant.
223 // @returns A ChannelInterval, either a suitable one or non-playing.
224 // @author Tom Breton (Tehom)
225 ChannelInterval
226 FreeChannels::
allocateChannelIntervalFrom(iterator i,RealTime start,RealTime end,Instrument * instrument,RealTime marginBefore,RealTime marginAfter)227 allocateChannelIntervalFrom(iterator i, RealTime start, RealTime end,
228                             Instrument *instrument,
229                             RealTime marginBefore,
230                             RealTime marginAfter)
231 {
232   const ChannelInterval cs = (*i);
233 
234   erase(i);
235   if (cs.m_start < start) {
236     // There's some length before `start'.  Insert a new piece.  (We
237     // can't alter it in place because the base class owns it and
238     // makes it const)
239       (void)insert(ChannelInterval(cs.getChannelId(),
240                                    cs.m_start,            start,
241                                    cs.m_instrumentBefore, instrument,
242                                    cs.m_marginBefore,     marginBefore));
243   } else { }
244 
245   if (cs.m_end > end) {
246     // There's some length after `end'.  Insert a new piece.
247     (void)insert(ChannelInterval(cs.getChannelId(),
248                                  end,         cs.m_end,
249                                  instrument,  cs.m_instrumentAfter,
250                                  marginAfter, cs.m_marginAfter));
251   } else {}
252 
253   return ChannelInterval(cs.getChannelId(),
254                          start, end,
255                          nullptr, nullptr,
256                          RealTime::zeroTime, RealTime::zeroTime);
257 }
258 
259 // Add a channel that may be allocated from.  It is caller's
260 // responsibility to not duplicate channel numbers.
261 // @author Tom Breton (Tehom)
262 void
263 FreeChannels::
addChannel(ChannelId channelNb)264 addChannel(ChannelId channelNb)
265 {
266     insert(begin(),
267            ChannelInterval(channelNb,
268                            ChannelInterval::m_beforeEarliestTime,
269                            ChannelInterval::m_afterLatestTime,
270                            nullptr, nullptr,
271                            RealTime::zeroTime, RealTime::zeroTime));
272 }
273 
274 // Remove channel from being allocated.  It is caller's
275 // responsibility to deal with objects currently holding channel
276 // intervals.
277 // @author Tom Breton (Tehom)
278 void
279 FreeChannels::
removeChannel(ChannelId channelNb)280 removeChannel(ChannelId channelNb)
281 {
282     // Filter the channels.  We build a temporary container, put
283     // everything that stays into that container, then swap contents.
284     container els;
285     for (iterator i = begin(); i != end(); ++i)
286         if (i->getChannelId() != channelNb) {
287             els.insert(els.end(), *i);
288         }
289     swap(els);
290 }
291 
292 
293 // Re-allocate a ChannelInterval to encompass start and end.
294 // @author Tom Breton (Tehom)
295 void
296 FreeChannels::
reallocateToFit(ChannelInterval & ci,RealTime start,RealTime end,Instrument * instrument,RealTime marginBefore,RealTime marginAfter)297 reallocateToFit(ChannelInterval &ci, RealTime start, RealTime end,
298                 Instrument *instrument,
299                 RealTime marginBefore,
300                 RealTime marginAfter)
301 {
302     // If it still fits, re-use it.
303     if ((ci.validChannel()) &&
304         (ci.m_start <= start) &&
305         (end <= ci.m_end))
306         { return; }
307 
308     // Otherwise just replace it.
309     if (ci.validChannel())
310         { freeChannelInterval(ci); }
311     ci = allocateChannelInterval(start, end, instrument,
312                                  marginBefore, marginAfter);
313 }
314 
315 void
dump()316 FreeChannels::dump()
317 {
318     RG_DEBUG << "FreeChannels::Dump()";
319     for (iterator I = begin(); I != end(); ++I) {
320         RG_DEBUG << "  Channel:" << I->getChannelId();
321         RG_DEBUG << "    Start:" << I->m_start;
322         RG_DEBUG << "    End:" << I->m_end;
323     }
324 }
325 
326 
327     /*** ChannelSetup definitions ***/
328 
329 // This is a stub in case we ever want AllocateChannels to set up
330 // differently for different devices.
331 const ChannelSetup ChannelSetup::MIDI = ChannelSetup();
332 
333     /*** AllocateChannels definitions ***/
334 
335 // Whether a channelId denotes a percussion channel
336 // @author Tom Breton (Tehom)
337 bool
338 AllocateChannels::
isPercussion(ChannelId channel)339 isPercussion(ChannelId channel)
340 {
341     return MidiDevice::isPercussionNumber(channel);
342 }
343 
344 // Whether a ChannelInterval denotes a percussion channel
345 // @author Tom Breton (Tehom)
346 bool
347 AllocateChannels::
isPercussion(ChannelInterval & ci)348 isPercussion(ChannelInterval &ci)
349 {
350     return isPercussion(ci.getChannelId());
351 }
352 
353 // Constructor for AllocateChannels
354 // @author Tom Breton (Tehom)
355 AllocateChannels::
AllocateChannels(ChannelSetup)356 AllocateChannels(ChannelSetup /*unused*/) :
357     m_freeChannels()
358 {
359     // Quick and dirty: assume ChannelSetup::MIDI.
360     for (int i = 0; i < 16; ++i) {
361         if (!isPercussion(i)) {
362             (void)m_freeChannels.addChannel(i);
363         }
364     }
365 }
366 
367 AllocateChannels::
~AllocateChannels()368 ~AllocateChannels()
369 {
370     RG_DEBUG
371         << "~AllocateChannels";
372 }
373 
374 // Re-allocate a ChannelInterval to encompass start and end,
375 // appropriately for Instrument
376 // @author Tom Breton (Tehom)
377 void
378 AllocateChannels::
reallocateToFit(Instrument & instrument,ChannelInterval & ci,RealTime start,RealTime end,RealTime marginBefore,RealTime marginAfter,bool changedInstrument)379 reallocateToFit(Instrument& instrument, ChannelInterval &ci,
380                 RealTime start, RealTime end,
381                 RealTime marginBefore, RealTime marginAfter,
382                 bool changedInstrument)
383 {
384     RG_DEBUG
385         << "reallocateToFit: Reallocating"
386         << (instrument.isPercussion() ? "percussion" : "non-percussion")
387         << instrument.getName() << instrument.getId()
388         << "on bank"
389         << (int)instrument.getMSB() << ":" << (int)instrument.getLSB()
390         << "channel "
391         << ci.getChannelId();
392     // If we already have a channel but it's the wrong type or it
393     // changed instrument, always free it.
394     if (ci.validChannel() &&
395         ((changedInstrument && (end != ChannelInterval::m_latestTime)) ||
396          (instrument.isPercussion() != (isPercussion(ci)))))
397         { freeChannelInterval(ci); }
398 
399     if (instrument.isPercussion()) {
400         // For single channel, this implicitly frees+reallocates
401         ci = ChannelInterval(getPercussionChannel(), start, end,
402                              nullptr, nullptr,
403                              RealTime::zeroTime, RealTime::zeroTime);
404     } else {
405         m_freeChannels.reallocateToFit(ci, start, end,
406                                        &instrument,
407                                        marginBefore, marginAfter);
408     }
409 
410     RG_DEBUG
411         << "Now channel "
412         << ci.getChannelId();
413 }
414 
415 // Free the given channel interval appropriately.
416 // @author Tom Breton (Tehom)
417 void
418 AllocateChannels::
freeChannelInterval(ChannelInterval & old)419 freeChannelInterval(ChannelInterval &old)
420 {
421     if (isPercussion(old)) {
422         old.clearChannelId();
423     }
424     else {
425         m_freeChannels.freeChannelInterval(old);
426     }
427 }
428 
429 // Reserve a channel for a fixed-channel instrument.
430 // The signal connections this uses are made by ChannelManager.
431 // @author Tom Breton (Tehom)
432 void
433 AllocateChannels::
reserveFixedChannel(ChannelId channel)434 reserveFixedChannel(ChannelId channel)
435 {
436     reserveChannel(channel, m_fixedChannels);
437 
438     // If m_thruChannels has it, release it from there too.  Fixed
439     // channels have priority over thru channels because their channel
440     // numbers are chosen by the user.  We do this last to be very
441     // sure we're in a nice clean state when ControlBlock looks for
442     // another thru channel.
443     FixedChannelSet::iterator i = m_thruChannels.find(channel);
444     if (i != m_thruChannels.end())
445     {
446         // This statement is effectively releaseThruChannel, since we
447         // already found the channel in m_thruChannels and we know
448         // it's not going into m_freeChannels.
449         m_thruChannels.erase(i);
450 
451         // Kick ControlBlock off this channel.  It will allocate
452         // another.
453         ControlBlock::getInstance()->vacateThruChannel(channel);
454     }
455 }
456 
457 ChannelId
458 AllocateChannels::
reallocateThruChannel(Instrument & instrument,ChannelId channel)459 reallocateThruChannel(Instrument& instrument, ChannelId channel)
460 {
461     // If we already have a valid channel and it has the right
462     // percussion-ness, we're done.
463     if (channel >= 0) {
464         bool isInstrumentPercussion = instrument.isPercussion();
465         bool isChannelPercussion    = isPercussion(channel);
466         if (isInstrumentPercussion == isChannelPercussion) { return channel; }
467     }
468 
469     // Otherwise, out with the old, in with the new.
470     releaseThruChannel(channel);
471     return allocateThruChannel(instrument);
472 }
473 
474 
475 // Allocate a channel for MIDI playthru
476 // The signal connections this uses are made by ChannelManager.
477 // @author Tom Breton (Tehom)
478 ChannelId
479 AllocateChannels::
allocateThruChannel(Instrument & instrument)480 allocateThruChannel(Instrument& instrument)
481 {
482     if (instrument.isPercussion()) { return getPercussionChannel(); }
483 
484     // Quick and dirty: assume ChannelSetup::MIDI.  We inspect
485     // channels highest-first because they tend to be used
486     // lowest-first and we'd prefer not to collide.
487     for (int channel = 15; channel >= 0; --channel) {
488         // Avoid channels we've already reserved.
489         if (m_thruChannels.find(channel) != m_thruChannels.end())
490             { continue; }
491 
492         // Avoid any channel in m_fixedChannels.
493         if (m_fixedChannels.find(channel) != m_fixedChannels.end())
494             { continue; }
495 
496         // We don't try to find a good channel, we just accept the
497         // first candidate.
498         reserveChannel(channel, m_thruChannels);
499         return channel;
500     }
501 
502     // Fixed channels has all the channels, so return invalid channel.
503     return -1;
504 }
505 
506 // Release a reserved channel from channelSet
507 // @author Tom Breton (Tehom)
508 void
509 AllocateChannels::
releaseReservedChannel(ChannelId channel,FixedChannelSet & channelSet)510 releaseReservedChannel(ChannelId channel, FixedChannelSet& channelSet)
511 {
512     // Releasing an invalid channel does nothing.
513     if (channel < 0) { return; }
514     // Releasing the percussion channel does nothing.
515     if (isPercussion(channel)) { return; }
516 
517     FixedChannelSet::iterator i = channelSet.find(channel);
518 
519     if (i == channelSet.end()) { return; }
520     RG_DEBUG
521         << "AllocateChannels: releaseFixedChannel releasing"
522         << (int) channel;
523 
524     // Remove from reserved channels.
525     channelSet.erase(i);
526 
527     // Add channel to FreeChannels (if not percussion)
528     if (!isPercussion(channel)) {
529         m_freeChannels.addChannel(channel);
530     }
531 }
532 
533 // Reserve a channel with regard to channelSet.
534 // @author Tom Breton (Tehom)
535 void
536 AllocateChannels::
reserveChannel(ChannelId channel,FixedChannelSet & channelSet)537 reserveChannel(ChannelId channel, FixedChannelSet& channelSet)
538 {
539     // Remove channel from FreeChannels (if not percussion) so we
540     // don't give anything an interval on this channel.
541     if (!isPercussion(channel)) {
542         RG_DEBUG
543             << "AllocateChannels: reserveFixedChannel reserving"
544             << (int) channel;
545         m_freeChannels.removeChannel(channel);
546     }
547     // Record that this channel is reserved.
548     channelSet.insert(channel);
549 
550     // Kick ChannelManagers off the channel.  They'll get a new
551     // channel; that's not a concern here.
552     emit sigVacateChannel(channel);
553 }
554 
555 }
556 
557