Mercurial > hg > svapp
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 |