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