Mercurial > hg > svapp
comparison audioio/AudioCallbackPlaySource.cpp @ 3:75c3ea1c3a32
* Add play-selection and looping modes. Looping seems to work OK, but
the plain play-selection is miscalculating current frame number to
feed back to the GUI.
* Cache selection rectanges wherever possible in View::paintEvent.
author | Chris Cannam |
---|---|
date | Tue, 24 Jan 2006 16:20:58 +0000 |
parents | 97c69acdcb82 |
children | 5865094175ea |
comparison
equal
deleted
inserted
replaced
2:df5923e33d01 | 3:75c3ea1c3a32 |
---|---|
45 // preallocate some slots, to avoid reallocation in an | 45 // preallocate some slots, to avoid reallocation in an |
46 // un-thread-safe manner later | 46 // un-thread-safe manner later |
47 while (m_buffers.size() < 20) m_buffers.push_back(0); | 47 while (m_buffers.size() < 20) m_buffers.push_back(0); |
48 | 48 |
49 m_viewManager->setAudioPlaySource(this); | 49 m_viewManager->setAudioPlaySource(this); |
50 | |
51 connect(m_viewManager, SIGNAL(selectionChanged()), | |
52 this, SLOT(selectionChanged())); | |
53 connect(m_viewManager, SIGNAL(playLoopModeChanged()), | |
54 this, SLOT(playLoopModeChanged())); | |
55 connect(m_viewManager, SIGNAL(playSelectionModeChanged()), | |
56 this, SLOT(playSelectionModeChanged())); | |
50 } | 57 } |
51 | 58 |
52 AudioCallbackPlaySource::~AudioCallbackPlaySource() | 59 AudioCallbackPlaySource::~AudioCallbackPlaySource() |
53 { | 60 { |
54 m_exiting = true; | 61 m_exiting = true; |
212 m_playing = false; | 219 m_playing = false; |
213 m_condition.wakeAll(); | 220 m_condition.wakeAll(); |
214 } | 221 } |
215 | 222 |
216 void | 223 void |
224 AudioCallbackPlaySource::selectionChanged() | |
225 { | |
226 if (m_viewManager->getPlaySelectionMode()) { | |
227 m_mutex.lock(); | |
228 for (size_t c = 0; c < m_bufferCount; ++c) { | |
229 getRingBuffer(c).reset(); | |
230 } | |
231 m_mutex.unlock(); | |
232 } | |
233 } | |
234 | |
235 void | |
236 AudioCallbackPlaySource::playLoopModeChanged() | |
237 { | |
238 m_mutex.lock(); | |
239 for (size_t c = 0; c < m_bufferCount; ++c) { | |
240 getRingBuffer(c).reset(); | |
241 } | |
242 m_mutex.unlock(); | |
243 } | |
244 | |
245 void | |
246 AudioCallbackPlaySource::playSelectionModeChanged() | |
247 { | |
248 if (!m_viewManager->getSelections().empty()) { | |
249 m_mutex.lock(); | |
250 for (size_t c = 0; c < m_bufferCount; ++c) { | |
251 getRingBuffer(c).reset(); | |
252 } | |
253 m_mutex.unlock(); | |
254 } | |
255 } | |
256 | |
257 void | |
217 AudioCallbackPlaySource::setTargetBlockSize(size_t size) | 258 AudioCallbackPlaySource::setTargetBlockSize(size_t size) |
218 { | 259 { |
219 std::cerr << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; | 260 std::cerr << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; |
220 m_blockSize = size; | 261 m_blockSize = size; |
221 for (size_t i = 0; i < m_bufferCount; ++i) { | 262 for (size_t i = 0; i < m_bufferCount; ++i) { |
260 } | 301 } |
261 | 302 |
262 if (resample) { | 303 if (resample) { |
263 readSpace = size_t(readSpace * ratio + 0.1); | 304 readSpace = size_t(readSpace * ratio + 0.1); |
264 } | 305 } |
265 | |
266 size_t lastRequestedFrame = 0; | |
267 if (m_bufferedToFrame > readSpace) { | |
268 lastRequestedFrame = m_bufferedToFrame - readSpace; | |
269 } | |
270 | |
271 size_t framePlaying = lastRequestedFrame; | |
272 | 306 |
273 size_t latency = m_playLatency; | 307 size_t latency = m_playLatency; |
274 if (resample) latency = size_t(m_playLatency * ratio + 0.1); | 308 if (resample) latency = size_t(m_playLatency * ratio + 0.1); |
275 | 309 |
276 TimeStretcherData *timeStretcher = m_timeStretcher; | 310 TimeStretcherData *timeStretcher = m_timeStretcher; |
277 if (timeStretcher) { | 311 if (timeStretcher) { |
278 latency += timeStretcher->getStretcher(0)->getProcessingLatency(); | 312 latency += timeStretcher->getStretcher(0)->getProcessingLatency(); |
279 } | 313 } |
280 | 314 |
281 if (framePlaying > latency) { | 315 latency += readSpace; |
282 framePlaying = framePlaying - latency; | 316 size_t bufferedFrame = m_bufferedToFrame; |
283 } else { | 317 |
284 framePlaying = 0; | 318 size_t framePlaying = bufferedFrame; |
285 } | 319 if (framePlaying > latency) framePlaying -= latency; |
286 | 320 else framePlaying = 0; |
287 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 321 |
288 std::cout << "getCurrentPlayingFrame: readSpace " << readSpace << ", lastRequestedFrame " << lastRequestedFrame << ", framePlaying " << framePlaying << ", latency " << latency << std::endl; | 322 if (!m_viewManager->getPlaySelectionMode()) { |
289 #endif | 323 return framePlaying; |
324 } | |
325 | |
326 ViewManager::SelectionList selections = m_viewManager->getSelections(); | |
327 if (selections.empty()) { | |
328 return framePlaying; | |
329 } | |
330 | |
331 ViewManager::SelectionList::const_iterator i; | |
332 | |
333 for (i = selections.begin(); i != selections.end(); ++i) { | |
334 if (i->contains(bufferedFrame)) break; | |
335 } | |
336 | |
337 size_t f = bufferedFrame; | |
338 | |
339 std::cerr << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << std::endl; | |
340 | |
341 if (i == selections.end()) { | |
342 --i; | |
343 if (i->getEndFrame() + latency < f) { | |
344 return framePlaying; | |
345 } else { | |
346 std::cerr << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; | |
347 latency -= (f - i->getEndFrame()); | |
348 f = i->getEndFrame(); | |
349 } | |
350 } | |
351 | |
352 std::cerr << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; | |
353 | |
354 while (latency > 0) { | |
355 size_t offset = f - i->getStartFrame(); | |
356 if (offset >= latency) { | |
357 if (f > latency) { | |
358 framePlaying = f - latency; | |
359 } else { | |
360 framePlaying = 0; | |
361 } | |
362 break; | |
363 } else { | |
364 if (i == selections.begin()) { | |
365 if (m_viewManager->getPlayLoopMode()) { | |
366 i = selections.end(); | |
367 } | |
368 } | |
369 latency -= offset; | |
370 --i; | |
371 f = i->getEndFrame(); | |
372 } | |
373 } | |
290 | 374 |
291 return framePlaying; | 375 return framePlaying; |
292 } | 376 } |
293 | 377 |
294 void | 378 void |
608 for (size_t i = 0; i < orig; ++i) { | 692 for (size_t i = 0; i < orig; ++i) { |
609 nonintlv[channels * i + c] = 0.0f; | 693 nonintlv[channels * i + c] = 0.0f; |
610 } | 694 } |
611 } | 695 } |
612 | 696 |
613 for (std::set<Model *>::iterator mi = m_models.begin(); | 697 for (size_t c = 0; c < channels; ++c) { |
614 mi != m_models.end(); ++mi) { | 698 bufferPtrs[c] = nonintlv + c * orig; |
615 | 699 } |
616 for (size_t c = 0; c < channels; ++c) { | 700 |
617 bufferPtrs[c] = nonintlv + c * orig; | 701 bool ended = !mixModels(f, orig, bufferPtrs); |
618 } | 702 got = orig; |
619 | |
620 size_t gotHere = m_audioGenerator->mixModel | |
621 (*mi, f, orig, bufferPtrs); | |
622 | |
623 got = std::max(got, gotHere); | |
624 } | |
625 | 703 |
626 // and interleave into first half | 704 // and interleave into first half |
627 for (size_t c = 0; c < channels; ++c) { | 705 for (size_t c = 0; c < channels; ++c) { |
628 for (size_t i = 0; i < orig; ++i) { | 706 for (size_t i = 0; i < orig; ++i) { |
629 float sample = 0; | 707 float sample = 0; |
662 for (size_t i = 0; i < toCopy; ++i) { | 740 for (size_t i = 0; i < toCopy; ++i) { |
663 tmp[i] = srcout[channels * i + c]; | 741 tmp[i] = srcout[channels * i + c]; |
664 } | 742 } |
665 getRingBuffer(c).write(tmp, toCopy); | 743 getRingBuffer(c).write(tmp, toCopy); |
666 } | 744 } |
745 | |
746 m_bufferedToFrame = f; | |
667 | 747 |
668 } else { | 748 } else { |
669 | 749 |
670 // space must be a multiple of generatorBlockSize | 750 // space must be a multiple of generatorBlockSize |
671 space = (space / generatorBlockSize) * generatorBlockSize; | 751 space = (space / generatorBlockSize) * generatorBlockSize; |
678 } | 758 } |
679 | 759 |
680 for (size_t c = 0; c < channels; ++c) { | 760 for (size_t c = 0; c < channels; ++c) { |
681 | 761 |
682 bufferPtrs[c] = tmp + c * space; | 762 bufferPtrs[c] = tmp + c * space; |
683 | 763 |
684 for (size_t i = 0; i < space; ++i) { | 764 for (size_t i = 0; i < space; ++i) { |
685 tmp[c * space + i] = 0.0f; | 765 tmp[c * space + i] = 0.0f; |
686 } | 766 } |
687 } | 767 } |
688 | 768 |
689 for (std::set<Model *>::iterator mi = m_models.begin(); | 769 bool ended = !mixModels(f, space, bufferPtrs); |
690 mi != m_models.end(); ++mi) { | |
691 | |
692 got = m_audioGenerator->mixModel | |
693 (*mi, f, space, bufferPtrs); | |
694 } | |
695 | 770 |
696 for (size_t c = 0; c < channels; ++c) { | 771 for (size_t c = 0; c < channels; ++c) { |
697 | 772 |
698 got = getRingBuffer(c).write(bufferPtrs[c], space); | 773 getRingBuffer(c).write(bufferPtrs[c], space); |
699 | 774 |
700 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 775 #ifdef DEBUG_AUDIO_PLAY_SOURCE |
701 std::cerr << "Wrote " << got << " frames for ch " << c << ", now " | 776 std::cerr << "Wrote " << got << " frames for ch " << c << ", now " |
702 << getRingBuffer(c).getReadSpace() << " to read" | 777 << getRingBuffer(c).getReadSpace() << " to read" |
703 << std::endl; | 778 << std::endl; |
704 #endif | 779 #endif |
705 } | 780 } |
706 } | 781 |
782 m_bufferedToFrame = f; | |
783 //!!! how do we know when ended? need to mark up a fully-buffered flag and check this if we find the buffers empty in getSourceSamples | |
784 } | |
785 } | |
786 | |
787 bool | |
788 AudioCallbackPlaySource::mixModels(size_t &frame, size_t count, float **buffers) | |
789 { | |
790 size_t processed = 0; | |
791 size_t chunkStart = frame; | |
792 size_t chunkSize = count; | |
793 size_t nextChunkStart = chunkStart + chunkSize; | |
707 | 794 |
708 m_bufferedToFrame = f + got; | 795 bool useSelection = (m_viewManager->getPlaySelectionMode() && |
709 } | 796 !m_viewManager->getSelections().empty()); |
797 | |
798 static float **chunkBufferPtrs = 0; | |
799 static size_t chunkBufferPtrCount = 0; | |
800 size_t channels = getSourceChannelCount(); | |
801 | |
802 #ifdef DEBUG_AUDIO_PLAY_SOURCE | |
803 std::cerr << "Selection playback: start " << frame << ", size " << count <<", channels " << channels << std::endl; | |
804 #endif | |
805 | |
806 if (chunkBufferPtrCount < channels) { | |
807 if (chunkBufferPtrs) delete[] chunkBufferPtrs; | |
808 chunkBufferPtrs = new float *[channels]; | |
809 chunkBufferPtrCount = channels; | |
810 } | |
811 | |
812 for (size_t c = 0; c < channels; ++c) { | |
813 chunkBufferPtrs[c] = buffers[c]; | |
814 } | |
815 | |
816 while (processed < count) { | |
817 | |
818 chunkSize = count - processed; | |
819 nextChunkStart = chunkStart + chunkSize; | |
820 | |
821 if (useSelection) { | |
822 | |
823 Selection selection = | |
824 m_viewManager->getContainingSelection(chunkStart, true); | |
825 | |
826 if (selection.isEmpty()) { | |
827 if (m_viewManager->getPlayLoopMode()) { | |
828 selection = *m_viewManager->getSelections().begin(); | |
829 chunkStart = selection.getStartFrame(); | |
830 } | |
831 } | |
832 | |
833 if (selection.isEmpty()) { | |
834 | |
835 chunkSize = 0; | |
836 nextChunkStart = chunkStart; | |
837 | |
838 } else { | |
839 | |
840 if (chunkStart < selection.getStartFrame()) { | |
841 chunkStart = selection.getStartFrame(); | |
842 } | |
843 | |
844 nextChunkStart = std::min(chunkStart + chunkSize, | |
845 selection.getEndFrame()); | |
846 | |
847 chunkSize = nextChunkStart - chunkStart; | |
848 } | |
849 } | |
850 | |
851 if (!chunkSize) { | |
852 #ifdef DEBUG_AUDIO_PLAY_SOURCE | |
853 std::cerr << "Ending selection playback at " << nextChunkStart << std::endl; | |
854 #endif | |
855 // We need to maintain full buffers so that the other | |
856 // thread can tell where it's got to in the playback -- so | |
857 // return the full amount here | |
858 frame = frame + count; | |
859 return false; | |
860 } | |
861 | |
862 #ifdef DEBUG_AUDIO_PLAY_SOURCE | |
863 std::cerr << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << std::endl; | |
864 #endif | |
865 | |
866 size_t got = 0; | |
867 | |
868 for (std::set<Model *>::iterator mi = m_models.begin(); | |
869 mi != m_models.end(); ++mi) { | |
870 | |
871 got = m_audioGenerator->mixModel(*mi, chunkStart, | |
872 chunkSize, chunkBufferPtrs); | |
873 } | |
874 | |
875 for (size_t c = 0; c < channels; ++c) { | |
876 chunkBufferPtrs[c] += chunkSize; | |
877 } | |
878 | |
879 processed += chunkSize; | |
880 chunkStart = nextChunkStart; | |
881 } | |
882 | |
883 #ifdef DEBUG_AUDIO_PLAY_SOURCE | |
884 std::cerr << "Returning selection playback at " << nextChunkStart << std::endl; | |
885 #endif | |
886 | |
887 frame = nextChunkStart; | |
888 return true; | |
889 } | |
710 | 890 |
711 void | 891 void |
712 AudioCallbackPlaySource::AudioCallbackPlaySourceFillThread::run() | 892 AudioCallbackPlaySource::AudioCallbackPlaySourceFillThread::run() |
713 { | 893 { |
714 AudioCallbackPlaySource &s(m_source); | 894 AudioCallbackPlaySource &s(m_source); |