| Chris@43 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@43 | 2 | 
| Chris@43 | 3 /* | 
| Chris@43 | 4     Sonic Visualiser | 
| Chris@43 | 5     An audio file viewer and annotation editor. | 
| Chris@43 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@43 | 7     This file copyright 2006 Chris Cannam and QMUL. | 
| Chris@43 | 8 | 
| Chris@43 | 9     This program is free software; you can redistribute it and/or | 
| Chris@43 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@43 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@43 | 12     License, or (at your option) any later version.  See the file | 
| Chris@43 | 13     COPYING included with this distribution for more information. | 
| Chris@43 | 14 */ | 
| Chris@43 | 15 | 
| Chris@43 | 16 #include "AudioCallbackPlaySource.h" | 
| Chris@43 | 17 | 
| Chris@43 | 18 #include "AudioGenerator.h" | 
| Chris@43 | 19 | 
| Chris@43 | 20 #include "data/model/Model.h" | 
| Chris@105 | 21 #include "base/ViewManagerBase.h" | 
| Chris@43 | 22 #include "base/PlayParameterRepository.h" | 
| Chris@43 | 23 #include "base/Preferences.h" | 
| Chris@43 | 24 #include "data/model/DenseTimeValueModel.h" | 
| Chris@43 | 25 #include "data/model/WaveFileModel.h" | 
| Chris@43 | 26 #include "data/model/SparseOneDimensionalModel.h" | 
| Chris@43 | 27 #include "plugin/RealTimePluginInstance.h" | 
| Chris@62 | 28 | 
| Chris@91 | 29 #include "AudioCallbackPlayTarget.h" | 
| Chris@91 | 30 | 
| Chris@62 | 31 #include <rubberband/RubberBandStretcher.h> | 
| Chris@62 | 32 using namespace RubberBand; | 
| Chris@43 | 33 | 
| Chris@43 | 34 #include <iostream> | 
| Chris@43 | 35 #include <cassert> | 
| Chris@43 | 36 | 
| Chris@174 | 37 //#define DEBUG_AUDIO_PLAY_SOURCE 1 | 
| Chris@43 | 38 //#define DEBUG_AUDIO_PLAY_SOURCE_PLAYING 1 | 
| Chris@43 | 39 | 
| Chris@43 | 40 const size_t AudioCallbackPlaySource::m_ringBufferSize = 131071; | 
| Chris@43 | 41 | 
| Chris@105 | 42 AudioCallbackPlaySource::AudioCallbackPlaySource(ViewManagerBase *manager, | 
| Chris@57 | 43                                                  QString clientName) : | 
| Chris@43 | 44     m_viewManager(manager), | 
| Chris@43 | 45     m_audioGenerator(new AudioGenerator()), | 
| Chris@57 | 46     m_clientName(clientName), | 
| Chris@43 | 47     m_readBuffers(0), | 
| Chris@43 | 48     m_writeBuffers(0), | 
| Chris@43 | 49     m_readBufferFill(0), | 
| Chris@43 | 50     m_writeBufferFill(0), | 
| Chris@43 | 51     m_bufferScavenger(1), | 
| Chris@43 | 52     m_sourceChannelCount(0), | 
| Chris@43 | 53     m_blockSize(1024), | 
| Chris@43 | 54     m_sourceSampleRate(0), | 
| Chris@43 | 55     m_targetSampleRate(0), | 
| Chris@43 | 56     m_playLatency(0), | 
| Chris@91 | 57     m_target(0), | 
| Chris@91 | 58     m_lastRetrievalTimestamp(0.0), | 
| Chris@91 | 59     m_lastRetrievedBlockSize(0), | 
| Chris@102 | 60     m_trustworthyTimestamps(true), | 
| Chris@102 | 61     m_lastCurrentFrame(0), | 
| Chris@43 | 62     m_playing(false), | 
| Chris@43 | 63     m_exiting(false), | 
| Chris@43 | 64     m_lastModelEndFrame(0), | 
| Chris@43 | 65     m_outputLeft(0.0), | 
| Chris@43 | 66     m_outputRight(0.0), | 
| Chris@43 | 67     m_auditioningPlugin(0), | 
| Chris@43 | 68     m_auditioningPluginBypassed(false), | 
| Chris@94 | 69     m_playStartFrame(0), | 
| Chris@94 | 70     m_playStartFramePassed(false), | 
| Chris@43 | 71     m_timeStretcher(0), | 
| Chris@130 | 72     m_monoStretcher(0), | 
| Chris@91 | 73     m_stretchRatio(1.0), | 
| Chris@91 | 74     m_stretcherInputCount(0), | 
| Chris@91 | 75     m_stretcherInputs(0), | 
| Chris@91 | 76     m_stretcherInputSizes(0), | 
| Chris@43 | 77     m_fillThread(0), | 
| Chris@43 | 78     m_converter(0), | 
| Chris@43 | 79     m_crapConverter(0), | 
| Chris@43 | 80     m_resampleQuality(Preferences::getInstance()->getResampleQuality()) | 
| Chris@43 | 81 { | 
| Chris@43 | 82     m_viewManager->setAudioPlaySource(this); | 
| Chris@43 | 83 | 
| Chris@43 | 84     connect(m_viewManager, SIGNAL(selectionChanged()), | 
| Chris@43 | 85 	    this, SLOT(selectionChanged())); | 
| Chris@43 | 86     connect(m_viewManager, SIGNAL(playLoopModeChanged()), | 
| Chris@43 | 87 	    this, SLOT(playLoopModeChanged())); | 
| Chris@43 | 88     connect(m_viewManager, SIGNAL(playSelectionModeChanged()), | 
| Chris@43 | 89 	    this, SLOT(playSelectionModeChanged())); | 
| Chris@43 | 90 | 
| Chris@43 | 91     connect(PlayParameterRepository::getInstance(), | 
| Chris@43 | 92 	    SIGNAL(playParametersChanged(PlayParameters *)), | 
| Chris@43 | 93 	    this, SLOT(playParametersChanged(PlayParameters *))); | 
| Chris@43 | 94 | 
| Chris@43 | 95     connect(Preferences::getInstance(), | 
| Chris@43 | 96             SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | 
| Chris@43 | 97             this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); | 
| Chris@43 | 98 } | 
| Chris@43 | 99 | 
| Chris@43 | 100 AudioCallbackPlaySource::~AudioCallbackPlaySource() | 
| Chris@43 | 101 { | 
| Chris@177 | 102 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@177 | 103     std::cerr << "AudioCallbackPlaySource::~AudioCallbackPlaySource entering" << std::endl; | 
| Chris@177 | 104 #endif | 
| Chris@43 | 105     m_exiting = true; | 
| Chris@43 | 106 | 
| Chris@43 | 107     if (m_fillThread) { | 
| Chris@43 | 108 	m_condition.wakeAll(); | 
| Chris@43 | 109 	m_fillThread->wait(); | 
| Chris@43 | 110 	delete m_fillThread; | 
| Chris@43 | 111     } | 
| Chris@43 | 112 | 
| Chris@43 | 113     clearModels(); | 
| Chris@43 | 114 | 
| Chris@43 | 115     if (m_readBuffers != m_writeBuffers) { | 
| Chris@43 | 116 	delete m_readBuffers; | 
| Chris@43 | 117     } | 
| Chris@43 | 118 | 
| Chris@43 | 119     delete m_writeBuffers; | 
| Chris@43 | 120 | 
| Chris@43 | 121     delete m_audioGenerator; | 
| Chris@43 | 122 | 
| Chris@91 | 123     for (size_t i = 0; i < m_stretcherInputCount; ++i) { | 
| Chris@91 | 124         delete[] m_stretcherInputs[i]; | 
| Chris@91 | 125     } | 
| Chris@91 | 126     delete[] m_stretcherInputSizes; | 
| Chris@91 | 127     delete[] m_stretcherInputs; | 
| Chris@91 | 128 | 
| Chris@130 | 129     delete m_timeStretcher; | 
| Chris@130 | 130     delete m_monoStretcher; | 
| Chris@130 | 131 | 
| Chris@43 | 132     m_bufferScavenger.scavenge(true); | 
| Chris@43 | 133     m_pluginScavenger.scavenge(true); | 
| Chris@177 | 134 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@177 | 135     std::cerr << "AudioCallbackPlaySource::~AudioCallbackPlaySource finishing" << std::endl; | 
| Chris@177 | 136 #endif | 
| Chris@43 | 137 } | 
| Chris@43 | 138 | 
| Chris@43 | 139 void | 
| Chris@43 | 140 AudioCallbackPlaySource::addModel(Model *model) | 
| Chris@43 | 141 { | 
| Chris@43 | 142     if (m_models.find(model) != m_models.end()) return; | 
| Chris@43 | 143 | 
| Chris@43 | 144     bool canPlay = m_audioGenerator->addModel(model); | 
| Chris@43 | 145 | 
| Chris@43 | 146     m_mutex.lock(); | 
| Chris@43 | 147 | 
| Chris@43 | 148     m_models.insert(model); | 
| Chris@43 | 149     if (model->getEndFrame() > m_lastModelEndFrame) { | 
| Chris@43 | 150 	m_lastModelEndFrame = model->getEndFrame(); | 
| Chris@43 | 151     } | 
| Chris@43 | 152 | 
| Chris@43 | 153     bool buffersChanged = false, srChanged = false; | 
| Chris@43 | 154 | 
| Chris@43 | 155     size_t modelChannels = 1; | 
| Chris@43 | 156     DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); | 
| Chris@43 | 157     if (dtvm) modelChannels = dtvm->getChannelCount(); | 
| Chris@43 | 158     if (modelChannels > m_sourceChannelCount) { | 
| Chris@43 | 159 	m_sourceChannelCount = modelChannels; | 
| Chris@43 | 160     } | 
| Chris@43 | 161 | 
| Chris@43 | 162 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@103 | 163     std::cout << "Adding model with " << modelChannels << " channels at rate " << model->getSampleRate() << std::endl; | 
| Chris@43 | 164 #endif | 
| Chris@43 | 165 | 
| Chris@43 | 166     if (m_sourceSampleRate == 0) { | 
| Chris@43 | 167 | 
| Chris@43 | 168 	m_sourceSampleRate = model->getSampleRate(); | 
| Chris@43 | 169 	srChanged = true; | 
| Chris@43 | 170 | 
| Chris@43 | 171     } else if (model->getSampleRate() != m_sourceSampleRate) { | 
| Chris@43 | 172 | 
| Chris@43 | 173         // If this is a dense time-value model and we have no other, we | 
| Chris@43 | 174         // can just switch to this model's sample rate | 
| Chris@43 | 175 | 
| Chris@43 | 176         if (dtvm) { | 
| Chris@43 | 177 | 
| Chris@43 | 178             bool conflicting = false; | 
| Chris@43 | 179 | 
| Chris@43 | 180             for (std::set<Model *>::const_iterator i = m_models.begin(); | 
| Chris@43 | 181                  i != m_models.end(); ++i) { | 
| Chris@43 | 182                 // Only wave file models can be considered conflicting -- | 
| Chris@43 | 183                 // writable wave file models are derived and we shouldn't | 
| Chris@43 | 184                 // take their rates into account.  Also, don't give any | 
| Chris@43 | 185                 // particular weight to a file that's already playing at | 
| Chris@43 | 186                 // the wrong rate anyway | 
| Chris@43 | 187                 WaveFileModel *wfm = dynamic_cast<WaveFileModel *>(*i); | 
| Chris@43 | 188                 if (wfm && wfm != dtvm && | 
| Chris@43 | 189                     wfm->getSampleRate() != model->getSampleRate() && | 
| Chris@43 | 190                     wfm->getSampleRate() == m_sourceSampleRate) { | 
| Chris@43 | 191                     std::cerr << "AudioCallbackPlaySource::addModel: Conflicting wave file model " << *i << " found" << std::endl; | 
| Chris@43 | 192                     conflicting = true; | 
| Chris@43 | 193                     break; | 
| Chris@43 | 194                 } | 
| Chris@43 | 195             } | 
| Chris@43 | 196 | 
| Chris@43 | 197             if (conflicting) { | 
| Chris@43 | 198 | 
| Chris@43 | 199                 std::cerr << "AudioCallbackPlaySource::addModel: ERROR: " | 
| Chris@43 | 200                           << "New model sample rate does not match" << std::endl | 
| Chris@43 | 201                           << "existing model(s) (new " << model->getSampleRate() | 
| Chris@43 | 202                           << " vs " << m_sourceSampleRate | 
| Chris@43 | 203                           << "), playback will be wrong" | 
| Chris@43 | 204                           << std::endl; | 
| Chris@43 | 205 | 
| Chris@43 | 206                 emit sampleRateMismatch(model->getSampleRate(), | 
| Chris@43 | 207                                         m_sourceSampleRate, | 
| Chris@43 | 208                                         false); | 
| Chris@43 | 209             } else { | 
| Chris@43 | 210                 m_sourceSampleRate = model->getSampleRate(); | 
| Chris@43 | 211                 srChanged = true; | 
| Chris@43 | 212             } | 
| Chris@43 | 213         } | 
| Chris@43 | 214     } | 
| Chris@43 | 215 | 
| Chris@43 | 216     if (!m_writeBuffers || (m_writeBuffers->size() < getTargetChannelCount())) { | 
| Chris@43 | 217 	clearRingBuffers(true, getTargetChannelCount()); | 
| Chris@43 | 218 	buffersChanged = true; | 
| Chris@43 | 219     } else { | 
| Chris@43 | 220 	if (canPlay) clearRingBuffers(true); | 
| Chris@43 | 221     } | 
| Chris@43 | 222 | 
| Chris@43 | 223     if (buffersChanged || srChanged) { | 
| Chris@43 | 224 	if (m_converter) { | 
| Chris@43 | 225 	    src_delete(m_converter); | 
| Chris@43 | 226             src_delete(m_crapConverter); | 
| Chris@43 | 227 	    m_converter = 0; | 
| Chris@43 | 228             m_crapConverter = 0; | 
| Chris@43 | 229 	} | 
| Chris@43 | 230     } | 
| Chris@43 | 231 | 
| Chris@164 | 232     rebuildRangeLists(); | 
| Chris@164 | 233 | 
| Chris@43 | 234     m_mutex.unlock(); | 
| Chris@43 | 235 | 
| Chris@43 | 236     m_audioGenerator->setTargetChannelCount(getTargetChannelCount()); | 
| Chris@43 | 237 | 
| Chris@43 | 238     if (!m_fillThread) { | 
| Chris@43 | 239 	m_fillThread = new FillThread(*this); | 
| Chris@43 | 240 	m_fillThread->start(); | 
| Chris@43 | 241     } | 
| Chris@43 | 242 | 
| Chris@43 | 243 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 244     std::cout << "AudioCallbackPlaySource::addModel: now have " << m_models.size() << " model(s) -- emitting modelReplaced" << std::endl; | 
| Chris@43 | 245 #endif | 
| Chris@43 | 246 | 
| Chris@43 | 247     if (buffersChanged || srChanged) { | 
| Chris@43 | 248 	emit modelReplaced(); | 
| Chris@43 | 249     } | 
| Chris@43 | 250 | 
| Chris@43 | 251     connect(model, SIGNAL(modelChanged(size_t, size_t)), | 
| Chris@43 | 252             this, SLOT(modelChanged(size_t, size_t))); | 
| Chris@43 | 253 | 
| Chris@43 | 254     m_condition.wakeAll(); | 
| Chris@43 | 255 } | 
| Chris@43 | 256 | 
| Chris@43 | 257 void | 
| Chris@43 | 258 AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame) | 
| Chris@43 | 259 { | 
| Chris@43 | 260 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 261     std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl; | 
| Chris@43 | 262 #endif | 
| Chris@93 | 263     if (endFrame > m_lastModelEndFrame) { | 
| Chris@93 | 264         m_lastModelEndFrame = endFrame; | 
| Chris@99 | 265         rebuildRangeLists(); | 
| Chris@93 | 266     } | 
| Chris@43 | 267 } | 
| Chris@43 | 268 | 
| Chris@43 | 269 void | 
| Chris@43 | 270 AudioCallbackPlaySource::removeModel(Model *model) | 
| Chris@43 | 271 { | 
| Chris@43 | 272     m_mutex.lock(); | 
| Chris@43 | 273 | 
| Chris@43 | 274 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 275     std::cout << "AudioCallbackPlaySource::removeModel(" << model << ")" << std::endl; | 
| Chris@43 | 276 #endif | 
| Chris@43 | 277 | 
| Chris@43 | 278     disconnect(model, SIGNAL(modelChanged(size_t, size_t)), | 
| Chris@43 | 279                this, SLOT(modelChanged(size_t, size_t))); | 
| Chris@43 | 280 | 
| Chris@43 | 281     m_models.erase(model); | 
| Chris@43 | 282 | 
| Chris@43 | 283     if (m_models.empty()) { | 
| Chris@43 | 284 	if (m_converter) { | 
| Chris@43 | 285 	    src_delete(m_converter); | 
| Chris@43 | 286             src_delete(m_crapConverter); | 
| Chris@43 | 287 	    m_converter = 0; | 
| Chris@43 | 288             m_crapConverter = 0; | 
| Chris@43 | 289 	} | 
| Chris@43 | 290 	m_sourceSampleRate = 0; | 
| Chris@43 | 291     } | 
| Chris@43 | 292 | 
| Chris@43 | 293     size_t lastEnd = 0; | 
| Chris@43 | 294     for (std::set<Model *>::const_iterator i = m_models.begin(); | 
| Chris@43 | 295 	 i != m_models.end(); ++i) { | 
| Chris@164 | 296 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@164 | 297 	std::cout << "AudioCallbackPlaySource::removeModel(" << model << "): checking end frame on model " << *i << std::endl; | 
| Chris@164 | 298 #endif | 
| Chris@43 | 299 	if ((*i)->getEndFrame() > lastEnd) lastEnd = (*i)->getEndFrame(); | 
| Chris@164 | 300 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@164 | 301 	std::cout << "(done, lastEnd now " << lastEnd << ")" << std::endl; | 
| Chris@164 | 302 #endif | 
| Chris@43 | 303     } | 
| Chris@43 | 304     m_lastModelEndFrame = lastEnd; | 
| Chris@43 | 305 | 
| Chris@43 | 306     m_mutex.unlock(); | 
| Chris@43 | 307 | 
| Chris@43 | 308     m_audioGenerator->removeModel(model); | 
| Chris@43 | 309 | 
| Chris@43 | 310     clearRingBuffers(); | 
| Chris@43 | 311 } | 
| Chris@43 | 312 | 
| Chris@43 | 313 void | 
| Chris@43 | 314 AudioCallbackPlaySource::clearModels() | 
| Chris@43 | 315 { | 
| Chris@43 | 316     m_mutex.lock(); | 
| Chris@43 | 317 | 
| Chris@43 | 318 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 319     std::cout << "AudioCallbackPlaySource::clearModels()" << std::endl; | 
| Chris@43 | 320 #endif | 
| Chris@43 | 321 | 
| Chris@43 | 322     m_models.clear(); | 
| Chris@43 | 323 | 
| Chris@43 | 324     if (m_converter) { | 
| Chris@43 | 325 	src_delete(m_converter); | 
| Chris@43 | 326         src_delete(m_crapConverter); | 
| Chris@43 | 327 	m_converter = 0; | 
| Chris@43 | 328         m_crapConverter = 0; | 
| Chris@43 | 329     } | 
| Chris@43 | 330 | 
| Chris@43 | 331     m_lastModelEndFrame = 0; | 
| Chris@43 | 332 | 
| Chris@43 | 333     m_sourceSampleRate = 0; | 
| Chris@43 | 334 | 
| Chris@43 | 335     m_mutex.unlock(); | 
| Chris@43 | 336 | 
| Chris@43 | 337     m_audioGenerator->clearModels(); | 
| Chris@93 | 338 | 
| Chris@93 | 339     clearRingBuffers(); | 
| Chris@43 | 340 } | 
| Chris@43 | 341 | 
| Chris@43 | 342 void | 
| Chris@43 | 343 AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count) | 
| Chris@43 | 344 { | 
| Chris@43 | 345     if (!haveLock) m_mutex.lock(); | 
| Chris@43 | 346 | 
| Chris@93 | 347     rebuildRangeLists(); | 
| Chris@93 | 348 | 
| Chris@43 | 349     if (count == 0) { | 
| Chris@43 | 350 	if (m_writeBuffers) count = m_writeBuffers->size(); | 
| Chris@43 | 351     } | 
| Chris@43 | 352 | 
| Chris@93 | 353     m_writeBufferFill = getCurrentBufferedFrame(); | 
| Chris@43 | 354 | 
| Chris@43 | 355     if (m_readBuffers != m_writeBuffers) { | 
| Chris@43 | 356 	delete m_writeBuffers; | 
| Chris@43 | 357     } | 
| Chris@43 | 358 | 
| Chris@43 | 359     m_writeBuffers = new RingBufferVector; | 
| Chris@43 | 360 | 
| Chris@43 | 361     for (size_t i = 0; i < count; ++i) { | 
| Chris@43 | 362 	m_writeBuffers->push_back(new RingBuffer<float>(m_ringBufferSize)); | 
| Chris@43 | 363     } | 
| Chris@43 | 364 | 
| Chris@43 | 365 //    std::cout << "AudioCallbackPlaySource::clearRingBuffers: Created " | 
| Chris@43 | 366 //	      << count << " write buffers" << std::endl; | 
| Chris@43 | 367 | 
| Chris@43 | 368     if (!haveLock) { | 
| Chris@43 | 369 	m_mutex.unlock(); | 
| Chris@43 | 370     } | 
| Chris@43 | 371 } | 
| Chris@43 | 372 | 
| Chris@43 | 373 void | 
| Chris@43 | 374 AudioCallbackPlaySource::play(size_t startFrame) | 
| Chris@43 | 375 { | 
| Chris@43 | 376     if (m_viewManager->getPlaySelectionMode() && | 
| Chris@43 | 377 	!m_viewManager->getSelections().empty()) { | 
| Chris@60 | 378 | 
| Chris@94 | 379         std::cerr << "AudioCallbackPlaySource::play: constraining frame " << startFrame << " to selection = "; | 
| Chris@94 | 380 | 
| Chris@60 | 381         startFrame = m_viewManager->constrainFrameToSelection(startFrame); | 
| Chris@60 | 382 | 
| Chris@94 | 383         std::cerr << startFrame << std::endl; | 
| Chris@94 | 384 | 
| Chris@43 | 385     } else { | 
| Chris@43 | 386 	if (startFrame >= m_lastModelEndFrame) { | 
| Chris@43 | 387 	    startFrame = 0; | 
| Chris@43 | 388 	} | 
| Chris@43 | 389     } | 
| Chris@43 | 390 | 
| Chris@132 | 391 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@60 | 392     std::cerr << "play(" << startFrame << ") -> playback model "; | 
| Chris@132 | 393 #endif | 
| Chris@60 | 394 | 
| Chris@60 | 395     startFrame = m_viewManager->alignReferenceToPlaybackFrame(startFrame); | 
| Chris@60 | 396 | 
| Chris@60 | 397     std::cerr << startFrame << std::endl; | 
| Chris@60 | 398 | 
| Chris@43 | 399     // The fill thread will automatically empty its buffers before | 
| Chris@43 | 400     // starting again if we have not so far been playing, but not if | 
| Chris@43 | 401     // we're just re-seeking. | 
| Chris@102 | 402     // NO -- we can end up playing some first -- always reset here | 
| Chris@43 | 403 | 
| Chris@43 | 404     m_mutex.lock(); | 
| Chris@102 | 405 | 
| Chris@91 | 406     if (m_timeStretcher) { | 
| Chris@91 | 407         m_timeStretcher->reset(); | 
| Chris@91 | 408     } | 
| Chris@130 | 409     if (m_monoStretcher) { | 
| Chris@130 | 410         m_monoStretcher->reset(); | 
| Chris@130 | 411     } | 
| Chris@102 | 412 | 
| Chris@102 | 413     m_readBufferFill = m_writeBufferFill = startFrame; | 
| Chris@102 | 414     if (m_readBuffers) { | 
| Chris@102 | 415         for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@102 | 416             RingBuffer<float> *rb = getReadRingBuffer(c); | 
| Chris@132 | 417 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@102 | 418             std::cerr << "reset ring buffer for channel " << c << std::endl; | 
| Chris@132 | 419 #endif | 
| Chris@102 | 420             if (rb) rb->reset(); | 
| Chris@102 | 421         } | 
| Chris@43 | 422     } | 
| Chris@102 | 423     if (m_converter) src_reset(m_converter); | 
| Chris@102 | 424     if (m_crapConverter) src_reset(m_crapConverter); | 
| Chris@102 | 425 | 
| Chris@43 | 426     m_mutex.unlock(); | 
| Chris@43 | 427 | 
| Chris@43 | 428     m_audioGenerator->reset(); | 
| Chris@43 | 429 | 
| Chris@94 | 430     m_playStartFrame = startFrame; | 
| Chris@94 | 431     m_playStartFramePassed = false; | 
| Chris@94 | 432     m_playStartedAt = RealTime::zeroTime; | 
| Chris@94 | 433     if (m_target) { | 
| Chris@94 | 434         m_playStartedAt = RealTime::fromSeconds(m_target->getCurrentTime()); | 
| Chris@94 | 435     } | 
| Chris@94 | 436 | 
| Chris@43 | 437     bool changed = !m_playing; | 
| Chris@91 | 438     m_lastRetrievalTimestamp = 0; | 
| Chris@102 | 439     m_lastCurrentFrame = 0; | 
| Chris@43 | 440     m_playing = true; | 
| Chris@43 | 441     m_condition.wakeAll(); | 
| Chris@158 | 442     if (changed) { | 
| Chris@158 | 443         emit playStatusChanged(m_playing); | 
| Chris@158 | 444         emit activity(tr("Play from %1").arg | 
| Chris@158 | 445                       (RealTime::frame2RealTime | 
| Chris@158 | 446                        (m_playStartFrame, m_sourceSampleRate).toText().c_str())); | 
| Chris@158 | 447     } | 
| Chris@43 | 448 } | 
| Chris@43 | 449 | 
| Chris@43 | 450 void | 
| Chris@43 | 451 AudioCallbackPlaySource::stop() | 
| Chris@43 | 452 { | 
| Chris@43 | 453     bool changed = m_playing; | 
| Chris@43 | 454     m_playing = false; | 
| Chris@43 | 455     m_condition.wakeAll(); | 
| Chris@91 | 456     m_lastRetrievalTimestamp = 0; | 
| Chris@158 | 457     if (changed) { | 
| Chris@158 | 458         emit playStatusChanged(m_playing); | 
| Chris@158 | 459         emit activity(tr("Stop at %1").arg | 
| Chris@158 | 460                       (RealTime::frame2RealTime | 
| Chris@158 | 461                        (m_lastCurrentFrame, m_sourceSampleRate).toText().c_str())); | 
| Chris@158 | 462     } | 
| Chris@102 | 463     m_lastCurrentFrame = 0; | 
| Chris@43 | 464 } | 
| Chris@43 | 465 | 
| Chris@43 | 466 void | 
| Chris@43 | 467 AudioCallbackPlaySource::selectionChanged() | 
| Chris@43 | 468 { | 
| Chris@43 | 469     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@43 | 470 	clearRingBuffers(); | 
| Chris@43 | 471     } | 
| Chris@43 | 472 } | 
| Chris@43 | 473 | 
| Chris@43 | 474 void | 
| Chris@43 | 475 AudioCallbackPlaySource::playLoopModeChanged() | 
| Chris@43 | 476 { | 
| Chris@43 | 477     clearRingBuffers(); | 
| Chris@43 | 478 } | 
| Chris@43 | 479 | 
| Chris@43 | 480 void | 
| Chris@43 | 481 AudioCallbackPlaySource::playSelectionModeChanged() | 
| Chris@43 | 482 { | 
| Chris@43 | 483     if (!m_viewManager->getSelections().empty()) { | 
| Chris@43 | 484 	clearRingBuffers(); | 
| Chris@43 | 485     } | 
| Chris@43 | 486 } | 
| Chris@43 | 487 | 
| Chris@43 | 488 void | 
| Chris@43 | 489 AudioCallbackPlaySource::playParametersChanged(PlayParameters *) | 
| Chris@43 | 490 { | 
| Chris@43 | 491     clearRingBuffers(); | 
| Chris@43 | 492 } | 
| Chris@43 | 493 | 
| Chris@43 | 494 void | 
| Chris@43 | 495 AudioCallbackPlaySource::preferenceChanged(PropertyContainer::PropertyName n) | 
| Chris@43 | 496 { | 
| Chris@43 | 497     if (n == "Resample Quality") { | 
| Chris@43 | 498         setResampleQuality(Preferences::getInstance()->getResampleQuality()); | 
| Chris@43 | 499     } | 
| Chris@43 | 500 } | 
| Chris@43 | 501 | 
| Chris@43 | 502 void | 
| Chris@43 | 503 AudioCallbackPlaySource::audioProcessingOverload() | 
| Chris@43 | 504 { | 
| Chris@130 | 505     std::cerr << "Audio processing overload!" << std::endl; | 
| Chris@130 | 506 | 
| Chris@130 | 507     if (!m_playing) return; | 
| Chris@130 | 508 | 
| Chris@43 | 509     RealTimePluginInstance *ap = m_auditioningPlugin; | 
| Chris@130 | 510     if (ap && !m_auditioningPluginBypassed) { | 
| Chris@43 | 511         m_auditioningPluginBypassed = true; | 
| Chris@43 | 512         emit audioOverloadPluginDisabled(); | 
| Chris@130 | 513         return; | 
| Chris@130 | 514     } | 
| Chris@130 | 515 | 
| Chris@130 | 516     if (m_timeStretcher && | 
| Chris@130 | 517         m_timeStretcher->getTimeRatio() < 1.0 && | 
| Chris@130 | 518         m_stretcherInputCount > 1 && | 
| Chris@130 | 519         m_monoStretcher && !m_stretchMono) { | 
| Chris@130 | 520         m_stretchMono = true; | 
| Chris@130 | 521         emit audioTimeStretchMultiChannelDisabled(); | 
| Chris@130 | 522         return; | 
| Chris@43 | 523     } | 
| Chris@43 | 524 } | 
| Chris@43 | 525 | 
| Chris@43 | 526 void | 
| Chris@91 | 527 AudioCallbackPlaySource::setTarget(AudioCallbackPlayTarget *target, size_t size) | 
| Chris@43 | 528 { | 
| Chris@91 | 529     m_target = target; | 
| Chris@43 | 530 //    std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; | 
| Chris@43 | 531     assert(size < m_ringBufferSize); | 
| Chris@43 | 532     m_blockSize = size; | 
| Chris@43 | 533 } | 
| Chris@43 | 534 | 
| Chris@43 | 535 size_t | 
| Chris@43 | 536 AudioCallbackPlaySource::getTargetBlockSize() const | 
| Chris@43 | 537 { | 
| Chris@43 | 538 //    std::cout << "AudioCallbackPlaySource::getTargetBlockSize() -> " << m_blockSize << std::endl; | 
| Chris@43 | 539     return m_blockSize; | 
| Chris@43 | 540 } | 
| Chris@43 | 541 | 
| Chris@43 | 542 void | 
| Chris@43 | 543 AudioCallbackPlaySource::setTargetPlayLatency(size_t latency) | 
| Chris@43 | 544 { | 
| Chris@43 | 545     m_playLatency = latency; | 
| Chris@43 | 546 } | 
| Chris@43 | 547 | 
| Chris@43 | 548 size_t | 
| Chris@43 | 549 AudioCallbackPlaySource::getTargetPlayLatency() const | 
| Chris@43 | 550 { | 
| Chris@43 | 551     return m_playLatency; | 
| Chris@43 | 552 } | 
| Chris@43 | 553 | 
| Chris@43 | 554 size_t | 
| Chris@43 | 555 AudioCallbackPlaySource::getCurrentPlayingFrame() | 
| Chris@43 | 556 { | 
| Chris@91 | 557     // This method attempts to estimate which audio sample frame is | 
| Chris@91 | 558     // "currently coming through the speakers". | 
| Chris@91 | 559 | 
| Chris@93 | 560     size_t targetRate = getTargetSampleRate(); | 
| Chris@93 | 561     size_t latency = m_playLatency; // at target rate | 
| Chris@93 | 562     RealTime latency_t = RealTime::frame2RealTime(latency, targetRate); | 
| Chris@93 | 563 | 
| Chris@93 | 564     return getCurrentFrame(latency_t); | 
| Chris@93 | 565 } | 
| Chris@93 | 566 | 
| Chris@93 | 567 size_t | 
| Chris@93 | 568 AudioCallbackPlaySource::getCurrentBufferedFrame() | 
| Chris@93 | 569 { | 
| Chris@93 | 570     return getCurrentFrame(RealTime::zeroTime); | 
| Chris@93 | 571 } | 
| Chris@93 | 572 | 
| Chris@93 | 573 size_t | 
| Chris@93 | 574 AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t) | 
| Chris@93 | 575 { | 
| Chris@43 | 576     bool resample = false; | 
| Chris@91 | 577     double resampleRatio = 1.0; | 
| Chris@43 | 578 | 
| Chris@91 | 579     // We resample when filling the ring buffer, and time-stretch when | 
| Chris@91 | 580     // draining it.  The buffer contains data at the "target rate" and | 
| Chris@91 | 581     // the latency provided by the target is also at the target rate. | 
| Chris@91 | 582     // Because of the multiple rates involved, we do the actual | 
| Chris@91 | 583     // calculation using RealTime instead. | 
| Chris@43 | 584 | 
| Chris@91 | 585     size_t sourceRate = getSourceSampleRate(); | 
| Chris@91 | 586     size_t targetRate = getTargetSampleRate(); | 
| Chris@91 | 587 | 
| Chris@91 | 588     if (sourceRate == 0 || targetRate == 0) return 0; | 
| Chris@91 | 589 | 
| Chris@91 | 590     size_t inbuffer = 0; // at target rate | 
| Chris@91 | 591 | 
| Chris@43 | 592     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 593 	RingBuffer<float> *rb = getReadRingBuffer(c); | 
| Chris@43 | 594 	if (rb) { | 
| Chris@91 | 595 	    size_t here = rb->getReadSpace(); | 
| Chris@91 | 596 	    if (c == 0 || here < inbuffer) inbuffer = here; | 
| Chris@43 | 597 	} | 
| Chris@43 | 598     } | 
| Chris@43 | 599 | 
| Chris@91 | 600     size_t readBufferFill = m_readBufferFill; | 
| Chris@91 | 601     size_t lastRetrievedBlockSize = m_lastRetrievedBlockSize; | 
| Chris@91 | 602     double lastRetrievalTimestamp = m_lastRetrievalTimestamp; | 
| Chris@91 | 603     double currentTime = 0.0; | 
| Chris@91 | 604     if (m_target) currentTime = m_target->getCurrentTime(); | 
| Chris@91 | 605 | 
| Chris@102 | 606     bool looping = m_viewManager->getPlayLoopMode(); | 
| Chris@102 | 607 | 
| Chris@91 | 608     RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate); | 
| Chris@91 | 609 | 
| Chris@91 | 610     size_t stretchlat = 0; | 
| Chris@91 | 611     double timeRatio = 1.0; | 
| Chris@91 | 612 | 
| Chris@91 | 613     if (m_timeStretcher) { | 
| Chris@91 | 614         stretchlat = m_timeStretcher->getLatency(); | 
| Chris@91 | 615         timeRatio = m_timeStretcher->getTimeRatio(); | 
| Chris@43 | 616     } | 
| Chris@43 | 617 | 
| Chris@91 | 618     RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, targetRate); | 
| Chris@43 | 619 | 
| Chris@91 | 620     // When the target has just requested a block from us, the last | 
| Chris@91 | 621     // sample it obtained was our buffer fill frame count minus the | 
| Chris@91 | 622     // amount of read space (converted back to source sample rate) | 
| Chris@91 | 623     // remaining now.  That sample is not expected to be played until | 
| Chris@91 | 624     // the target's play latency has elapsed.  By the time the | 
| Chris@91 | 625     // following block is requested, that sample will be at the | 
| Chris@91 | 626     // target's play latency minus the last requested block size away | 
| Chris@91 | 627     // from being played. | 
| Chris@91 | 628 | 
| Chris@91 | 629     RealTime sincerequest_t = RealTime::zeroTime; | 
| Chris@91 | 630     RealTime lastretrieved_t = RealTime::zeroTime; | 
| Chris@91 | 631 | 
| Chris@102 | 632     if (m_target && | 
| Chris@102 | 633         m_trustworthyTimestamps && | 
| Chris@102 | 634         lastRetrievalTimestamp != 0.0) { | 
| Chris@91 | 635 | 
| Chris@91 | 636         lastretrieved_t = RealTime::frame2RealTime | 
| Chris@91 | 637             (lastRetrievedBlockSize, targetRate); | 
| Chris@91 | 638 | 
| Chris@91 | 639         // calculate number of frames at target rate that have elapsed | 
| Chris@91 | 640         // since the end of the last call to getSourceSamples | 
| Chris@91 | 641 | 
| Chris@102 | 642         if (m_trustworthyTimestamps && !looping) { | 
| Chris@91 | 643 | 
| Chris@102 | 644             // this adjustment seems to cause more problems when looping | 
| Chris@102 | 645             double elapsed = currentTime - lastRetrievalTimestamp; | 
| Chris@102 | 646 | 
| Chris@102 | 647             if (elapsed > 0.0) { | 
| Chris@102 | 648                 sincerequest_t = RealTime::fromSeconds(elapsed); | 
| Chris@102 | 649             } | 
| Chris@91 | 650         } | 
| Chris@91 | 651 | 
| Chris@91 | 652     } else { | 
| Chris@91 | 653 | 
| Chris@91 | 654         lastretrieved_t = RealTime::frame2RealTime | 
| Chris@91 | 655             (getTargetBlockSize(), targetRate); | 
| Chris@62 | 656     } | 
| Chris@91 | 657 | 
| Chris@91 | 658     RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, sourceRate); | 
| Chris@91 | 659 | 
| Chris@91 | 660     if (timeRatio != 1.0) { | 
| Chris@91 | 661         lastretrieved_t = lastretrieved_t / timeRatio; | 
| Chris@91 | 662         sincerequest_t = sincerequest_t / timeRatio; | 
| Chris@163 | 663         latency_t = latency_t / timeRatio; | 
| Chris@43 | 664     } | 
| Chris@43 | 665 | 
| Chris@91 | 666 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 
| Chris@163 | 667     std::cerr << "\nbuffered to: " << bufferedto_t << ", in buffer: " << inbuffer_t << ", time ratio " << timeRatio << "\n  stretcher latency: " << stretchlat_t << ", device latency: " << latency_t << "\n  since request: " << sincerequest_t << ", last retrieved quantity: " << lastretrieved_t << std::endl; | 
| Chris@91 | 668 #endif | 
| Chris@43 | 669 | 
| Chris@91 | 670     RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); | 
| Chris@60 | 671 | 
| Chris@93 | 672     // Normally the range lists should contain at least one item each | 
| Chris@93 | 673     // -- if playback is unconstrained, that item should report the | 
| Chris@93 | 674     // entire source audio duration. | 
| Chris@43 | 675 | 
| Chris@93 | 676     if (m_rangeStarts.empty()) { | 
| Chris@93 | 677         rebuildRangeLists(); | 
| Chris@93 | 678     } | 
| Chris@92 | 679 | 
| Chris@93 | 680     if (m_rangeStarts.empty()) { | 
| Chris@93 | 681         // this code is only used in case of error in rebuildRangeLists | 
| Chris@93 | 682         RealTime playing_t = bufferedto_t | 
| Chris@93 | 683             - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | 
| Chris@93 | 684             + sincerequest_t; | 
| Chris@93 | 685         size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); | 
| Chris@93 | 686         return m_viewManager->alignPlaybackFrameToReference(frame); | 
| Chris@93 | 687     } | 
| Chris@43 | 688 | 
| Chris@91 | 689     int inRange = 0; | 
| Chris@91 | 690     int index = 0; | 
| Chris@91 | 691 | 
| Chris@93 | 692     for (size_t i = 0; i < m_rangeStarts.size(); ++i) { | 
| Chris@93 | 693         if (bufferedto_t >= m_rangeStarts[i]) { | 
| Chris@93 | 694             inRange = index; | 
| Chris@93 | 695         } else { | 
| Chris@93 | 696             break; | 
| Chris@93 | 697         } | 
| Chris@93 | 698         ++index; | 
| Chris@93 | 699     } | 
| Chris@93 | 700 | 
| Chris@93 | 701     if (inRange >= m_rangeStarts.size()) inRange = m_rangeStarts.size()-1; | 
| Chris@93 | 702 | 
| Chris@94 | 703     RealTime playing_t = bufferedto_t; | 
| Chris@93 | 704 | 
| Chris@93 | 705     playing_t = playing_t | 
| Chris@93 | 706         - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t | 
| Chris@93 | 707         + sincerequest_t; | 
| Chris@94 | 708 | 
| Chris@94 | 709     // This rather gross little hack is used to ensure that latency | 
| Chris@94 | 710     // compensation doesn't result in the playback pointer appearing | 
| Chris@94 | 711     // to start earlier than the actual playback does.  It doesn't | 
| Chris@94 | 712     // work properly (hence the bail-out in the middle) because if we | 
| Chris@94 | 713     // are playing a relatively short looped region, the playing time | 
| Chris@94 | 714     // estimated from the buffer fill frame may have wrapped around | 
| Chris@94 | 715     // the region boundary and end up being much smaller than the | 
| Chris@94 | 716     // theoretical play start frame, perhaps even for the entire | 
| Chris@94 | 717     // duration of playback! | 
| Chris@94 | 718 | 
| Chris@94 | 719     if (!m_playStartFramePassed) { | 
| Chris@94 | 720         RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, | 
| Chris@94 | 721                                                         sourceRate); | 
| Chris@94 | 722         if (playing_t < playstart_t) { | 
| Chris@132 | 723 //            std::cerr << "playing_t " << playing_t << " < playstart_t " | 
| Chris@132 | 724 //                      << playstart_t << std::endl; | 
| Chris@122 | 725             if (/*!!! sincerequest_t > RealTime::zeroTime && */ | 
| Chris@94 | 726                 m_playStartedAt + latency_t + stretchlat_t < | 
| Chris@94 | 727                 RealTime::fromSeconds(currentTime)) { | 
| Chris@176 | 728 //                std::cerr << "but we've been playing for long enough that I think we should disregard it (it probably results from loop wrapping)" << std::endl; | 
| Chris@94 | 729                 m_playStartFramePassed = true; | 
| Chris@94 | 730             } else { | 
| Chris@94 | 731                 playing_t = playstart_t; | 
| Chris@94 | 732             } | 
| Chris@94 | 733         } else { | 
| Chris@94 | 734             m_playStartFramePassed = true; | 
| Chris@94 | 735         } | 
| Chris@94 | 736     } | 
| Chris@163 | 737 | 
| Chris@163 | 738 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 
| Chris@163 | 739     std::cerr << "playing_t " << playing_t; | 
| Chris@163 | 740 #endif | 
| Chris@94 | 741 | 
| Chris@94 | 742     playing_t = playing_t - m_rangeStarts[inRange]; | 
| Chris@93 | 743 | 
| Chris@93 | 744 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 
| Chris@163 | 745     std::cerr << " as offset into range " << inRange << " (start =" << m_rangeStarts[inRange] << " duration =" << m_rangeDurations[inRange] << ") = " << playing_t << std::endl; | 
| Chris@93 | 746 #endif | 
| Chris@93 | 747 | 
| Chris@93 | 748     while (playing_t < RealTime::zeroTime) { | 
| Chris@93 | 749 | 
| Chris@93 | 750         if (inRange == 0) { | 
| Chris@93 | 751             if (looping) { | 
| Chris@93 | 752                 inRange = m_rangeStarts.size() - 1; | 
| Chris@93 | 753             } else { | 
| Chris@93 | 754                 break; | 
| Chris@93 | 755             } | 
| Chris@93 | 756         } else { | 
| Chris@93 | 757             --inRange; | 
| Chris@93 | 758         } | 
| Chris@93 | 759 | 
| Chris@93 | 760         playing_t = playing_t + m_rangeDurations[inRange]; | 
| Chris@93 | 761     } | 
| Chris@93 | 762 | 
| Chris@93 | 763     playing_t = playing_t + m_rangeStarts[inRange]; | 
| Chris@93 | 764 | 
| Chris@93 | 765 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 
| Chris@93 | 766     std::cerr << "  playing time: " << playing_t << std::endl; | 
| Chris@93 | 767 #endif | 
| Chris@93 | 768 | 
| Chris@93 | 769     if (!looping) { | 
| Chris@93 | 770         if (inRange == m_rangeStarts.size()-1 && | 
| Chris@93 | 771             playing_t >= m_rangeStarts[inRange] + m_rangeDurations[inRange]) { | 
| Chris@96 | 772 std::cerr << "Not looping, inRange " << inRange << " == rangeStarts.size()-1, playing_t " << playing_t << " >= m_rangeStarts[inRange] " << m_rangeStarts[inRange] << " + m_rangeDurations[inRange] " << m_rangeDurations[inRange] << " -- stopping" << std::endl; | 
| Chris@93 | 773             stop(); | 
| Chris@93 | 774         } | 
| Chris@93 | 775     } | 
| Chris@93 | 776 | 
| Chris@93 | 777     if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; | 
| Chris@93 | 778 | 
| Chris@93 | 779     size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); | 
| Chris@102 | 780 | 
| Chris@102 | 781     if (m_lastCurrentFrame > 0 && !looping) { | 
| Chris@102 | 782         if (frame < m_lastCurrentFrame) { | 
| Chris@102 | 783             frame = m_lastCurrentFrame; | 
| Chris@102 | 784         } | 
| Chris@102 | 785     } | 
| Chris@102 | 786 | 
| Chris@102 | 787     m_lastCurrentFrame = frame; | 
| Chris@102 | 788 | 
| Chris@93 | 789     return m_viewManager->alignPlaybackFrameToReference(frame); | 
| Chris@93 | 790 } | 
| Chris@93 | 791 | 
| Chris@93 | 792 void | 
| Chris@93 | 793 AudioCallbackPlaySource::rebuildRangeLists() | 
| Chris@93 | 794 { | 
| Chris@93 | 795     bool constrained = (m_viewManager->getPlaySelectionMode()); | 
| Chris@93 | 796 | 
| Chris@93 | 797     m_rangeStarts.clear(); | 
| Chris@93 | 798     m_rangeDurations.clear(); | 
| Chris@93 | 799 | 
| Chris@93 | 800     size_t sourceRate = getSourceSampleRate(); | 
| Chris@93 | 801     if (sourceRate == 0) return; | 
| Chris@93 | 802 | 
| Chris@93 | 803     RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); | 
| Chris@93 | 804     if (end == RealTime::zeroTime) return; | 
| Chris@93 | 805 | 
| Chris@93 | 806     if (!constrained) { | 
| Chris@93 | 807         m_rangeStarts.push_back(RealTime::zeroTime); | 
| Chris@93 | 808         m_rangeDurations.push_back(end); | 
| Chris@93 | 809         return; | 
| Chris@93 | 810     } | 
| Chris@93 | 811 | 
| Chris@93 | 812     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@93 | 813     MultiSelection::SelectionList::const_iterator i; | 
| Chris@93 | 814 | 
| Chris@93 | 815 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@93 | 816     std::cerr << "AudioCallbackPlaySource::rebuildRangeLists" << std::endl; | 
| Chris@93 | 817 #endif | 
| Chris@93 | 818 | 
| Chris@93 | 819     if (!selections.empty()) { | 
| Chris@91 | 820 | 
| Chris@91 | 821         for (i = selections.begin(); i != selections.end(); ++i) { | 
| Chris@91 | 822 | 
| Chris@91 | 823             RealTime start = | 
| Chris@91 | 824                 (RealTime::frame2RealTime | 
| Chris@91 | 825                  (m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), | 
| Chris@91 | 826                   sourceRate)); | 
| Chris@91 | 827             RealTime duration = | 
| Chris@91 | 828                 (RealTime::frame2RealTime | 
| Chris@91 | 829                  (m_viewManager->alignReferenceToPlaybackFrame(i->getEndFrame()) - | 
| Chris@91 | 830                   m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), | 
| Chris@91 | 831                   sourceRate)); | 
| Chris@91 | 832 | 
| Chris@93 | 833             m_rangeStarts.push_back(start); | 
| Chris@93 | 834             m_rangeDurations.push_back(duration); | 
| Chris@91 | 835         } | 
| Chris@93 | 836     } else { | 
| Chris@93 | 837         m_rangeStarts.push_back(RealTime::zeroTime); | 
| Chris@93 | 838         m_rangeDurations.push_back(end); | 
| Chris@43 | 839     } | 
| Chris@43 | 840 | 
| Chris@93 | 841 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@93 | 842     std::cerr << "Now have " << m_rangeStarts.size() << " play ranges" << std::endl; | 
| Chris@91 | 843 #endif | 
| Chris@43 | 844 } | 
| Chris@43 | 845 | 
| Chris@43 | 846 void | 
| Chris@43 | 847 AudioCallbackPlaySource::setOutputLevels(float left, float right) | 
| Chris@43 | 848 { | 
| Chris@43 | 849     m_outputLeft = left; | 
| Chris@43 | 850     m_outputRight = right; | 
| Chris@43 | 851 } | 
| Chris@43 | 852 | 
| Chris@43 | 853 bool | 
| Chris@43 | 854 AudioCallbackPlaySource::getOutputLevels(float &left, float &right) | 
| Chris@43 | 855 { | 
| Chris@43 | 856     left = m_outputLeft; | 
| Chris@43 | 857     right = m_outputRight; | 
| Chris@43 | 858     return true; | 
| Chris@43 | 859 } | 
| Chris@43 | 860 | 
| Chris@43 | 861 void | 
| Chris@43 | 862 AudioCallbackPlaySource::setTargetSampleRate(size_t sr) | 
| Chris@43 | 863 { | 
| Chris@43 | 864     m_targetSampleRate = sr; | 
| Chris@43 | 865     initialiseConverter(); | 
| Chris@43 | 866 } | 
| Chris@43 | 867 | 
| Chris@43 | 868 void | 
| Chris@43 | 869 AudioCallbackPlaySource::initialiseConverter() | 
| Chris@43 | 870 { | 
| Chris@43 | 871     m_mutex.lock(); | 
| Chris@43 | 872 | 
| Chris@43 | 873     if (m_converter) { | 
| Chris@43 | 874         src_delete(m_converter); | 
| Chris@43 | 875         src_delete(m_crapConverter); | 
| Chris@43 | 876         m_converter = 0; | 
| Chris@43 | 877         m_crapConverter = 0; | 
| Chris@43 | 878     } | 
| Chris@43 | 879 | 
| Chris@43 | 880     if (getSourceSampleRate() != getTargetSampleRate()) { | 
| Chris@43 | 881 | 
| Chris@43 | 882 	int err = 0; | 
| Chris@43 | 883 | 
| Chris@43 | 884 	m_converter = src_new(m_resampleQuality == 2 ? SRC_SINC_BEST_QUALITY : | 
| Chris@43 | 885                               m_resampleQuality == 1 ? SRC_SINC_MEDIUM_QUALITY : | 
| Chris@43 | 886                               m_resampleQuality == 0 ? SRC_SINC_FASTEST : | 
| Chris@43 | 887                                                        SRC_SINC_MEDIUM_QUALITY, | 
| Chris@43 | 888 			      getTargetChannelCount(), &err); | 
| Chris@43 | 889 | 
| Chris@43 | 890         if (m_converter) { | 
| Chris@43 | 891             m_crapConverter = src_new(SRC_LINEAR, | 
| Chris@43 | 892                                       getTargetChannelCount(), | 
| Chris@43 | 893                                       &err); | 
| Chris@43 | 894         } | 
| Chris@43 | 895 | 
| Chris@43 | 896 	if (!m_converter || !m_crapConverter) { | 
| Chris@43 | 897 	    std::cerr | 
| Chris@43 | 898 		<< "AudioCallbackPlaySource::setModel: ERROR in creating samplerate converter: " | 
| Chris@43 | 899 		<< src_strerror(err) << std::endl; | 
| Chris@43 | 900 | 
| Chris@43 | 901             if (m_converter) { | 
| Chris@43 | 902                 src_delete(m_converter); | 
| Chris@43 | 903                 m_converter = 0; | 
| Chris@43 | 904             } | 
| Chris@43 | 905 | 
| Chris@43 | 906             if (m_crapConverter) { | 
| Chris@43 | 907                 src_delete(m_crapConverter); | 
| Chris@43 | 908                 m_crapConverter = 0; | 
| Chris@43 | 909             } | 
| Chris@43 | 910 | 
| Chris@43 | 911             m_mutex.unlock(); | 
| Chris@43 | 912 | 
| Chris@43 | 913             emit sampleRateMismatch(getSourceSampleRate(), | 
| Chris@43 | 914                                     getTargetSampleRate(), | 
| Chris@43 | 915                                     false); | 
| Chris@43 | 916 	} else { | 
| Chris@43 | 917 | 
| Chris@43 | 918             m_mutex.unlock(); | 
| Chris@43 | 919 | 
| Chris@43 | 920             emit sampleRateMismatch(getSourceSampleRate(), | 
| Chris@43 | 921                                     getTargetSampleRate(), | 
| Chris@43 | 922                                     true); | 
| Chris@43 | 923         } | 
| Chris@43 | 924     } else { | 
| Chris@43 | 925         m_mutex.unlock(); | 
| Chris@43 | 926     } | 
| Chris@43 | 927 } | 
| Chris@43 | 928 | 
| Chris@43 | 929 void | 
| Chris@43 | 930 AudioCallbackPlaySource::setResampleQuality(int q) | 
| Chris@43 | 931 { | 
| Chris@43 | 932     if (q == m_resampleQuality) return; | 
| Chris@43 | 933     m_resampleQuality = q; | 
| Chris@43 | 934 | 
| Chris@43 | 935 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 936     std::cerr << "AudioCallbackPlaySource::setResampleQuality: setting to " | 
| Chris@43 | 937               << m_resampleQuality << std::endl; | 
| Chris@43 | 938 #endif | 
| Chris@43 | 939 | 
| Chris@43 | 940     initialiseConverter(); | 
| Chris@43 | 941 } | 
| Chris@43 | 942 | 
| Chris@43 | 943 void | 
| Chris@107 | 944 AudioCallbackPlaySource::setAuditioningEffect(Auditionable *a) | 
| Chris@43 | 945 { | 
| Chris@107 | 946     RealTimePluginInstance *plugin = dynamic_cast<RealTimePluginInstance *>(a); | 
| Chris@107 | 947     if (a && !plugin) { | 
| Chris@107 | 948         std::cerr << "WARNING: AudioCallbackPlaySource::setAuditioningEffect: auditionable object " << a << " is not a real-time plugin instance" << std::endl; | 
| Chris@107 | 949     } | 
| Chris@43 | 950     RealTimePluginInstance *formerPlugin = m_auditioningPlugin; | 
| Chris@43 | 951     m_auditioningPlugin = plugin; | 
| Chris@43 | 952     m_auditioningPluginBypassed = false; | 
| Chris@43 | 953     if (formerPlugin) m_pluginScavenger.claim(formerPlugin); | 
| Chris@43 | 954 } | 
| Chris@43 | 955 | 
| Chris@43 | 956 void | 
| Chris@43 | 957 AudioCallbackPlaySource::setSoloModelSet(std::set<Model *> s) | 
| Chris@43 | 958 { | 
| Chris@43 | 959     m_audioGenerator->setSoloModelSet(s); | 
| Chris@43 | 960     clearRingBuffers(); | 
| Chris@43 | 961 } | 
| Chris@43 | 962 | 
| Chris@43 | 963 void | 
| Chris@43 | 964 AudioCallbackPlaySource::clearSoloModelSet() | 
| Chris@43 | 965 { | 
| Chris@43 | 966     m_audioGenerator->clearSoloModelSet(); | 
| Chris@43 | 967     clearRingBuffers(); | 
| Chris@43 | 968 } | 
| Chris@43 | 969 | 
| Chris@43 | 970 size_t | 
| Chris@43 | 971 AudioCallbackPlaySource::getTargetSampleRate() const | 
| Chris@43 | 972 { | 
| Chris@43 | 973     if (m_targetSampleRate) return m_targetSampleRate; | 
| Chris@43 | 974     else return getSourceSampleRate(); | 
| Chris@43 | 975 } | 
| Chris@43 | 976 | 
| Chris@43 | 977 size_t | 
| Chris@43 | 978 AudioCallbackPlaySource::getSourceChannelCount() const | 
| Chris@43 | 979 { | 
| Chris@43 | 980     return m_sourceChannelCount; | 
| Chris@43 | 981 } | 
| Chris@43 | 982 | 
| Chris@43 | 983 size_t | 
| Chris@43 | 984 AudioCallbackPlaySource::getTargetChannelCount() const | 
| Chris@43 | 985 { | 
| Chris@43 | 986     if (m_sourceChannelCount < 2) return 2; | 
| Chris@43 | 987     return m_sourceChannelCount; | 
| Chris@43 | 988 } | 
| Chris@43 | 989 | 
| Chris@43 | 990 size_t | 
| Chris@43 | 991 AudioCallbackPlaySource::getSourceSampleRate() const | 
| Chris@43 | 992 { | 
| Chris@43 | 993     return m_sourceSampleRate; | 
| Chris@43 | 994 } | 
| Chris@43 | 995 | 
| Chris@43 | 996 void | 
| Chris@91 | 997 AudioCallbackPlaySource::setTimeStretch(float factor) | 
| Chris@43 | 998 { | 
| Chris@91 | 999     m_stretchRatio = factor; | 
| Chris@91 | 1000 | 
| Chris@91 | 1001     if (m_timeStretcher || (factor == 1.f)) { | 
| Chris@91 | 1002         // stretch ratio will be set in next process call if appropriate | 
| Chris@62 | 1003     } else { | 
| Chris@91 | 1004         m_stretcherInputCount = getTargetChannelCount(); | 
| Chris@62 | 1005         RubberBandStretcher *stretcher = new RubberBandStretcher | 
| Chris@62 | 1006             (getTargetSampleRate(), | 
| Chris@91 | 1007              m_stretcherInputCount, | 
| Chris@62 | 1008              RubberBandStretcher::OptionProcessRealTime, | 
| Chris@62 | 1009              factor); | 
| Chris@130 | 1010         RubberBandStretcher *monoStretcher = new RubberBandStretcher | 
| Chris@130 | 1011             (getTargetSampleRate(), | 
| Chris@130 | 1012              1, | 
| Chris@130 | 1013              RubberBandStretcher::OptionProcessRealTime, | 
| Chris@130 | 1014              factor); | 
| Chris@91 | 1015         m_stretcherInputs = new float *[m_stretcherInputCount]; | 
| Chris@91 | 1016         m_stretcherInputSizes = new size_t[m_stretcherInputCount]; | 
| Chris@91 | 1017         for (size_t c = 0; c < m_stretcherInputCount; ++c) { | 
| Chris@91 | 1018             m_stretcherInputSizes[c] = 16384; | 
| Chris@91 | 1019             m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | 
| Chris@91 | 1020         } | 
| Chris@130 | 1021         m_monoStretcher = monoStretcher; | 
| Chris@62 | 1022         m_timeStretcher = stretcher; | 
| Chris@62 | 1023     } | 
| Chris@158 | 1024 | 
| Chris@158 | 1025     emit activity(tr("Change time-stretch factor to %1").arg(factor)); | 
| Chris@43 | 1026 } | 
| Chris@43 | 1027 | 
| Chris@43 | 1028 size_t | 
| Chris@130 | 1029 AudioCallbackPlaySource::getSourceSamples(size_t ucount, float **buffer) | 
| Chris@43 | 1030 { | 
| Chris@130 | 1031     int count = ucount; | 
| Chris@130 | 1032 | 
| Chris@43 | 1033     if (!m_playing) { | 
| Chris@43 | 1034 	for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { | 
| Chris@130 | 1035 	    for (int i = 0; i < count; ++i) { | 
| Chris@43 | 1036 		buffer[ch][i] = 0.0; | 
| Chris@43 | 1037 	    } | 
| Chris@43 | 1038 	} | 
| Chris@43 | 1039 	return 0; | 
| Chris@43 | 1040     } | 
| Chris@43 | 1041 | 
| Chris@43 | 1042     // Ensure that all buffers have at least the amount of data we | 
| Chris@43 | 1043     // need -- else reduce the size of our requests correspondingly | 
| Chris@43 | 1044 | 
| Chris@43 | 1045     for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { | 
| Chris@43 | 1046 | 
| Chris@43 | 1047         RingBuffer<float> *rb = getReadRingBuffer(ch); | 
| Chris@43 | 1048 | 
| Chris@43 | 1049         if (!rb) { | 
| Chris@43 | 1050             std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: " | 
| Chris@43 | 1051                       << "No ring buffer available for channel " << ch | 
| Chris@43 | 1052                       << ", returning no data here" << std::endl; | 
| Chris@43 | 1053             count = 0; | 
| Chris@43 | 1054             break; | 
| Chris@43 | 1055         } | 
| Chris@43 | 1056 | 
| Chris@43 | 1057         size_t rs = rb->getReadSpace(); | 
| Chris@43 | 1058         if (rs < count) { | 
| Chris@43 | 1059 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1060             std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: " | 
| Chris@43 | 1061                       << "Ring buffer for channel " << ch << " has only " | 
| Chris@43 | 1062                       << rs << " (of " << count << ") samples available, " | 
| Chris@43 | 1063                       << "reducing request size" << std::endl; | 
| Chris@43 | 1064 #endif | 
| Chris@43 | 1065             count = rs; | 
| Chris@43 | 1066         } | 
| Chris@43 | 1067     } | 
| Chris@43 | 1068 | 
| Chris@43 | 1069     if (count == 0) return 0; | 
| Chris@43 | 1070 | 
| Chris@62 | 1071     RubberBandStretcher *ts = m_timeStretcher; | 
| Chris@130 | 1072     RubberBandStretcher *ms = m_monoStretcher; | 
| Chris@130 | 1073 | 
| Chris@62 | 1074     float ratio = ts ? ts->getTimeRatio() : 1.f; | 
| Chris@91 | 1075 | 
| Chris@91 | 1076     if (ratio != m_stretchRatio) { | 
| Chris@91 | 1077         if (!ts) { | 
| Chris@91 | 1078             std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: Time ratio change to " << m_stretchRatio << " is pending, but no stretcher is set" << std::endl; | 
| Chris@91 | 1079             m_stretchRatio = 1.f; | 
| Chris@91 | 1080         } else { | 
| Chris@91 | 1081             ts->setTimeRatio(m_stretchRatio); | 
| Chris@130 | 1082             if (ms) ms->setTimeRatio(m_stretchRatio); | 
| Chris@130 | 1083             if (m_stretchRatio >= 1.0) m_stretchMono = false; | 
| Chris@130 | 1084         } | 
| Chris@130 | 1085     } | 
| Chris@130 | 1086 | 
| Chris@130 | 1087     int stretchChannels = m_stretcherInputCount; | 
| Chris@130 | 1088     if (m_stretchMono) { | 
| Chris@130 | 1089         if (ms) { | 
| Chris@130 | 1090             ts = ms; | 
| Chris@130 | 1091             stretchChannels = 1; | 
| Chris@130 | 1092         } else { | 
| Chris@130 | 1093             m_stretchMono = false; | 
| Chris@91 | 1094         } | 
| Chris@91 | 1095     } | 
| Chris@91 | 1096 | 
| Chris@91 | 1097     if (m_target) { | 
| Chris@91 | 1098         m_lastRetrievedBlockSize = count; | 
| Chris@91 | 1099         m_lastRetrievalTimestamp = m_target->getCurrentTime(); | 
| Chris@91 | 1100     } | 
| Chris@43 | 1101 | 
| Chris@62 | 1102     if (!ts || ratio == 1.f) { | 
| Chris@43 | 1103 | 
| Chris@130 | 1104 	int got = 0; | 
| Chris@43 | 1105 | 
| Chris@43 | 1106 	for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { | 
| Chris@43 | 1107 | 
| Chris@43 | 1108 	    RingBuffer<float> *rb = getReadRingBuffer(ch); | 
| Chris@43 | 1109 | 
| Chris@43 | 1110 	    if (rb) { | 
| Chris@43 | 1111 | 
| Chris@43 | 1112 		// this is marginally more likely to leave our channels in | 
| Chris@43 | 1113 		// sync after a processing failure than just passing "count": | 
| Chris@43 | 1114 		size_t request = count; | 
| Chris@43 | 1115 		if (ch > 0) request = got; | 
| Chris@43 | 1116 | 
| Chris@43 | 1117 		got = rb->read(buffer[ch], request); | 
| Chris@43 | 1118 | 
| Chris@43 | 1119 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 
| Chris@43 | 1120 		std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << std::endl; | 
| Chris@43 | 1121 #endif | 
| Chris@43 | 1122 	    } | 
| Chris@43 | 1123 | 
| Chris@43 | 1124 	    for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { | 
| Chris@130 | 1125 		for (int i = got; i < count; ++i) { | 
| Chris@43 | 1126 		    buffer[ch][i] = 0.0; | 
| Chris@43 | 1127 		} | 
| Chris@43 | 1128 	    } | 
| Chris@43 | 1129 	} | 
| Chris@43 | 1130 | 
| Chris@43 | 1131         applyAuditioningEffect(count, buffer); | 
| Chris@43 | 1132 | 
| Chris@43 | 1133         m_condition.wakeAll(); | 
| Chris@91 | 1134 | 
| Chris@43 | 1135 	return got; | 
| Chris@43 | 1136     } | 
| Chris@43 | 1137 | 
| Chris@62 | 1138     size_t channels = getTargetChannelCount(); | 
| Chris@91 | 1139     size_t available; | 
| Chris@91 | 1140     int warned = 0; | 
| Chris@91 | 1141     size_t fedToStretcher = 0; | 
| Chris@43 | 1142 | 
| Chris@91 | 1143     // The input block for a given output is approx output / ratio, | 
| Chris@91 | 1144     // but we can't predict it exactly, for an adaptive timestretcher. | 
| Chris@91 | 1145 | 
| Chris@91 | 1146     while ((available = ts->available()) < count) { | 
| Chris@91 | 1147 | 
| Chris@91 | 1148         size_t reqd = lrintf((count - available) / ratio); | 
| Chris@91 | 1149         reqd = std::max(reqd, ts->getSamplesRequired()); | 
| Chris@91 | 1150         if (reqd == 0) reqd = 1; | 
| Chris@91 | 1151 | 
| Chris@91 | 1152         size_t got = reqd; | 
| Chris@91 | 1153 | 
| Chris@91 | 1154 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 
| Chris@91 | 1155         std::cerr << "reqd = " <<reqd << ", channels = " << channels << ", ic = " << m_stretcherInputCount << std::endl; | 
| Chris@62 | 1156 #endif | 
| Chris@43 | 1157 | 
| Chris@91 | 1158         for (size_t c = 0; c < channels; ++c) { | 
| Chris@131 | 1159             if (c >= m_stretcherInputCount) continue; | 
| Chris@91 | 1160             if (reqd > m_stretcherInputSizes[c]) { | 
| Chris@91 | 1161                 if (c == 0) { | 
| Chris@91 | 1162                     std::cerr << "WARNING: resizing stretcher input buffer from " << m_stretcherInputSizes[c] << " to " << (reqd * 2) << std::endl; | 
| Chris@91 | 1163                 } | 
| Chris@91 | 1164                 delete[] m_stretcherInputs[c]; | 
| Chris@91 | 1165                 m_stretcherInputSizes[c] = reqd * 2; | 
| Chris@91 | 1166                 m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; | 
| Chris@91 | 1167             } | 
| Chris@91 | 1168         } | 
| Chris@43 | 1169 | 
| Chris@91 | 1170         for (size_t c = 0; c < channels; ++c) { | 
| Chris@131 | 1171             if (c >= m_stretcherInputCount) continue; | 
| Chris@91 | 1172             RingBuffer<float> *rb = getReadRingBuffer(c); | 
| Chris@91 | 1173             if (rb) { | 
| Chris@130 | 1174                 size_t gotHere; | 
| Chris@130 | 1175                 if (stretchChannels == 1 && c > 0) { | 
| Chris@130 | 1176                     gotHere = rb->readAdding(m_stretcherInputs[0], got); | 
| Chris@130 | 1177                 } else { | 
| Chris@130 | 1178                     gotHere = rb->read(m_stretcherInputs[c], got); | 
| Chris@130 | 1179                 } | 
| Chris@91 | 1180                 if (gotHere < got) got = gotHere; | 
| Chris@91 | 1181 | 
| Chris@91 | 1182 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 
| Chris@91 | 1183                 if (c == 0) { | 
| Chris@91 | 1184                     std::cerr << "feeding stretcher: got " << gotHere | 
| Chris@91 | 1185                               << ", " << rb->getReadSpace() << " remain" << std::endl; | 
| Chris@91 | 1186                 } | 
| Chris@62 | 1187 #endif | 
| Chris@43 | 1188 | 
| Chris@91 | 1189             } else { | 
| Chris@91 | 1190                 std::cerr << "WARNING: No ring buffer available for channel " << c << " in stretcher input block" << std::endl; | 
| Chris@43 | 1191             } | 
| Chris@43 | 1192         } | 
| Chris@43 | 1193 | 
| Chris@43 | 1194         if (got < reqd) { | 
| Chris@43 | 1195             std::cerr << "WARNING: Read underrun in playback (" | 
| Chris@43 | 1196                       << got << " < " << reqd << ")" << std::endl; | 
| Chris@43 | 1197         } | 
| Chris@43 | 1198 | 
| Chris@91 | 1199         ts->process(m_stretcherInputs, got, false); | 
| Chris@91 | 1200 | 
| Chris@91 | 1201         fedToStretcher += got; | 
| Chris@43 | 1202 | 
| Chris@43 | 1203         if (got == 0) break; | 
| Chris@43 | 1204 | 
| Chris@62 | 1205         if (ts->available() == available) { | 
| Chris@43 | 1206             std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << std::endl; | 
| Chris@43 | 1207             if (++warned == 5) break; | 
| Chris@43 | 1208         } | 
| Chris@43 | 1209     } | 
| Chris@43 | 1210 | 
| Chris@62 | 1211     ts->retrieve(buffer, count); | 
| Chris@43 | 1212 | 
| Chris@130 | 1213     for (int c = stretchChannels; c < getTargetChannelCount(); ++c) { | 
| Chris@130 | 1214         for (int i = 0; i < count; ++i) { | 
| Chris@130 | 1215             buffer[c][i] = buffer[0][i]; | 
| Chris@130 | 1216         } | 
| Chris@130 | 1217     } | 
| Chris@130 | 1218 | 
| Chris@43 | 1219     applyAuditioningEffect(count, buffer); | 
| Chris@43 | 1220 | 
| Chris@43 | 1221     m_condition.wakeAll(); | 
| Chris@43 | 1222 | 
| Chris@43 | 1223     return count; | 
| Chris@43 | 1224 } | 
| Chris@43 | 1225 | 
| Chris@43 | 1226 void | 
| Chris@43 | 1227 AudioCallbackPlaySource::applyAuditioningEffect(size_t count, float **buffers) | 
| Chris@43 | 1228 { | 
| Chris@43 | 1229     if (m_auditioningPluginBypassed) return; | 
| Chris@43 | 1230     RealTimePluginInstance *plugin = m_auditioningPlugin; | 
| Chris@43 | 1231     if (!plugin) return; | 
| Chris@43 | 1232 | 
| Chris@43 | 1233     if (plugin->getAudioInputCount() != getTargetChannelCount()) { | 
| Chris@43 | 1234 //        std::cerr << "plugin input count " << plugin->getAudioInputCount() | 
| Chris@43 | 1235 //                  << " != our channel count " << getTargetChannelCount() | 
| Chris@43 | 1236 //                  << std::endl; | 
| Chris@43 | 1237         return; | 
| Chris@43 | 1238     } | 
| Chris@43 | 1239     if (plugin->getAudioOutputCount() != getTargetChannelCount()) { | 
| Chris@43 | 1240 //        std::cerr << "plugin output count " << plugin->getAudioOutputCount() | 
| Chris@43 | 1241 //                  << " != our channel count " << getTargetChannelCount() | 
| Chris@43 | 1242 //                  << std::endl; | 
| Chris@43 | 1243         return; | 
| Chris@43 | 1244     } | 
| Chris@102 | 1245     if (plugin->getBufferSize() < count) { | 
| Chris@43 | 1246 //        std::cerr << "plugin buffer size " << plugin->getBufferSize() | 
| Chris@102 | 1247 //                  << " < our block size " << count | 
| Chris@43 | 1248 //                  << std::endl; | 
| Chris@43 | 1249         return; | 
| Chris@43 | 1250     } | 
| Chris@43 | 1251 | 
| Chris@43 | 1252     float **ib = plugin->getAudioInputBuffers(); | 
| Chris@43 | 1253     float **ob = plugin->getAudioOutputBuffers(); | 
| Chris@43 | 1254 | 
| Chris@43 | 1255     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 1256         for (size_t i = 0; i < count; ++i) { | 
| Chris@43 | 1257             ib[c][i] = buffers[c][i]; | 
| Chris@43 | 1258         } | 
| Chris@43 | 1259     } | 
| Chris@43 | 1260 | 
| Chris@102 | 1261     plugin->run(Vamp::RealTime::zeroTime, count); | 
| Chris@43 | 1262 | 
| Chris@43 | 1263     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 1264         for (size_t i = 0; i < count; ++i) { | 
| Chris@43 | 1265             buffers[c][i] = ob[c][i]; | 
| Chris@43 | 1266         } | 
| Chris@43 | 1267     } | 
| Chris@43 | 1268 } | 
| Chris@43 | 1269 | 
| Chris@43 | 1270 // Called from fill thread, m_playing true, mutex held | 
| Chris@43 | 1271 bool | 
| Chris@43 | 1272 AudioCallbackPlaySource::fillBuffers() | 
| Chris@43 | 1273 { | 
| Chris@43 | 1274     static float *tmp = 0; | 
| Chris@43 | 1275     static size_t tmpSize = 0; | 
| Chris@43 | 1276 | 
| Chris@43 | 1277     size_t space = 0; | 
| Chris@43 | 1278     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 1279 	RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1280 	if (wb) { | 
| Chris@43 | 1281 	    size_t spaceHere = wb->getWriteSpace(); | 
| Chris@43 | 1282 	    if (c == 0 || spaceHere < space) space = spaceHere; | 
| Chris@43 | 1283 	} | 
| Chris@43 | 1284     } | 
| Chris@43 | 1285 | 
| Chris@103 | 1286     if (space == 0) { | 
| Chris@103 | 1287 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@103 | 1288         std::cout << "AudioCallbackPlaySourceFillThread: no space to fill" << std::endl; | 
| Chris@103 | 1289 #endif | 
| Chris@103 | 1290         return false; | 
| Chris@103 | 1291     } | 
| Chris@43 | 1292 | 
| Chris@43 | 1293     size_t f = m_writeBufferFill; | 
| Chris@43 | 1294 | 
| Chris@43 | 1295     bool readWriteEqual = (m_readBuffers == m_writeBuffers); | 
| Chris@43 | 1296 | 
| Chris@43 | 1297 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1298     std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl; | 
| Chris@43 | 1299 #endif | 
| Chris@43 | 1300 | 
| Chris@43 | 1301 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1302     std::cout << "buffered to " << f << " already" << std::endl; | 
| Chris@43 | 1303 #endif | 
| Chris@43 | 1304 | 
| Chris@43 | 1305     bool resample = (getSourceSampleRate() != getTargetSampleRate()); | 
| Chris@43 | 1306 | 
| Chris@43 | 1307 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1308     std::cout << (resample ? "" : "not ") << "resampling (source " << getSourceSampleRate() << ", target " << getTargetSampleRate() << ")" << std::endl; | 
| Chris@43 | 1309 #endif | 
| Chris@43 | 1310 | 
| Chris@43 | 1311     size_t channels = getTargetChannelCount(); | 
| Chris@43 | 1312 | 
| Chris@43 | 1313     size_t orig = space; | 
| Chris@43 | 1314     size_t got = 0; | 
| Chris@43 | 1315 | 
| Chris@43 | 1316     static float **bufferPtrs = 0; | 
| Chris@43 | 1317     static size_t bufferPtrCount = 0; | 
| Chris@43 | 1318 | 
| Chris@43 | 1319     if (bufferPtrCount < channels) { | 
| Chris@43 | 1320 	if (bufferPtrs) delete[] bufferPtrs; | 
| Chris@43 | 1321 	bufferPtrs = new float *[channels]; | 
| Chris@43 | 1322 	bufferPtrCount = channels; | 
| Chris@43 | 1323     } | 
| Chris@43 | 1324 | 
| Chris@43 | 1325     size_t generatorBlockSize = m_audioGenerator->getBlockSize(); | 
| Chris@43 | 1326 | 
| Chris@43 | 1327     if (resample && !m_converter) { | 
| Chris@43 | 1328 	static bool warned = false; | 
| Chris@43 | 1329 	if (!warned) { | 
| Chris@43 | 1330 	    std::cerr << "WARNING: sample rates differ, but no converter available!" << std::endl; | 
| Chris@43 | 1331 	    warned = true; | 
| Chris@43 | 1332 	} | 
| Chris@43 | 1333     } | 
| Chris@43 | 1334 | 
| Chris@43 | 1335     if (resample && m_converter) { | 
| Chris@43 | 1336 | 
| Chris@43 | 1337 	double ratio = | 
| Chris@43 | 1338 	    double(getTargetSampleRate()) / double(getSourceSampleRate()); | 
| Chris@43 | 1339 	orig = size_t(orig / ratio + 0.1); | 
| Chris@43 | 1340 | 
| Chris@43 | 1341 	// orig must be a multiple of generatorBlockSize | 
| Chris@43 | 1342 	orig = (orig / generatorBlockSize) * generatorBlockSize; | 
| Chris@43 | 1343 	if (orig == 0) return false; | 
| Chris@43 | 1344 | 
| Chris@43 | 1345 	size_t work = std::max(orig, space); | 
| Chris@43 | 1346 | 
| Chris@43 | 1347 	// We only allocate one buffer, but we use it in two halves. | 
| Chris@43 | 1348 	// We place the non-interleaved values in the second half of | 
| Chris@43 | 1349 	// the buffer (orig samples for channel 0, orig samples for | 
| Chris@43 | 1350 	// channel 1 etc), and then interleave them into the first | 
| Chris@43 | 1351 	// half of the buffer.  Then we resample back into the second | 
| Chris@43 | 1352 	// half (interleaved) and de-interleave the results back to | 
| Chris@43 | 1353 	// the start of the buffer for insertion into the ringbuffers. | 
| Chris@43 | 1354 	// What a faff -- especially as we've already de-interleaved | 
| Chris@43 | 1355 	// the audio data from the source file elsewhere before we | 
| Chris@43 | 1356 	// even reach this point. | 
| Chris@43 | 1357 | 
| Chris@43 | 1358 	if (tmpSize < channels * work * 2) { | 
| Chris@43 | 1359 	    delete[] tmp; | 
| Chris@43 | 1360 	    tmp = new float[channels * work * 2]; | 
| Chris@43 | 1361 	    tmpSize = channels * work * 2; | 
| Chris@43 | 1362 	} | 
| Chris@43 | 1363 | 
| Chris@43 | 1364 	float *nonintlv = tmp + channels * work; | 
| Chris@43 | 1365 	float *intlv = tmp; | 
| Chris@43 | 1366 	float *srcout = tmp + channels * work; | 
| Chris@43 | 1367 | 
| Chris@43 | 1368 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1369 	    for (size_t i = 0; i < orig; ++i) { | 
| Chris@43 | 1370 		nonintlv[channels * i + c] = 0.0f; | 
| Chris@43 | 1371 	    } | 
| Chris@43 | 1372 	} | 
| Chris@43 | 1373 | 
| Chris@43 | 1374 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1375 	    bufferPtrs[c] = nonintlv + c * orig; | 
| Chris@43 | 1376 	} | 
| Chris@43 | 1377 | 
| Chris@163 | 1378 	got = mixModels(f, orig, bufferPtrs); // also modifies f | 
| Chris@43 | 1379 | 
| Chris@43 | 1380 	// and interleave into first half | 
| Chris@43 | 1381 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1382 	    for (size_t i = 0; i < got; ++i) { | 
| Chris@43 | 1383 		float sample = nonintlv[c * got + i]; | 
| Chris@43 | 1384 		intlv[channels * i + c] = sample; | 
| Chris@43 | 1385 	    } | 
| Chris@43 | 1386 	} | 
| Chris@43 | 1387 | 
| Chris@43 | 1388 	SRC_DATA data; | 
| Chris@43 | 1389 	data.data_in = intlv; | 
| Chris@43 | 1390 	data.data_out = srcout; | 
| Chris@43 | 1391 	data.input_frames = got; | 
| Chris@43 | 1392 	data.output_frames = work; | 
| Chris@43 | 1393 	data.src_ratio = ratio; | 
| Chris@43 | 1394 	data.end_of_input = 0; | 
| Chris@43 | 1395 | 
| Chris@43 | 1396 	int err = 0; | 
| Chris@43 | 1397 | 
| Chris@62 | 1398         if (m_timeStretcher && m_timeStretcher->getTimeRatio() < 0.4) { | 
| Chris@43 | 1399 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1400             std::cout << "Using crappy converter" << std::endl; | 
| Chris@43 | 1401 #endif | 
| Chris@43 | 1402             err = src_process(m_crapConverter, &data); | 
| Chris@43 | 1403         } else { | 
| Chris@43 | 1404             err = src_process(m_converter, &data); | 
| Chris@43 | 1405         } | 
| Chris@43 | 1406 | 
| Chris@43 | 1407 	size_t toCopy = size_t(got * ratio + 0.1); | 
| Chris@43 | 1408 | 
| Chris@43 | 1409 	if (err) { | 
| Chris@43 | 1410 	    std::cerr | 
| Chris@43 | 1411 		<< "AudioCallbackPlaySourceFillThread: ERROR in samplerate conversion: " | 
| Chris@43 | 1412 		<< src_strerror(err) << std::endl; | 
| Chris@43 | 1413 	    //!!! Then what? | 
| Chris@43 | 1414 	} else { | 
| Chris@43 | 1415 	    got = data.input_frames_used; | 
| Chris@43 | 1416 	    toCopy = data.output_frames_gen; | 
| Chris@43 | 1417 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1418 	    std::cout << "Resampled " << got << " frames to " << toCopy << " frames" << std::endl; | 
| Chris@43 | 1419 #endif | 
| Chris@43 | 1420 	} | 
| Chris@43 | 1421 | 
| Chris@43 | 1422 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1423 	    for (size_t i = 0; i < toCopy; ++i) { | 
| Chris@43 | 1424 		tmp[i] = srcout[channels * i + c]; | 
| Chris@43 | 1425 	    } | 
| Chris@43 | 1426 	    RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1427 	    if (wb) wb->write(tmp, toCopy); | 
| Chris@43 | 1428 	} | 
| Chris@43 | 1429 | 
| Chris@43 | 1430 	m_writeBufferFill = f; | 
| Chris@43 | 1431 	if (readWriteEqual) m_readBufferFill = f; | 
| Chris@43 | 1432 | 
| Chris@43 | 1433     } else { | 
| Chris@43 | 1434 | 
| Chris@43 | 1435 	// space must be a multiple of generatorBlockSize | 
| Chris@43 | 1436 	space = (space / generatorBlockSize) * generatorBlockSize; | 
| Chris@91 | 1437 	if (space == 0) { | 
| Chris@91 | 1438 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@91 | 1439             std::cout << "requested fill is less than generator block size of " | 
| Chris@91 | 1440                       << generatorBlockSize << ", leaving it" << std::endl; | 
| Chris@91 | 1441 #endif | 
| Chris@91 | 1442             return false; | 
| Chris@91 | 1443         } | 
| Chris@43 | 1444 | 
| Chris@43 | 1445 	if (tmpSize < channels * space) { | 
| Chris@43 | 1446 	    delete[] tmp; | 
| Chris@43 | 1447 	    tmp = new float[channels * space]; | 
| Chris@43 | 1448 	    tmpSize = channels * space; | 
| Chris@43 | 1449 	} | 
| Chris@43 | 1450 | 
| Chris@43 | 1451 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1452 | 
| Chris@43 | 1453 	    bufferPtrs[c] = tmp + c * space; | 
| Chris@43 | 1454 | 
| Chris@43 | 1455 	    for (size_t i = 0; i < space; ++i) { | 
| Chris@43 | 1456 		tmp[c * space + i] = 0.0f; | 
| Chris@43 | 1457 	    } | 
| Chris@43 | 1458 	} | 
| Chris@43 | 1459 | 
| Chris@163 | 1460 	size_t got = mixModels(f, space, bufferPtrs); // also modifies f | 
| Chris@43 | 1461 | 
| Chris@43 | 1462 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1463 | 
| Chris@43 | 1464 	    RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1465 	    if (wb) { | 
| Chris@43 | 1466                 size_t actual = wb->write(bufferPtrs[c], got); | 
| Chris@43 | 1467 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1468 		std::cout << "Wrote " << actual << " samples for ch " << c << ", now " | 
| Chris@43 | 1469 			  << wb->getReadSpace() << " to read" | 
| Chris@43 | 1470 			  << std::endl; | 
| Chris@43 | 1471 #endif | 
| Chris@43 | 1472                 if (actual < got) { | 
| Chris@43 | 1473                     std::cerr << "WARNING: Buffer overrun in channel " << c | 
| Chris@43 | 1474                               << ": wrote " << actual << " of " << got | 
| Chris@43 | 1475                               << " samples" << std::endl; | 
| Chris@43 | 1476                 } | 
| Chris@43 | 1477             } | 
| Chris@43 | 1478 	} | 
| Chris@43 | 1479 | 
| Chris@43 | 1480 	m_writeBufferFill = f; | 
| Chris@43 | 1481 	if (readWriteEqual) m_readBufferFill = f; | 
| Chris@43 | 1482 | 
| Chris@163 | 1483 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@163 | 1484         std::cout << "Read buffer fill is now " << m_readBufferFill << std::endl; | 
| Chris@163 | 1485 #endif | 
| Chris@163 | 1486 | 
| Chris@43 | 1487 	//!!! 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 | 
| Chris@43 | 1488     } | 
| Chris@43 | 1489 | 
| Chris@43 | 1490     return true; | 
| Chris@43 | 1491 } | 
| Chris@43 | 1492 | 
| Chris@43 | 1493 size_t | 
| Chris@43 | 1494 AudioCallbackPlaySource::mixModels(size_t &frame, size_t count, float **buffers) | 
| Chris@43 | 1495 { | 
| Chris@43 | 1496     size_t processed = 0; | 
| Chris@43 | 1497     size_t chunkStart = frame; | 
| Chris@43 | 1498     size_t chunkSize = count; | 
| Chris@43 | 1499     size_t selectionSize = 0; | 
| Chris@43 | 1500     size_t nextChunkStart = chunkStart + chunkSize; | 
| Chris@43 | 1501 | 
| Chris@43 | 1502     bool looping = m_viewManager->getPlayLoopMode(); | 
| Chris@43 | 1503     bool constrained = (m_viewManager->getPlaySelectionMode() && | 
| Chris@43 | 1504 			!m_viewManager->getSelections().empty()); | 
| Chris@43 | 1505 | 
| Chris@43 | 1506     static float **chunkBufferPtrs = 0; | 
| Chris@43 | 1507     static size_t chunkBufferPtrCount = 0; | 
| Chris@43 | 1508     size_t channels = getTargetChannelCount(); | 
| Chris@43 | 1509 | 
| Chris@43 | 1510 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1511     std::cout << "Selection playback: start " << frame << ", size " << count <<", channels " << channels << std::endl; | 
| Chris@43 | 1512 #endif | 
| Chris@43 | 1513 | 
| Chris@43 | 1514     if (chunkBufferPtrCount < channels) { | 
| Chris@43 | 1515 	if (chunkBufferPtrs) delete[] chunkBufferPtrs; | 
| Chris@43 | 1516 	chunkBufferPtrs = new float *[channels]; | 
| Chris@43 | 1517 	chunkBufferPtrCount = channels; | 
| Chris@43 | 1518     } | 
| Chris@43 | 1519 | 
| Chris@43 | 1520     for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1521 	chunkBufferPtrs[c] = buffers[c]; | 
| Chris@43 | 1522     } | 
| Chris@43 | 1523 | 
| Chris@43 | 1524     while (processed < count) { | 
| Chris@43 | 1525 | 
| Chris@43 | 1526 	chunkSize = count - processed; | 
| Chris@43 | 1527 	nextChunkStart = chunkStart + chunkSize; | 
| Chris@43 | 1528 	selectionSize = 0; | 
| Chris@43 | 1529 | 
| Chris@43 | 1530 	size_t fadeIn = 0, fadeOut = 0; | 
| Chris@43 | 1531 | 
| Chris@43 | 1532 	if (constrained) { | 
| Chris@60 | 1533 | 
| Chris@60 | 1534             size_t rChunkStart = | 
| Chris@60 | 1535                 m_viewManager->alignPlaybackFrameToReference(chunkStart); | 
| Chris@43 | 1536 | 
| Chris@43 | 1537 	    Selection selection = | 
| Chris@60 | 1538 		m_viewManager->getContainingSelection(rChunkStart, true); | 
| Chris@43 | 1539 | 
| Chris@43 | 1540 	    if (selection.isEmpty()) { | 
| Chris@43 | 1541 		if (looping) { | 
| Chris@43 | 1542 		    selection = *m_viewManager->getSelections().begin(); | 
| Chris@60 | 1543 		    chunkStart = m_viewManager->alignReferenceToPlaybackFrame | 
| Chris@60 | 1544                         (selection.getStartFrame()); | 
| Chris@43 | 1545 		    fadeIn = 50; | 
| Chris@43 | 1546 		} | 
| Chris@43 | 1547 	    } | 
| Chris@43 | 1548 | 
| Chris@43 | 1549 	    if (selection.isEmpty()) { | 
| Chris@43 | 1550 | 
| Chris@43 | 1551 		chunkSize = 0; | 
| Chris@43 | 1552 		nextChunkStart = chunkStart; | 
| Chris@43 | 1553 | 
| Chris@43 | 1554 	    } else { | 
| Chris@43 | 1555 | 
| Chris@60 | 1556                 size_t sf = m_viewManager->alignReferenceToPlaybackFrame | 
| Chris@60 | 1557                     (selection.getStartFrame()); | 
| Chris@60 | 1558                 size_t ef = m_viewManager->alignReferenceToPlaybackFrame | 
| Chris@60 | 1559                     (selection.getEndFrame()); | 
| Chris@43 | 1560 | 
| Chris@60 | 1561 		selectionSize = ef - sf; | 
| Chris@60 | 1562 | 
| Chris@60 | 1563 		if (chunkStart < sf) { | 
| Chris@60 | 1564 		    chunkStart = sf; | 
| Chris@43 | 1565 		    fadeIn = 50; | 
| Chris@43 | 1566 		} | 
| Chris@43 | 1567 | 
| Chris@43 | 1568 		nextChunkStart = chunkStart + chunkSize; | 
| Chris@43 | 1569 | 
| Chris@60 | 1570 		if (nextChunkStart >= ef) { | 
| Chris@60 | 1571 		    nextChunkStart = ef; | 
| Chris@43 | 1572 		    fadeOut = 50; | 
| Chris@43 | 1573 		} | 
| Chris@43 | 1574 | 
| Chris@43 | 1575 		chunkSize = nextChunkStart - chunkStart; | 
| Chris@43 | 1576 	    } | 
| Chris@43 | 1577 | 
| Chris@43 | 1578 	} else if (looping && m_lastModelEndFrame > 0) { | 
| Chris@43 | 1579 | 
| Chris@43 | 1580 	    if (chunkStart >= m_lastModelEndFrame) { | 
| Chris@43 | 1581 		chunkStart = 0; | 
| Chris@43 | 1582 	    } | 
| Chris@43 | 1583 	    if (chunkSize > m_lastModelEndFrame - chunkStart) { | 
| Chris@43 | 1584 		chunkSize = m_lastModelEndFrame - chunkStart; | 
| Chris@43 | 1585 	    } | 
| Chris@43 | 1586 	    nextChunkStart = chunkStart + chunkSize; | 
| Chris@43 | 1587 	} | 
| Chris@43 | 1588 | 
| Chris@43 | 1589 //	std::cout << "chunkStart " << chunkStart << ", chunkSize " << chunkSize << ", nextChunkStart " << nextChunkStart << ", frame " << frame << ", count " << count << ", processed " << processed << std::endl; | 
| Chris@43 | 1590 | 
| Chris@43 | 1591 	if (!chunkSize) { | 
| Chris@43 | 1592 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1593 	    std::cout << "Ending selection playback at " << nextChunkStart << std::endl; | 
| Chris@43 | 1594 #endif | 
| Chris@43 | 1595 	    // We need to maintain full buffers so that the other | 
| Chris@43 | 1596 	    // thread can tell where it's got to in the playback -- so | 
| Chris@43 | 1597 	    // return the full amount here | 
| Chris@43 | 1598 	    frame = frame + count; | 
| Chris@43 | 1599 	    return count; | 
| Chris@43 | 1600 	} | 
| Chris@43 | 1601 | 
| Chris@43 | 1602 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1603 	std::cout << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << std::endl; | 
| Chris@43 | 1604 #endif | 
| Chris@43 | 1605 | 
| Chris@43 | 1606 	size_t got = 0; | 
| Chris@43 | 1607 | 
| Chris@43 | 1608 	if (selectionSize < 100) { | 
| Chris@43 | 1609 	    fadeIn = 0; | 
| Chris@43 | 1610 	    fadeOut = 0; | 
| Chris@43 | 1611 	} else if (selectionSize < 300) { | 
| Chris@43 | 1612 	    if (fadeIn > 0) fadeIn = 10; | 
| Chris@43 | 1613 	    if (fadeOut > 0) fadeOut = 10; | 
| Chris@43 | 1614 	} | 
| Chris@43 | 1615 | 
| Chris@43 | 1616 	if (fadeIn > 0) { | 
| Chris@43 | 1617 	    if (processed * 2 < fadeIn) { | 
| Chris@43 | 1618 		fadeIn = processed * 2; | 
| Chris@43 | 1619 	    } | 
| Chris@43 | 1620 	} | 
| Chris@43 | 1621 | 
| Chris@43 | 1622 	if (fadeOut > 0) { | 
| Chris@43 | 1623 	    if ((count - processed - chunkSize) * 2 < fadeOut) { | 
| Chris@43 | 1624 		fadeOut = (count - processed - chunkSize) * 2; | 
| Chris@43 | 1625 	    } | 
| Chris@43 | 1626 	} | 
| Chris@43 | 1627 | 
| Chris@43 | 1628 	for (std::set<Model *>::iterator mi = m_models.begin(); | 
| Chris@43 | 1629 	     mi != m_models.end(); ++mi) { | 
| Chris@43 | 1630 | 
| Chris@43 | 1631 	    got = m_audioGenerator->mixModel(*mi, chunkStart, | 
| Chris@43 | 1632 					     chunkSize, chunkBufferPtrs, | 
| Chris@43 | 1633 					     fadeIn, fadeOut); | 
| Chris@43 | 1634 	} | 
| Chris@43 | 1635 | 
| Chris@43 | 1636 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1637 	    chunkBufferPtrs[c] += chunkSize; | 
| Chris@43 | 1638 	} | 
| Chris@43 | 1639 | 
| Chris@43 | 1640 	processed += chunkSize; | 
| Chris@43 | 1641 	chunkStart = nextChunkStart; | 
| Chris@43 | 1642     } | 
| Chris@43 | 1643 | 
| Chris@43 | 1644 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1645     std::cout << "Returning selection playback " << processed << " frames to " << nextChunkStart << std::endl; | 
| Chris@43 | 1646 #endif | 
| Chris@43 | 1647 | 
| Chris@43 | 1648     frame = nextChunkStart; | 
| Chris@43 | 1649     return processed; | 
| Chris@43 | 1650 } | 
| Chris@43 | 1651 | 
| Chris@43 | 1652 void | 
| Chris@43 | 1653 AudioCallbackPlaySource::unifyRingBuffers() | 
| Chris@43 | 1654 { | 
| Chris@43 | 1655     if (m_readBuffers == m_writeBuffers) return; | 
| Chris@43 | 1656 | 
| Chris@43 | 1657     // only unify if there will be something to read | 
| Chris@43 | 1658     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 1659 	RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1660 	if (wb) { | 
| Chris@43 | 1661 	    if (wb->getReadSpace() < m_blockSize * 2) { | 
| Chris@43 | 1662 		if ((m_writeBufferFill + m_blockSize * 2) < | 
| Chris@43 | 1663 		    m_lastModelEndFrame) { | 
| Chris@43 | 1664 		    // OK, we don't have enough and there's more to | 
| Chris@43 | 1665 		    // read -- don't unify until we can do better | 
| Chris@43 | 1666 		    return; | 
| Chris@43 | 1667 		} | 
| Chris@43 | 1668 	    } | 
| Chris@43 | 1669 	    break; | 
| Chris@43 | 1670 	} | 
| Chris@43 | 1671     } | 
| Chris@43 | 1672 | 
| Chris@43 | 1673     size_t rf = m_readBufferFill; | 
| Chris@43 | 1674     RingBuffer<float> *rb = getReadRingBuffer(0); | 
| Chris@43 | 1675     if (rb) { | 
| Chris@43 | 1676 	size_t rs = rb->getReadSpace(); | 
| Chris@43 | 1677 	//!!! incorrect when in non-contiguous selection, see comments elsewhere | 
| Chris@43 | 1678 //	std::cout << "rs = " << rs << std::endl; | 
| Chris@43 | 1679 	if (rs < rf) rf -= rs; | 
| Chris@43 | 1680 	else rf = 0; | 
| Chris@43 | 1681     } | 
| Chris@43 | 1682 | 
| Chris@43 | 1683     //std::cout << "m_readBufferFill = " << m_readBufferFill << ", rf = " << rf << ", m_writeBufferFill = " << m_writeBufferFill << std::endl; | 
| Chris@43 | 1684 | 
| Chris@43 | 1685     size_t wf = m_writeBufferFill; | 
| Chris@43 | 1686     size_t skip = 0; | 
| Chris@43 | 1687     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 1688 	RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1689 	if (wb) { | 
| Chris@43 | 1690 	    if (c == 0) { | 
| Chris@43 | 1691 | 
| Chris@43 | 1692 		size_t wrs = wb->getReadSpace(); | 
| Chris@43 | 1693 //		std::cout << "wrs = " << wrs << std::endl; | 
| Chris@43 | 1694 | 
| Chris@43 | 1695 		if (wrs < wf) wf -= wrs; | 
| Chris@43 | 1696 		else wf = 0; | 
| Chris@43 | 1697 //		std::cout << "wf = " << wf << std::endl; | 
| Chris@43 | 1698 | 
| Chris@43 | 1699 		if (wf < rf) skip = rf - wf; | 
| Chris@43 | 1700 		if (skip == 0) break; | 
| Chris@43 | 1701 	    } | 
| Chris@43 | 1702 | 
| Chris@43 | 1703 //	    std::cout << "skipping " << skip << std::endl; | 
| Chris@43 | 1704 	    wb->skip(skip); | 
| Chris@43 | 1705 	} | 
| Chris@43 | 1706     } | 
| Chris@43 | 1707 | 
| Chris@43 | 1708     m_bufferScavenger.claim(m_readBuffers); | 
| Chris@43 | 1709     m_readBuffers = m_writeBuffers; | 
| Chris@43 | 1710     m_readBufferFill = m_writeBufferFill; | 
| Chris@43 | 1711 //    std::cout << "unified" << std::endl; | 
| Chris@43 | 1712 } | 
| Chris@43 | 1713 | 
| Chris@43 | 1714 void | 
| Chris@43 | 1715 AudioCallbackPlaySource::FillThread::run() | 
| Chris@43 | 1716 { | 
| Chris@43 | 1717     AudioCallbackPlaySource &s(m_source); | 
| Chris@43 | 1718 | 
| Chris@43 | 1719 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1720     std::cout << "AudioCallbackPlaySourceFillThread starting" << std::endl; | 
| Chris@43 | 1721 #endif | 
| Chris@43 | 1722 | 
| Chris@43 | 1723     s.m_mutex.lock(); | 
| Chris@43 | 1724 | 
| Chris@43 | 1725     bool previouslyPlaying = s.m_playing; | 
| Chris@43 | 1726     bool work = false; | 
| Chris@43 | 1727 | 
| Chris@43 | 1728     while (!s.m_exiting) { | 
| Chris@43 | 1729 | 
| Chris@43 | 1730 	s.unifyRingBuffers(); | 
| Chris@43 | 1731 	s.m_bufferScavenger.scavenge(); | 
| Chris@43 | 1732         s.m_pluginScavenger.scavenge(); | 
| Chris@43 | 1733 | 
| Chris@43 | 1734 	if (work && s.m_playing && s.getSourceSampleRate()) { | 
| Chris@43 | 1735 | 
| Chris@43 | 1736 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1737 	    std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl; | 
| Chris@43 | 1738 #endif | 
| Chris@43 | 1739 | 
| Chris@43 | 1740 	    s.m_mutex.unlock(); | 
| Chris@43 | 1741 	    s.m_mutex.lock(); | 
| Chris@43 | 1742 | 
| Chris@43 | 1743 	} else { | 
| Chris@43 | 1744 | 
| Chris@43 | 1745 	    float ms = 100; | 
| Chris@43 | 1746 	    if (s.getSourceSampleRate() > 0) { | 
| Chris@43 | 1747 		ms = float(m_ringBufferSize) / float(s.getSourceSampleRate()) * 1000.0; | 
| Chris@43 | 1748 	    } | 
| Chris@43 | 1749 | 
| Chris@43 | 1750 	    if (s.m_playing) ms /= 10; | 
| Chris@43 | 1751 | 
| Chris@43 | 1752 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1753             if (!s.m_playing) std::cout << std::endl; | 
| Chris@43 | 1754 	    std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms << "ms..." << std::endl; | 
| Chris@43 | 1755 #endif | 
| Chris@43 | 1756 | 
| Chris@43 | 1757 	    s.m_condition.wait(&s.m_mutex, size_t(ms)); | 
| Chris@43 | 1758 	} | 
| Chris@43 | 1759 | 
| Chris@43 | 1760 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1761 	std::cout << "AudioCallbackPlaySourceFillThread: awoken" << std::endl; | 
| Chris@43 | 1762 #endif | 
| Chris@43 | 1763 | 
| Chris@43 | 1764 	work = false; | 
| Chris@43 | 1765 | 
| Chris@103 | 1766 	if (!s.getSourceSampleRate()) { | 
| Chris@103 | 1767 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@103 | 1768             std::cout << "AudioCallbackPlaySourceFillThread: source sample rate is zero" << std::endl; | 
| Chris@103 | 1769 #endif | 
| Chris@103 | 1770             continue; | 
| Chris@103 | 1771         } | 
| Chris@43 | 1772 | 
| Chris@43 | 1773 	bool playing = s.m_playing; | 
| Chris@43 | 1774 | 
| Chris@43 | 1775 	if (playing && !previouslyPlaying) { | 
| Chris@43 | 1776 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1777 	    std::cout << "AudioCallbackPlaySourceFillThread: playback state changed, resetting" << std::endl; | 
| Chris@43 | 1778 #endif | 
| Chris@43 | 1779 	    for (size_t c = 0; c < s.getTargetChannelCount(); ++c) { | 
| Chris@43 | 1780 		RingBuffer<float> *rb = s.getReadRingBuffer(c); | 
| Chris@43 | 1781 		if (rb) rb->reset(); | 
| Chris@43 | 1782 	    } | 
| Chris@43 | 1783 	} | 
| Chris@43 | 1784 	previouslyPlaying = playing; | 
| Chris@43 | 1785 | 
| Chris@43 | 1786 	work = s.fillBuffers(); | 
| Chris@43 | 1787     } | 
| Chris@43 | 1788 | 
| Chris@43 | 1789     s.m_mutex.unlock(); | 
| Chris@43 | 1790 } | 
| Chris@43 | 1791 |