1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7     See the AUTHORS file for more details.
8 
9     This program is free software; you can redistribute it and/or
10     modify it under the terms of the GNU General Public License as
11     published by the Free Software Foundation; either version 2 of the
12     License, or (at your option) any later version.  See the file
13     COPYING included with this distribution for more information.
14 */
15 
16 #define RG_MODULE_STRING "[AudioPlayQueue]"
17 
18 #include "AudioPlayQueue.h"
19 
20 #include "misc/Debug.h"
21 #include "PlayableAudioFile.h"
22 #include "base/Profiler.h"
23 
24 //#define DEBUG_AUDIO_PLAY_QUEUE 1
25 //#define FINE_DEBUG_AUDIO_PLAY_QUEUE 1
26 
27 namespace Rosegarden
28 {
29 
30 
instrumentId2Index(InstrumentId id)31 static inline unsigned int instrumentId2Index(InstrumentId id)
32 {
33     if (id < AudioInstrumentBase)
34         return 0;
35     else
36         return (id - AudioInstrumentBase);
37 }
38 
39 bool
operator ()(const PlayableAudioFile & f1,const PlayableAudioFile & f2) const40 AudioPlayQueue::FileTimeCmp::operator()(const PlayableAudioFile &f1,
41                                         const PlayableAudioFile &f2) const
42 {
43     return operator()(&f1, &f2);
44 }
45 
46 bool
operator ()(const PlayableAudioFile * f1,const PlayableAudioFile * f2) const47 AudioPlayQueue::FileTimeCmp::operator()(const PlayableAudioFile *f1,
48                                         const PlayableAudioFile *f2) const
49 {
50     RealTime t1 = f1->getStartTime(), t2 = f2->getStartTime();
51     if (t1 < t2)
52         return true;
53     else if (t2 < t1)
54         return false;
55     else
56         return f1 < f2;
57 }
58 
59 
AudioPlayQueue()60 AudioPlayQueue::AudioPlayQueue() :
61         m_maxBuffers(0)
62 {
63     // nothing to do
64 }
65 
~AudioPlayQueue()66 AudioPlayQueue::~AudioPlayQueue()
67 {
68     RG_DEBUG << "dtor";
69 
70     clear();
71 }
72 
73 void
addScheduled(PlayableAudioFile * file)74 AudioPlayQueue::addScheduled(PlayableAudioFile *file)
75 {
76     if (m_files.find(file) != m_files.end()) {
77         RG_WARNING << "WARNING: addScheduled(" << file << "): already in queue";
78         return ;
79     }
80 
81     m_files.insert(file);
82 
83     RealTime startTime = file->getStartTime();
84     RealTime endTime = file->getStartTime() + file->getDuration();
85 
86     InstrumentId instrument = file->getInstrument();
87     unsigned int index = instrumentId2Index(instrument);
88 
89     while ((unsigned int)m_instrumentIndex.size() <= index) {
90         m_instrumentIndex.push_back(ReverseFileMap());
91     }
92 
93 #ifdef DEBUG_AUDIO_PLAY_QUEUE
94     RG_DEBUG << "[" << this << "]::addScheduled(" << file << "): start " << file->getStartTime() << ", end " << file->getEndTime() << ", slots: ";
95 #endif
96 
97     for (int i = startTime.sec; i <= endTime.sec; ++i) {
98         m_index[i].push_back(file);
99         m_instrumentIndex[index][i].push_back(file);
100         if (!file->isSmallFile()) {
101             m_counts[i] += file->getTargetChannels();
102             if (m_counts[i] > m_maxBuffers) {
103                 m_maxBuffers = m_counts[i];
104             }
105         }
106 #ifdef DEBUG_AUDIO_PLAY_QUEUE
107         RG_DEBUG << "  " << i;
108 #endif
109 
110     }
111 
112 #ifdef DEBUG_AUDIO_PLAY_QUEUE
113     RG_DEBUG << "  (max buffers now " << m_maxBuffers << ")";
114 #endif
115 }
116 
117 void
addUnscheduled(PlayableAudioFile * file)118 AudioPlayQueue::addUnscheduled(PlayableAudioFile *file)
119 {
120 #ifdef DEBUG_AUDIO_PLAY_QUEUE
121     RG_DEBUG << "[" << this << "]::addUnscheduled(" << file << "): start " << file->getStartTime() << ", end " << file->getEndTime() << ", instrument " << file->getInstrument();
122 #endif
123 
124     m_unscheduled.push_back(file);
125 
126 #ifdef DEBUG_AUDIO_PLAY_QUEUE
127     RG_DEBUG << "[" << this << "]::addUnscheduled: now " << m_unscheduled.size() << " unscheduled files";
128 #endif
129 
130 }
131 
132 void
erase(PlayableAudioFile * file)133 AudioPlayQueue::erase(PlayableAudioFile *file)
134 {
135 #ifdef DEBUG_AUDIO_PLAY_QUEUE
136     RG_DEBUG << "erase(" << file << "): start " << file->getStartTime() << ", end " << file->getEndTime();
137 #endif
138 
139     FileSet::iterator fi = m_files.find(file);
140     if (fi == m_files.end()) {
141         for (FileList::iterator fli = m_unscheduled.begin();
142                 fli != m_unscheduled.end(); ++fli) {
143             if (*fli == file) {
144                 m_unscheduled.erase(fli);
145                 delete file;
146                 return ;
147             }
148         }
149         return ;
150     }
151     m_files.erase(fi);
152 
153     InstrumentId instrument = file->getInstrument();
154     unsigned int index = instrumentId2Index(instrument);
155 
156     for (ReverseFileMap::iterator mi = m_instrumentIndex[index].begin();
157             mi != m_instrumentIndex[index].end(); ++mi) {
158 
159         for (FileVector::iterator fi = mi->second.begin();
160                 fi != mi->second.end(); ++fi) {
161 
162             if (*fi == file) {
163                 mi->second.erase(fi);
164                 if (m_counts[mi->first] > 0)
165                     --m_counts[mi->first];
166                 break;
167             }
168         }
169     }
170 
171     for (ReverseFileMap::iterator mi = m_index.begin();
172             mi != m_index.end(); ++mi) {
173 
174         for (FileVector::iterator fi = mi->second.begin();
175                 fi != mi->second.end(); ++fi) {
176 
177             if (*fi == file) {
178                 mi->second.erase(fi);
179                 if (m_counts[mi->first] > 0)
180                     --m_counts[mi->first];
181                 break;
182             }
183         }
184     }
185 
186     delete file;
187 }
188 
189 void
clear()190 AudioPlayQueue::clear()
191 {
192 #ifdef DEBUG_AUDIO_PLAY_QUEUE
193     RG_DEBUG << "clear()";
194 #endif
195 
196     while (m_files.begin() != m_files.end()) {
197         delete *m_files.begin();
198         m_files.erase(m_files.begin());
199     }
200 
201     while (m_unscheduled.begin() != m_unscheduled.end()) {
202         delete *m_unscheduled.begin();
203         m_unscheduled.erase(m_unscheduled.begin());
204     }
205 
206     m_instrumentIndex.clear();
207     m_index.clear();
208     m_counts.clear();
209     m_maxBuffers = 0;
210 }
211 
212 bool
empty() const213 AudioPlayQueue::empty() const
214 {
215     return m_unscheduled.empty() && m_files.empty();
216 }
217 
218 size_t
size() const219 AudioPlayQueue::size() const
220 {
221     return m_unscheduled.size() + m_files.size();
222 }
223 
224 void
getPlayingFiles(const RealTime & sliceStart,const RealTime & sliceDuration,FileSet & playing) const225 AudioPlayQueue::getPlayingFiles(const RealTime &sliceStart,
226                                 const RealTime &sliceDuration,
227                                 FileSet &playing) const
228 {
229     //    Profiler profiler("AudioPlayQueue::getPlayingFiles");
230 
231     // This one needs to be quick.
232 
233     playing.clear();
234 
235     RealTime sliceEnd = sliceStart + sliceDuration;
236 
237     for (int i = sliceStart.sec; i <= sliceEnd.sec; ++i) {
238 
239         ReverseFileMap::const_iterator mi(m_index.find(i));
240         if (mi == m_index.end())
241             continue;
242 
243         for (FileVector::const_iterator fi = mi->second.begin();
244                 fi != mi->second.end(); ++fi) {
245 
246             PlayableAudioFile *f = *fi;
247 
248             if (f->getStartTime() > sliceEnd ||
249                     f->getStartTime() + f->getDuration() <= sliceStart)
250                 continue;
251 
252 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
253             RG_DEBUG << "getPlayingFiles(): found " << f << " in slot " << i;
254 #endif
255 
256             playing.insert(f);
257         }
258     }
259 
260     for (FileList::const_iterator fli = m_unscheduled.begin();
261             fli != m_unscheduled.end(); ++fli) {
262         PlayableAudioFile *file = *fli;
263         if (file->getStartTime() <= sliceEnd &&
264                 file->getStartTime() + file->getDuration() > sliceStart) {
265             playing.insert(file);
266         }
267     }
268 
269 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
270     if (playing.size() > 0) {
271         RG_DEBUG << "getPlayingFiles(" << sliceStart << "," << sliceDuration << "): total " << playing.size() << " files";
272     }
273 #endif
274 }
275 
276 void
getPlayingFilesForInstrument(const RealTime & sliceStart,const RealTime & sliceDuration,InstrumentId instrumentId,PlayableAudioFile ** playing,size_t & size) const277 AudioPlayQueue::getPlayingFilesForInstrument(const RealTime &sliceStart,
278         const RealTime &sliceDuration,
279         InstrumentId instrumentId,
280         PlayableAudioFile **playing,
281         size_t &size) const
282 {
283 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
284     bool printed = false;
285     Profiler profiler("AudioPlayQueue::getPlayingFilesForInstrument", true);
286 #endif
287 
288     // This one needs to be quick.
289 
290     size_t written = 0;
291 
292     RealTime sliceEnd = sliceStart + sliceDuration;
293 
294     unsigned int index = instrumentId2Index(instrumentId);
295     if (index >= (unsigned int)m_instrumentIndex.size()) {
296         goto unscheduled; // nothing scheduled here
297     }
298 
299     for (int i = sliceStart.sec; i <= sliceEnd.sec; ++i) {
300 
301         ReverseFileMap::const_iterator mi
302         (m_instrumentIndex[index].find(i));
303 
304         if (mi == m_instrumentIndex[index].end())
305             continue;
306 
307         for (FileVector::const_iterator fi = mi->second.begin();
308                 fi != mi->second.end(); ++fi) {
309 
310             PlayableAudioFile *f = *fi;
311 
312             if (f->getInstrument() != instrumentId)
313                 continue;
314 
315 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
316             if (!printed) {
317                 RG_DEBUG << "getPlayingFilesForInstrument(" << sliceStart << ", " << sliceDuration << ", " << instrumentId << ")";
318                 printed = true;
319             }
320 #endif
321 
322             if (f->getStartTime() > sliceEnd ||
323                     f->getEndTime() <= sliceStart) {
324 
325 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
326                 RG_DEBUG << "  rejected " << f << " in slot " << i;
327                 if (f->getStartTime() > sliceEnd) {
328                     RG_DEBUG << "  (" << f->getStartTime() << " > " << sliceEnd << ")";
329                 } else {
330                     RG_DEBUG << "  (" << f->getEndTime() << " <= " << sliceStart << ")";
331                 }
332 #endif
333 
334                 continue;
335             }
336 
337 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
338             RG_DEBUG << "  found " << f << " in slot " << i << " (" << f->getStartTime() << " -> " << f->getEndTime() << ")";
339 #endif
340 
341             size_t j = 0;
342             for (j = 0; j < written; ++j) {
343                 if (playing[j] == f)
344                     break;
345             }
346             if (j < written)
347                 break; // already have it
348 
349             if (written >= size) {
350 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
351                 RG_DEBUG << "  No room to write it!";
352 #endif
353 
354                 break;
355             }
356 
357             playing[written++] = f;
358         }
359     }
360 
361 unscheduled:
362 
363     for (FileList::const_iterator fli = m_unscheduled.begin();
364             fli != m_unscheduled.end(); ++fli) {
365 
366         PlayableAudioFile *f = *fli;
367 
368         if (f->getInstrument() != instrumentId) {
369 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
370             RG_DEBUG << "  rejecting unscheduled " << f << " as wrong instrument (" << f->getInstrument() << " != " << instrumentId << ")";
371 #endif
372 
373             continue;
374         }
375 
376 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
377         if (!printed) {
378             RG_DEBUG << "getPlayingFilesForInstrument(" << sliceStart << ", " << sliceDuration << ", " << instrumentId << ")";
379             printed = true;
380         }
381 #endif
382 
383         if (f->getStartTime() <= sliceEnd &&
384                 f->getStartTime() + f->getDuration() > sliceStart) {
385 
386 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
387             RG_DEBUG << "  found " << f << " in unscheduled list (" << f->getStartTime() << " -> " << f->getEndTime() << ")";
388 #endif
389 
390             if (written >= size)
391                 break;
392             playing[written++] = f;
393 
394 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
395 
396         } else {
397 
398             RG_DEBUG << "  rejected " << f << " in unscheduled list";
399             if (f->getStartTime() > sliceEnd) {
400                 RG_DEBUG << "  (" << f->getStartTime() << " > " << sliceEnd << ")";
401             } else {
402                 RG_DEBUG << "  (" << f->getEndTime() << " <= " << sliceStart << ")";
403             }
404 #endif
405 
406         }
407     }
408 
409 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
410     if (written > 0) {
411         RG_DEBUG << "getPlayingFilesForInstrument(): total " << written << " files";
412     }
413 #endif
414 
415     size = written;
416 }
417 
418 bool
haveFilesForInstrument(InstrumentId instrumentId) const419 AudioPlayQueue::haveFilesForInstrument(InstrumentId instrumentId) const
420 {
421 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
422     RG_DEBUG << "haveFilesForInstrument(" << instrumentId << ")...";
423 #endif
424 
425     unsigned int index = instrumentId2Index(instrumentId);
426 
427     if (index < (unsigned int)m_instrumentIndex.size() &&
428             !m_instrumentIndex[index].empty()) {
429 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
430         RG_DEBUG << "  yes (scheduled)";
431 #endif
432 
433         return true;
434     }
435 
436     for (FileList::const_iterator fli = m_unscheduled.begin();
437             fli != m_unscheduled.end(); ++fli) {
438         PlayableAudioFile *file = *fli;
439         if (file->getInstrument() == instrumentId) {
440 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
441             RG_DEBUG << "  yes (unscheduled)";
442 #endif
443 
444             return true;
445         }
446     }
447 
448 #ifdef FINE_DEBUG_AUDIO_PLAY_QUEUE
449     RG_DEBUG << "  no";
450 #endif
451 
452     return false;
453 }
454 
455 const AudioPlayQueue::FileSet &
getAllScheduledFiles() const456 AudioPlayQueue::getAllScheduledFiles() const
457 {
458 #ifdef DEBUG_AUDIO_PLAY_QUEUE
459     RG_DEBUG << "[" << this << "]::getAllScheduledFiles(): have " << m_files.size() << " files";
460 #endif
461 
462     return m_files;
463 }
464 
465 const AudioPlayQueue::FileList &
getAllUnscheduledFiles() const466 AudioPlayQueue::getAllUnscheduledFiles() const
467 {
468 #ifdef DEBUG_AUDIO_PLAY_QUEUE
469     RG_DEBUG << "[" << this << "]::getAllUnscheduledFiles(): have " << m_unscheduled.size() << " files";
470 #endif
471 
472     return m_unscheduled;
473 }
474 
475 
476 }
477 
478