comparison audioio/AudioCallbackPlaySource.cpp @ 0:db6fcbd4405c

initial import
author Chris Cannam
date Tue, 10 Jan 2006 16:33:16 +0000
parents
children 97c69acdcb82
comparison
equal deleted inserted replaced
-1:000000000000 0:db6fcbd4405c
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 A waveform viewer and audio annotation editor.
5 Chris Cannam, Queen Mary University of London, 2005
6
7 This is experimental software. Not for distribution.
8 */
9
10 #include "AudioCallbackPlaySource.h"
11
12 #include "AudioGenerator.h"
13
14 #include "base/Model.h"
15 #include "base/ViewManager.h"
16 #include "model/DenseTimeValueModel.h"
17 #include "model/SparseOneDimensionalModel.h"
18 #include "dsp/timestretching/IntegerTimeStretcher.h"
19
20 #include <iostream>
21
22 //#define DEBUG_AUDIO_PLAY_SOURCE 1
23
24 //const size_t AudioCallbackPlaySource::m_ringBufferSize = 102400;
25 const size_t AudioCallbackPlaySource::m_ringBufferSize = 131071;
26
27 AudioCallbackPlaySource::AudioCallbackPlaySource(ViewManager *manager) :
28 m_viewManager(manager),
29 m_audioGenerator(new AudioGenerator(manager)),
30 m_bufferCount(0),
31 m_blockSize(1024),
32 m_sourceSampleRate(0),
33 m_targetSampleRate(0),
34 m_playLatency(0),
35 m_playing(false),
36 m_exiting(false),
37 m_bufferedToFrame(0),
38 m_outputLeft(0.0),
39 m_outputRight(0.0),
40 m_slowdownCounter(0),
41 m_timeStretcher(0),
42 m_fillThread(0),
43 m_converter(0)
44 {
45 // preallocate some slots, to avoid reallocation in an
46 // un-thread-safe manner later
47 while (m_buffers.size() < 20) m_buffers.push_back(0);
48
49 m_viewManager->setAudioPlaySource(this);
50 }
51
52 AudioCallbackPlaySource::~AudioCallbackPlaySource()
53 {
54 m_exiting = true;
55
56 if (m_fillThread) {
57 m_condition.wakeAll();
58 m_fillThread->wait();
59 delete m_fillThread;
60 }
61
62 clearModels();
63 }
64
65 void
66 AudioCallbackPlaySource::addModel(Model *model)
67 {
68 m_mutex.lock();
69
70 m_models.insert(model);
71
72 bool buffersChanged = false, srChanged = false;
73
74 if (m_sourceSampleRate == 0) {
75
76 m_sourceSampleRate = model->getSampleRate();
77 srChanged = true;
78
79 } else if (model->getSampleRate() != m_sourceSampleRate) {
80 std::cerr << "AudioCallbackPlaySource::addModel: ERROR: "
81 << "New model sample rate does not match" << std::endl
82 << "existing model(s) (new " << model->getSampleRate()
83 << " vs " << m_sourceSampleRate
84 << "), playback will be wrong"
85 << std::endl;
86 }
87
88 size_t sz = m_ringBufferSize;
89 if (m_bufferCount > 0) {
90 sz = m_buffers[0]->getSize();
91 }
92
93 size_t modelChannels = 1;
94 DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
95 if (dtvm) modelChannels = dtvm->getChannelCount();
96
97 while (m_bufferCount < modelChannels) {
98
99 if (m_buffers.size() < modelChannels) {
100 // This is a hideously chancy operation -- the RT thread
101 // could be using this vector. We allocated several slots
102 // in the ctor to avoid exactly this, but if we ever end
103 // up with more channels than that (!) then we're just
104 // going to have to risk it
105 m_buffers.push_back(new RingBuffer<float>(sz));
106
107 } else {
108 // The usual case
109 m_buffers[m_bufferCount] = new RingBuffer<float>(sz);
110 }
111
112 ++m_bufferCount;
113 buffersChanged = true;
114 }
115
116 if (buffersChanged) {
117 m_audioGenerator->setTargetChannelCount(m_bufferCount);
118 }
119
120 if (buffersChanged || srChanged) {
121
122 if (m_converter) {
123 src_delete(m_converter);
124 m_converter = 0;
125 }
126
127 if (getSourceSampleRate() != getTargetSampleRate()) {
128
129 int err = 0;
130 m_converter = src_new(SRC_SINC_FASTEST, m_bufferCount, &err);
131 if (!m_converter) {
132 std::cerr
133 << "AudioCallbackPlaySource::setModel: ERROR in creating samplerate converter: "
134 << src_strerror(err) << std::endl;
135 }
136 }
137 }
138
139 m_audioGenerator->addModel(model);
140
141 m_mutex.unlock();
142
143 if (!m_fillThread) {
144 m_fillThread = new AudioCallbackPlaySourceFillThread(*this);
145 m_fillThread->start();
146 }
147
148 #ifdef DEBUG_AUDIO_PLAY_SOURCE
149 std::cerr << "AudioCallbackPlaySource::addModel: emitting modelReplaced" << std::endl;
150 #endif
151 emit modelReplaced();
152
153 if (srChanged && (getSourceSampleRate() != getTargetSampleRate())) {
154 emit sampleRateMismatch(getSourceSampleRate(), getTargetSampleRate());
155 }
156 }
157
158 void
159 AudioCallbackPlaySource::removeModel(Model *model)
160 {
161 m_mutex.lock();
162
163 m_models.erase(model);
164
165 if (m_models.empty()) {
166 if (m_converter) {
167 src_delete(m_converter);
168 m_converter = 0;
169 }
170 m_sourceSampleRate = 0;
171 }
172
173 m_audioGenerator->removeModel(model);
174
175 m_mutex.unlock();
176 }
177
178 void
179 AudioCallbackPlaySource::clearModels()
180 {
181 m_mutex.lock();
182
183 m_models.clear();
184
185 if (m_converter) {
186 src_delete(m_converter);
187 m_converter = 0;
188 }
189
190 m_audioGenerator->clearModels();
191
192 m_sourceSampleRate = 0;
193
194 m_mutex.unlock();
195 }
196
197 void
198 AudioCallbackPlaySource::play(size_t startFrame)
199 {
200 // The fill thread will automatically empty its buffers before
201 // starting again if we have not so far been playing, but not if
202 // we're just re-seeking.
203
204 if (m_playing) {
205 m_mutex.lock();
206 m_bufferedToFrame = startFrame;
207 for (size_t c = 0; c < m_bufferCount; ++c) {
208 getRingBuffer(c).reset();
209 if (m_converter) src_reset(m_converter);
210 }
211 m_mutex.unlock();
212 } else {
213 m_bufferedToFrame = startFrame;
214 }
215
216 m_audioGenerator->reset();
217
218 m_playing = true;
219 m_condition.wakeAll();
220 }
221
222 void
223 AudioCallbackPlaySource::stop()
224 {
225 m_playing = false;
226 m_condition.wakeAll();
227 }
228
229 void
230 AudioCallbackPlaySource::setTargetBlockSize(size_t size)
231 {
232 std::cerr << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl;
233 m_blockSize = size;
234 for (size_t i = 0; i < m_bufferCount; ++i) {
235 getRingBuffer(i).resize(m_ringBufferSize);
236 }
237 }
238
239 size_t
240 AudioCallbackPlaySource::getTargetBlockSize() const
241 {
242 std::cerr << "AudioCallbackPlaySource::getTargetBlockSize() -> " << m_blockSize << std::endl;
243 return m_blockSize;
244 }
245
246 void
247 AudioCallbackPlaySource::setTargetPlayLatency(size_t latency)
248 {
249 m_playLatency = latency;
250 }
251
252 size_t
253 AudioCallbackPlaySource::getTargetPlayLatency() const
254 {
255 return m_playLatency;
256 }
257
258 size_t
259 AudioCallbackPlaySource::getCurrentPlayingFrame()
260 {
261 bool resample = false;
262 double ratio = 1.0;
263
264 if (getSourceSampleRate() != getTargetSampleRate()) {
265 resample = true;
266 ratio = double(getSourceSampleRate()) / double(getTargetSampleRate());
267 }
268
269 size_t readSpace = 0;
270 for (size_t c = 0; c < getSourceChannelCount(); ++c) {
271 size_t spaceHere = getRingBuffer(c).getReadSpace();
272 if (c == 0 || spaceHere < readSpace) readSpace = spaceHere;
273 }
274
275 if (resample) {
276 readSpace = size_t(readSpace * ratio + 0.1);
277 }
278
279 size_t lastRequestedFrame = 0;
280 if (m_bufferedToFrame > readSpace) {
281 lastRequestedFrame = m_bufferedToFrame - readSpace;
282 }
283
284 size_t framePlaying = lastRequestedFrame;
285
286 size_t latency = m_playLatency;
287 if (resample) latency = size_t(m_playLatency * ratio + 0.1);
288
289 TimeStretcherData *timeStretcher = m_timeStretcher;
290 if (timeStretcher) {
291 latency += timeStretcher->getStretcher(0)->getProcessingLatency();
292 }
293
294 if (framePlaying > latency) {
295 framePlaying = framePlaying - latency;
296 } else {
297 framePlaying = 0;
298 }
299
300 #ifdef DEBUG_AUDIO_PLAY_SOURCE
301 std::cout << "getCurrentPlayingFrame: readSpace " << readSpace << ", lastRequestedFrame " << lastRequestedFrame << ", framePlaying " << framePlaying << ", latency " << latency << std::endl;
302 #endif
303
304 return framePlaying;
305 }
306
307 void
308 AudioCallbackPlaySource::setOutputLevels(float left, float right)
309 {
310 m_outputLeft = left;
311 m_outputRight = right;
312 }
313
314 bool
315 AudioCallbackPlaySource::getOutputLevels(float &left, float &right)
316 {
317 left = m_outputLeft;
318 right = m_outputRight;
319 return true;
320 }
321
322 void
323 AudioCallbackPlaySource::setTargetSampleRate(size_t sr)
324 {
325 m_targetSampleRate = sr;
326 }
327
328 size_t
329 AudioCallbackPlaySource::getTargetSampleRate() const
330 {
331 if (m_targetSampleRate) return m_targetSampleRate;
332 else return getSourceSampleRate();
333 }
334
335 size_t
336 AudioCallbackPlaySource::getSourceChannelCount() const
337 {
338 return m_bufferCount;
339 }
340
341 size_t
342 AudioCallbackPlaySource::getSourceSampleRate() const
343 {
344 return m_sourceSampleRate;
345 }
346
347 AudioCallbackPlaySource::TimeStretcherData::TimeStretcherData(size_t channels,
348 size_t factor,
349 size_t blockSize) :
350 m_factor(factor),
351 m_blockSize(blockSize)
352 {
353 std::cerr << "TimeStretcherData::TimeStretcherData(" << channels << ", " << factor << ", " << blockSize << ")" << std::endl;
354
355 for (size_t ch = 0; ch < channels; ++ch) {
356 m_stretcher[ch] = StretcherBuffer
357 //!!! We really need to measure performance and work out
358 //what sort of quality level to use -- or at least to
359 //allow the user to configure it
360 (new IntegerTimeStretcher(factor, blockSize, 128),
361 new double[blockSize * factor]);
362 }
363 m_stretchInputBuffer = new double[blockSize];
364 }
365
366 AudioCallbackPlaySource::TimeStretcherData::~TimeStretcherData()
367 {
368 std::cerr << "IntegerTimeStretcher::~IntegerTimeStretcher" << std::endl;
369
370 while (!m_stretcher.empty()) {
371 delete m_stretcher.begin()->second.first;
372 delete[] m_stretcher.begin()->second.second;
373 m_stretcher.erase(m_stretcher.begin());
374 }
375 delete m_stretchInputBuffer;
376 }
377
378 IntegerTimeStretcher *
379 AudioCallbackPlaySource::TimeStretcherData::getStretcher(size_t channel)
380 {
381 return m_stretcher[channel].first;
382 }
383
384 double *
385 AudioCallbackPlaySource::TimeStretcherData::getOutputBuffer(size_t channel)
386 {
387 return m_stretcher[channel].second;
388 }
389
390 double *
391 AudioCallbackPlaySource::TimeStretcherData::getInputBuffer()
392 {
393 return m_stretchInputBuffer;
394 }
395
396 void
397 AudioCallbackPlaySource::TimeStretcherData::run(size_t channel)
398 {
399 getStretcher(channel)->process(getInputBuffer(),
400 getOutputBuffer(channel),
401 m_blockSize);
402 }
403
404 void
405 AudioCallbackPlaySource::setSlowdownFactor(size_t factor)
406 {
407 // Avoid locks -- create, assign, mark old one for scavenging
408 // later (as a call to getSourceSamples may still be using it)
409
410 TimeStretcherData *existingStretcher = m_timeStretcher;
411
412 if (existingStretcher && existingStretcher->getFactor() == factor) {
413 return;
414 }
415
416 if (factor > 1) {
417 TimeStretcherData *newStretcher = new TimeStretcherData
418 (getSourceChannelCount(), factor, getTargetBlockSize());
419 m_slowdownCounter = 0;
420 m_timeStretcher = newStretcher;
421 } else {
422 m_timeStretcher = 0;
423 }
424
425 if (existingStretcher) {
426 m_timeStretcherScavenger.claim(existingStretcher);
427 }
428 }
429
430 size_t
431 AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer)
432 {
433 if (!m_playing) {
434 for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) {
435 for (size_t i = 0; i < count; ++i) {
436 buffer[ch][i] = 0.0;
437 }
438 }
439 return 0;
440 }
441
442 TimeStretcherData *timeStretcher = m_timeStretcher;
443
444 if (!timeStretcher || timeStretcher->getFactor() == 1) {
445
446 size_t got = 0;
447
448 for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) {
449
450 RingBuffer<float> &rb = *m_buffers[ch];
451
452 // this is marginally more likely to leave our channels in
453 // sync after a processing failure than just passing "count":
454 size_t request = count;
455 if (ch > 0) request = got;
456
457 got = rb.read(buffer[ch], request);
458
459 #ifdef DEBUG_AUDIO_PLAY_SOURCE
460 std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " samples on channel " << ch << ", signalling for more (possibly)" << std::endl;
461 #endif
462 }
463
464 for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) {
465 for (size_t i = got; i < count; ++i) {
466 buffer[ch][i] = 0.0;
467 }
468 }
469
470 m_condition.wakeAll();
471 return got;
472 }
473
474 if (m_slowdownCounter == 0) {
475
476 size_t got = 0;
477 double *ib = timeStretcher->getInputBuffer();
478
479 for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) {
480
481 RingBuffer<float> &rb = *m_buffers[ch];
482 size_t request = count;
483 if (ch > 0) request = got; // see above
484 got = rb.read(buffer[ch], request);
485
486 #ifdef DEBUG_AUDIO_PLAY_SOURCE
487 std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " samples on channel " << ch << ", running time stretcher" << std::endl;
488 #endif
489
490 for (size_t i = 0; i < count; ++i) {
491 ib[i] = buffer[ch][i];
492 }
493
494 timeStretcher->run(ch);
495 }
496
497 } else if (m_slowdownCounter >= timeStretcher->getFactor()) {
498 // reset this in case the factor has changed leaving the
499 // counter out of range
500 m_slowdownCounter = 0;
501 }
502
503 for (size_t ch = 0; ch < getSourceChannelCount(); ++ch) {
504
505 double *ob = timeStretcher->getOutputBuffer(ch);
506
507 #ifdef DEBUG_AUDIO_PLAY_SOURCE
508 std::cerr << "AudioCallbackPlaySource::getSamples: Copying from (" << (m_slowdownCounter * count) << "," << count << ") to buffer" << std::endl;
509 #endif
510
511 for (size_t i = 0; i < count; ++i) {
512 buffer[ch][i] = ob[m_slowdownCounter * count + i];
513 }
514 }
515
516 if (m_slowdownCounter == 0) m_condition.wakeAll();
517 m_slowdownCounter = (m_slowdownCounter + 1) % timeStretcher->getFactor();
518 return count;
519 }
520
521 void
522 AudioCallbackPlaySource::fillBuffers()
523 {
524 static float *tmp = 0;
525 static size_t tmpSize = 0;
526
527 size_t space = 0;
528 for (size_t c = 0; c < m_bufferCount; ++c) {
529 size_t spaceHere = getRingBuffer(c).getWriteSpace();
530 if (c == 0 || spaceHere < space) space = spaceHere;
531 }
532
533 if (space == 0) return;
534
535 #ifdef DEBUG_AUDIO_PLAY_SOURCE
536 std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl;
537 #endif
538
539 size_t f = m_bufferedToFrame;
540
541 #ifdef DEBUG_AUDIO_PLAY_SOURCE
542 std::cout << "buffered to " << f << " already" << std::endl;
543 #endif
544
545 bool resample = (getSourceSampleRate() != getTargetSampleRate());
546 size_t channels = getSourceChannelCount();
547 size_t orig = space;
548 size_t got = 0;
549
550 static float **bufferPtrs = 0;
551 static size_t bufferPtrCount = 0;
552
553 if (bufferPtrCount < channels) {
554 if (bufferPtrs) delete[] bufferPtrs;
555 bufferPtrs = new float *[channels];
556 bufferPtrCount = channels;
557 }
558
559 size_t generatorBlockSize = m_audioGenerator->getBlockSize();
560
561 if (resample && m_converter) {
562
563 double ratio =
564 double(getTargetSampleRate()) / double(getSourceSampleRate());
565 orig = size_t(orig / ratio + 0.1);
566
567 // orig must be a multiple of generatorBlockSize
568 orig = (orig / generatorBlockSize) * generatorBlockSize;
569 if (orig == 0) return;
570
571 size_t work = std::max(orig, space);
572
573 // We only allocate one buffer, but we use it in two halves.
574 // We place the non-interleaved values in the second half of
575 // the buffer (orig samples for channel 0, orig samples for
576 // channel 1 etc), and then interleave them into the first
577 // half of the buffer. Then we resample back into the second
578 // half (interleaved) and de-interleave the results back to
579 // the start of the buffer for insertion into the ringbuffers.
580 // What a faff -- especially as we've already de-interleaved
581 // the audio data from the source file elsewhere before we
582 // even reach this point.
583
584 if (tmpSize < channels * work * 2) {
585 delete[] tmp;
586 tmp = new float[channels * work * 2];
587 tmpSize = channels * work * 2;
588 }
589
590 float *nonintlv = tmp + channels * work;
591 float *intlv = tmp;
592 float *srcout = tmp + channels * work;
593
594 for (size_t c = 0; c < channels; ++c) {
595 for (size_t i = 0; i < orig; ++i) {
596 nonintlv[channels * i + c] = 0.0f;
597 }
598 }
599
600 for (std::set<Model *>::iterator mi = m_models.begin();
601 mi != m_models.end(); ++mi) {
602
603 for (size_t c = 0; c < channels; ++c) {
604 bufferPtrs[c] = nonintlv + c * orig;
605 }
606
607 size_t gotHere = m_audioGenerator->mixModel
608 (*mi, f, orig, bufferPtrs);
609
610 got = std::max(got, gotHere);
611 }
612
613 // and interleave into first half
614 for (size_t c = 0; c < channels; ++c) {
615 for (size_t i = 0; i < orig; ++i) {
616 float sample = 0;
617 if (i < got) {
618 sample = nonintlv[c * orig + i];
619 }
620 intlv[channels * i + c] = sample;
621 }
622 }
623
624 SRC_DATA data;
625 data.data_in = intlv;
626 data.data_out = srcout;
627 data.input_frames = orig;
628 data.output_frames = work;
629 data.src_ratio = ratio;
630 data.end_of_input = 0;
631
632 int err = src_process(m_converter, &data);
633 size_t toCopy = size_t(work * ratio + 0.1);
634
635 if (err) {
636 std::cerr
637 << "AudioCallbackPlaySourceFillThread: ERROR in samplerate conversion: "
638 << src_strerror(err) << std::endl;
639 //!!! Then what?
640 } else {
641 got = data.input_frames_used;
642 toCopy = data.output_frames_gen;
643 #ifdef DEBUG_AUDIO_PLAY_SOURCE
644 std::cerr << "Resampled " << got << " frames to " << toCopy << " frames" << std::endl;
645 #endif
646 }
647
648 for (size_t c = 0; c < channels; ++c) {
649 for (size_t i = 0; i < toCopy; ++i) {
650 tmp[i] = srcout[channels * i + c];
651 }
652 getRingBuffer(c).write(tmp, toCopy);
653 }
654
655 } else {
656
657 // space must be a multiple of generatorBlockSize
658 space = (space / generatorBlockSize) * generatorBlockSize;
659 if (space == 0) return;
660
661 if (tmpSize < channels * space) {
662 delete[] tmp;
663 tmp = new float[channels * space];
664 tmpSize = channels * space;
665 }
666
667 for (size_t c = 0; c < channels; ++c) {
668
669 bufferPtrs[c] = tmp + c * space;
670
671 for (size_t i = 0; i < space; ++i) {
672 tmp[c * space + i] = 0.0f;
673 }
674 }
675
676 for (std::set<Model *>::iterator mi = m_models.begin();
677 mi != m_models.end(); ++mi) {
678
679 got = m_audioGenerator->mixModel
680 (*mi, f, space, bufferPtrs);
681 }
682
683 for (size_t c = 0; c < channels; ++c) {
684
685 got = getRingBuffer(c).write(bufferPtrs[c], space);
686
687 #ifdef DEBUG_AUDIO_PLAY_SOURCE
688 std::cerr << "Wrote " << got << " frames for ch " << c << ", now "
689 << getRingBuffer(c).getReadSpace() << " to read"
690 << std::endl;
691 #endif
692 }
693 }
694
695 m_bufferedToFrame = f + got;
696 }
697
698 void
699 AudioCallbackPlaySource::AudioCallbackPlaySourceFillThread::run()
700 {
701 AudioCallbackPlaySource &s(m_source);
702
703 #ifdef DEBUG_AUDIO_PLAY_SOURCE
704 std::cerr << "AudioCallbackPlaySourceFillThread starting" << std::endl;
705 #endif
706
707 s.m_mutex.lock();
708
709 bool previouslyPlaying = s.m_playing;
710
711 while (!s.m_exiting) {
712
713 s.m_timeStretcherScavenger.scavenge();
714
715 float ms = 100;
716 if (s.getSourceSampleRate() > 0) {
717 ms = float(m_ringBufferSize) / float(s.getSourceSampleRate()) * 1000.0;
718 }
719
720 if (!s.m_playing) ms *= 10;
721
722 #ifdef DEBUG_AUDIO_PLAY_SOURCE
723 std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms/4 << "ms..." << std::endl;
724 #endif
725
726 s.m_condition.wait(&s.m_mutex, size_t(ms / 4));
727
728 #ifdef DEBUG_AUDIO_PLAY_SOURCE
729 std::cout << "AudioCallbackPlaySourceFillThread: awoken" << std::endl;
730 #endif
731
732 if (!s.getSourceSampleRate()) continue;
733
734 bool playing = s.m_playing;
735
736 if (playing && !previouslyPlaying) {
737 #ifdef DEBUG_AUDIO_PLAY_SOURCE
738 std::cout << "AudioCallbackPlaySourceFillThread: playback state changed, resetting" << std::endl;
739 #endif
740 for (size_t c = 0; c < s.getSourceChannelCount(); ++c) {
741 s.getRingBuffer(c).reset();
742 }
743 }
744 previouslyPlaying = playing;
745
746 if (!playing) continue;
747
748 s.fillBuffers();
749 }
750
751 s.m_mutex.unlock();
752 }
753
754
755
756 #ifdef INCLUDE_MOCFILES
757 #include "AudioCallbackPlaySource.moc.cpp"
758 #endif
759