| 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@43 | 21 #include "view/ViewManager.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@43 | 28 #include "PhaseVocoderTimeStretcher.h" | 
| Chris@43 | 29 | 
| Chris@43 | 30 #include <iostream> | 
| Chris@43 | 31 #include <cassert> | 
| Chris@43 | 32 | 
| Chris@43 | 33 //#define DEBUG_AUDIO_PLAY_SOURCE 1 | 
| Chris@43 | 34 //#define DEBUG_AUDIO_PLAY_SOURCE_PLAYING 1 | 
| Chris@43 | 35 | 
| Chris@43 | 36 const size_t AudioCallbackPlaySource::m_ringBufferSize = 131071; | 
| Chris@43 | 37 | 
| Chris@57 | 38 AudioCallbackPlaySource::AudioCallbackPlaySource(ViewManager *manager, | 
| Chris@57 | 39                                                  QString clientName) : | 
| Chris@43 | 40     m_viewManager(manager), | 
| Chris@43 | 41     m_audioGenerator(new AudioGenerator()), | 
| Chris@57 | 42     m_clientName(clientName), | 
| Chris@43 | 43     m_readBuffers(0), | 
| Chris@43 | 44     m_writeBuffers(0), | 
| Chris@43 | 45     m_readBufferFill(0), | 
| Chris@43 | 46     m_writeBufferFill(0), | 
| Chris@43 | 47     m_bufferScavenger(1), | 
| Chris@43 | 48     m_sourceChannelCount(0), | 
| Chris@43 | 49     m_blockSize(1024), | 
| Chris@43 | 50     m_sourceSampleRate(0), | 
| Chris@43 | 51     m_targetSampleRate(0), | 
| Chris@43 | 52     m_playLatency(0), | 
| Chris@43 | 53     m_playing(false), | 
| Chris@43 | 54     m_exiting(false), | 
| Chris@43 | 55     m_lastModelEndFrame(0), | 
| Chris@43 | 56     m_outputLeft(0.0), | 
| Chris@43 | 57     m_outputRight(0.0), | 
| Chris@43 | 58     m_auditioningPlugin(0), | 
| Chris@43 | 59     m_auditioningPluginBypassed(false), | 
| Chris@43 | 60     m_timeStretcher(0), | 
| Chris@43 | 61     m_fillThread(0), | 
| Chris@43 | 62     m_converter(0), | 
| Chris@43 | 63     m_crapConverter(0), | 
| Chris@43 | 64     m_resampleQuality(Preferences::getInstance()->getResampleQuality()) | 
| Chris@43 | 65 { | 
| Chris@43 | 66     m_viewManager->setAudioPlaySource(this); | 
| Chris@43 | 67 | 
| Chris@43 | 68     connect(m_viewManager, SIGNAL(selectionChanged()), | 
| Chris@43 | 69 	    this, SLOT(selectionChanged())); | 
| Chris@43 | 70     connect(m_viewManager, SIGNAL(playLoopModeChanged()), | 
| Chris@43 | 71 	    this, SLOT(playLoopModeChanged())); | 
| Chris@43 | 72     connect(m_viewManager, SIGNAL(playSelectionModeChanged()), | 
| Chris@43 | 73 	    this, SLOT(playSelectionModeChanged())); | 
| Chris@43 | 74 | 
| Chris@43 | 75     connect(PlayParameterRepository::getInstance(), | 
| Chris@43 | 76 	    SIGNAL(playParametersChanged(PlayParameters *)), | 
| Chris@43 | 77 	    this, SLOT(playParametersChanged(PlayParameters *))); | 
| Chris@43 | 78 | 
| Chris@43 | 79     connect(Preferences::getInstance(), | 
| Chris@43 | 80             SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | 
| Chris@43 | 81             this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); | 
| Chris@43 | 82 } | 
| Chris@43 | 83 | 
| Chris@43 | 84 AudioCallbackPlaySource::~AudioCallbackPlaySource() | 
| Chris@43 | 85 { | 
| Chris@43 | 86     m_exiting = true; | 
| Chris@43 | 87 | 
| Chris@43 | 88     if (m_fillThread) { | 
| Chris@43 | 89 	m_condition.wakeAll(); | 
| Chris@43 | 90 	m_fillThread->wait(); | 
| Chris@43 | 91 	delete m_fillThread; | 
| Chris@43 | 92     } | 
| Chris@43 | 93 | 
| Chris@43 | 94     clearModels(); | 
| Chris@43 | 95 | 
| Chris@43 | 96     if (m_readBuffers != m_writeBuffers) { | 
| Chris@43 | 97 	delete m_readBuffers; | 
| Chris@43 | 98     } | 
| Chris@43 | 99 | 
| Chris@43 | 100     delete m_writeBuffers; | 
| Chris@43 | 101 | 
| Chris@43 | 102     delete m_audioGenerator; | 
| Chris@43 | 103 | 
| Chris@43 | 104     m_bufferScavenger.scavenge(true); | 
| Chris@43 | 105     m_pluginScavenger.scavenge(true); | 
| Chris@43 | 106     m_timeStretcherScavenger.scavenge(true); | 
| Chris@43 | 107 } | 
| Chris@43 | 108 | 
| Chris@43 | 109 void | 
| Chris@43 | 110 AudioCallbackPlaySource::addModel(Model *model) | 
| Chris@43 | 111 { | 
| Chris@43 | 112     if (m_models.find(model) != m_models.end()) return; | 
| Chris@43 | 113 | 
| Chris@43 | 114     bool canPlay = m_audioGenerator->addModel(model); | 
| Chris@43 | 115 | 
| Chris@43 | 116     m_mutex.lock(); | 
| Chris@43 | 117 | 
| Chris@43 | 118     m_models.insert(model); | 
| Chris@43 | 119     if (model->getEndFrame() > m_lastModelEndFrame) { | 
| Chris@43 | 120 	m_lastModelEndFrame = model->getEndFrame(); | 
| Chris@43 | 121     } | 
| Chris@43 | 122 | 
| Chris@43 | 123     bool buffersChanged = false, srChanged = false; | 
| Chris@43 | 124 | 
| Chris@43 | 125     size_t modelChannels = 1; | 
| Chris@43 | 126     DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); | 
| Chris@43 | 127     if (dtvm) modelChannels = dtvm->getChannelCount(); | 
| Chris@43 | 128     if (modelChannels > m_sourceChannelCount) { | 
| Chris@43 | 129 	m_sourceChannelCount = modelChannels; | 
| Chris@43 | 130     } | 
| Chris@43 | 131 | 
| Chris@43 | 132 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 133     std::cout << "Adding model with " << modelChannels << " channels " << std::endl; | 
| Chris@43 | 134 #endif | 
| Chris@43 | 135 | 
| Chris@43 | 136     if (m_sourceSampleRate == 0) { | 
| Chris@43 | 137 | 
| Chris@43 | 138 	m_sourceSampleRate = model->getSampleRate(); | 
| Chris@43 | 139 	srChanged = true; | 
| Chris@43 | 140 | 
| Chris@43 | 141     } else if (model->getSampleRate() != m_sourceSampleRate) { | 
| Chris@43 | 142 | 
| Chris@43 | 143         // If this is a dense time-value model and we have no other, we | 
| Chris@43 | 144         // can just switch to this model's sample rate | 
| Chris@43 | 145 | 
| Chris@43 | 146         if (dtvm) { | 
| Chris@43 | 147 | 
| Chris@43 | 148             bool conflicting = false; | 
| Chris@43 | 149 | 
| Chris@43 | 150             for (std::set<Model *>::const_iterator i = m_models.begin(); | 
| Chris@43 | 151                  i != m_models.end(); ++i) { | 
| Chris@43 | 152                 // Only wave file models can be considered conflicting -- | 
| Chris@43 | 153                 // writable wave file models are derived and we shouldn't | 
| Chris@43 | 154                 // take their rates into account.  Also, don't give any | 
| Chris@43 | 155                 // particular weight to a file that's already playing at | 
| Chris@43 | 156                 // the wrong rate anyway | 
| Chris@43 | 157                 WaveFileModel *wfm = dynamic_cast<WaveFileModel *>(*i); | 
| Chris@43 | 158                 if (wfm && wfm != dtvm && | 
| Chris@43 | 159                     wfm->getSampleRate() != model->getSampleRate() && | 
| Chris@43 | 160                     wfm->getSampleRate() == m_sourceSampleRate) { | 
| Chris@43 | 161                     std::cerr << "AudioCallbackPlaySource::addModel: Conflicting wave file model " << *i << " found" << std::endl; | 
| Chris@43 | 162                     conflicting = true; | 
| Chris@43 | 163                     break; | 
| Chris@43 | 164                 } | 
| Chris@43 | 165             } | 
| Chris@43 | 166 | 
| Chris@43 | 167             if (conflicting) { | 
| Chris@43 | 168 | 
| Chris@43 | 169                 std::cerr << "AudioCallbackPlaySource::addModel: ERROR: " | 
| Chris@43 | 170                           << "New model sample rate does not match" << std::endl | 
| Chris@43 | 171                           << "existing model(s) (new " << model->getSampleRate() | 
| Chris@43 | 172                           << " vs " << m_sourceSampleRate | 
| Chris@43 | 173                           << "), playback will be wrong" | 
| Chris@43 | 174                           << std::endl; | 
| Chris@43 | 175 | 
| Chris@43 | 176                 emit sampleRateMismatch(model->getSampleRate(), | 
| Chris@43 | 177                                         m_sourceSampleRate, | 
| Chris@43 | 178                                         false); | 
| Chris@43 | 179             } else { | 
| Chris@43 | 180                 m_sourceSampleRate = model->getSampleRate(); | 
| Chris@43 | 181                 srChanged = true; | 
| Chris@43 | 182             } | 
| Chris@43 | 183         } | 
| Chris@43 | 184     } | 
| Chris@43 | 185 | 
| Chris@43 | 186     if (!m_writeBuffers || (m_writeBuffers->size() < getTargetChannelCount())) { | 
| Chris@43 | 187 	clearRingBuffers(true, getTargetChannelCount()); | 
| Chris@43 | 188 	buffersChanged = true; | 
| Chris@43 | 189     } else { | 
| Chris@43 | 190 	if (canPlay) clearRingBuffers(true); | 
| Chris@43 | 191     } | 
| Chris@43 | 192 | 
| Chris@43 | 193     if (buffersChanged || srChanged) { | 
| Chris@43 | 194 	if (m_converter) { | 
| Chris@43 | 195 	    src_delete(m_converter); | 
| Chris@43 | 196             src_delete(m_crapConverter); | 
| Chris@43 | 197 	    m_converter = 0; | 
| Chris@43 | 198             m_crapConverter = 0; | 
| Chris@43 | 199 	} | 
| Chris@43 | 200     } | 
| Chris@43 | 201 | 
| Chris@43 | 202     m_mutex.unlock(); | 
| Chris@43 | 203 | 
| Chris@43 | 204     m_audioGenerator->setTargetChannelCount(getTargetChannelCount()); | 
| Chris@43 | 205 | 
| Chris@43 | 206     if (!m_fillThread) { | 
| Chris@43 | 207 	m_fillThread = new FillThread(*this); | 
| Chris@43 | 208 	m_fillThread->start(); | 
| Chris@43 | 209     } | 
| Chris@43 | 210 | 
| Chris@43 | 211 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 212     std::cout << "AudioCallbackPlaySource::addModel: now have " << m_models.size() << " model(s) -- emitting modelReplaced" << std::endl; | 
| Chris@43 | 213 #endif | 
| Chris@43 | 214 | 
| Chris@43 | 215     if (buffersChanged || srChanged) { | 
| Chris@43 | 216 	emit modelReplaced(); | 
| Chris@43 | 217     } | 
| Chris@43 | 218 | 
| Chris@43 | 219     connect(model, SIGNAL(modelChanged(size_t, size_t)), | 
| Chris@43 | 220             this, SLOT(modelChanged(size_t, size_t))); | 
| Chris@43 | 221 | 
| Chris@43 | 222     m_condition.wakeAll(); | 
| Chris@43 | 223 } | 
| Chris@43 | 224 | 
| Chris@43 | 225 void | 
| Chris@43 | 226 AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame) | 
| Chris@43 | 227 { | 
| Chris@43 | 228 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 229     std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl; | 
| Chris@43 | 230 #endif | 
| Chris@43 | 231     if (endFrame > m_lastModelEndFrame) m_lastModelEndFrame = endFrame; | 
| Chris@43 | 232 } | 
| Chris@43 | 233 | 
| Chris@43 | 234 void | 
| Chris@43 | 235 AudioCallbackPlaySource::removeModel(Model *model) | 
| Chris@43 | 236 { | 
| Chris@43 | 237     m_mutex.lock(); | 
| Chris@43 | 238 | 
| Chris@43 | 239 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 240     std::cout << "AudioCallbackPlaySource::removeModel(" << model << ")" << std::endl; | 
| Chris@43 | 241 #endif | 
| Chris@43 | 242 | 
| Chris@43 | 243     disconnect(model, SIGNAL(modelChanged(size_t, size_t)), | 
| Chris@43 | 244                this, SLOT(modelChanged(size_t, size_t))); | 
| Chris@43 | 245 | 
| Chris@43 | 246     m_models.erase(model); | 
| Chris@43 | 247 | 
| Chris@43 | 248     if (m_models.empty()) { | 
| Chris@43 | 249 	if (m_converter) { | 
| Chris@43 | 250 	    src_delete(m_converter); | 
| Chris@43 | 251             src_delete(m_crapConverter); | 
| Chris@43 | 252 	    m_converter = 0; | 
| Chris@43 | 253             m_crapConverter = 0; | 
| Chris@43 | 254 	} | 
| Chris@43 | 255 	m_sourceSampleRate = 0; | 
| Chris@43 | 256     } | 
| Chris@43 | 257 | 
| Chris@43 | 258     size_t lastEnd = 0; | 
| Chris@43 | 259     for (std::set<Model *>::const_iterator i = m_models.begin(); | 
| Chris@43 | 260 	 i != m_models.end(); ++i) { | 
| Chris@43 | 261 //	std::cout << "AudioCallbackPlaySource::removeModel(" << model << "): checking end frame on model " << *i << std::endl; | 
| Chris@43 | 262 	if ((*i)->getEndFrame() > lastEnd) lastEnd = (*i)->getEndFrame(); | 
| Chris@43 | 263 //	std::cout << "(done, lastEnd now " << lastEnd << ")" << std::endl; | 
| Chris@43 | 264     } | 
| Chris@43 | 265     m_lastModelEndFrame = lastEnd; | 
| Chris@43 | 266 | 
| Chris@43 | 267     m_mutex.unlock(); | 
| Chris@43 | 268 | 
| Chris@43 | 269     m_audioGenerator->removeModel(model); | 
| Chris@43 | 270 | 
| Chris@43 | 271     clearRingBuffers(); | 
| Chris@43 | 272 } | 
| Chris@43 | 273 | 
| Chris@43 | 274 void | 
| Chris@43 | 275 AudioCallbackPlaySource::clearModels() | 
| Chris@43 | 276 { | 
| Chris@43 | 277     m_mutex.lock(); | 
| Chris@43 | 278 | 
| Chris@43 | 279 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 280     std::cout << "AudioCallbackPlaySource::clearModels()" << std::endl; | 
| Chris@43 | 281 #endif | 
| Chris@43 | 282 | 
| Chris@43 | 283     m_models.clear(); | 
| Chris@43 | 284 | 
| Chris@43 | 285     if (m_converter) { | 
| Chris@43 | 286 	src_delete(m_converter); | 
| Chris@43 | 287         src_delete(m_crapConverter); | 
| Chris@43 | 288 	m_converter = 0; | 
| Chris@43 | 289         m_crapConverter = 0; | 
| Chris@43 | 290     } | 
| Chris@43 | 291 | 
| Chris@43 | 292     m_lastModelEndFrame = 0; | 
| Chris@43 | 293 | 
| Chris@43 | 294     m_sourceSampleRate = 0; | 
| Chris@43 | 295 | 
| Chris@43 | 296     m_mutex.unlock(); | 
| Chris@43 | 297 | 
| Chris@43 | 298     m_audioGenerator->clearModels(); | 
| Chris@43 | 299 } | 
| Chris@43 | 300 | 
| Chris@43 | 301 void | 
| Chris@43 | 302 AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count) | 
| Chris@43 | 303 { | 
| Chris@43 | 304     if (!haveLock) m_mutex.lock(); | 
| Chris@43 | 305 | 
| Chris@43 | 306     if (count == 0) { | 
| Chris@43 | 307 	if (m_writeBuffers) count = m_writeBuffers->size(); | 
| Chris@43 | 308     } | 
| Chris@43 | 309 | 
| Chris@43 | 310     size_t sf = m_readBufferFill; | 
| Chris@43 | 311     RingBuffer<float> *rb = getReadRingBuffer(0); | 
| Chris@43 | 312     if (rb) { | 
| Chris@43 | 313 	//!!! This is incorrect if we're in a non-contiguous selection | 
| Chris@43 | 314 	//Same goes for all related code (subtracting the read space | 
| Chris@43 | 315 	//from the fill frame to try to establish where the effective | 
| Chris@43 | 316 	//pre-resample/timestretch read pointer is) | 
| Chris@43 | 317 	size_t rs = rb->getReadSpace(); | 
| Chris@43 | 318 	if (rs < sf) sf -= rs; | 
| Chris@43 | 319 	else sf = 0; | 
| Chris@43 | 320     } | 
| Chris@43 | 321     m_writeBufferFill = sf; | 
| Chris@43 | 322 | 
| Chris@43 | 323     if (m_readBuffers != m_writeBuffers) { | 
| Chris@43 | 324 	delete m_writeBuffers; | 
| Chris@43 | 325     } | 
| Chris@43 | 326 | 
| Chris@43 | 327     m_writeBuffers = new RingBufferVector; | 
| Chris@43 | 328 | 
| Chris@43 | 329     for (size_t i = 0; i < count; ++i) { | 
| Chris@43 | 330 	m_writeBuffers->push_back(new RingBuffer<float>(m_ringBufferSize)); | 
| Chris@43 | 331     } | 
| Chris@43 | 332 | 
| Chris@43 | 333 //    std::cout << "AudioCallbackPlaySource::clearRingBuffers: Created " | 
| Chris@43 | 334 //	      << count << " write buffers" << std::endl; | 
| Chris@43 | 335 | 
| Chris@43 | 336     if (!haveLock) { | 
| Chris@43 | 337 	m_mutex.unlock(); | 
| Chris@43 | 338     } | 
| Chris@43 | 339 } | 
| Chris@43 | 340 | 
| Chris@43 | 341 void | 
| Chris@43 | 342 AudioCallbackPlaySource::play(size_t startFrame) | 
| Chris@43 | 343 { | 
| Chris@43 | 344     if (m_viewManager->getPlaySelectionMode() && | 
| Chris@43 | 345 	!m_viewManager->getSelections().empty()) { | 
| Chris@60 | 346 | 
| Chris@60 | 347         startFrame = m_viewManager->constrainFrameToSelection(startFrame); | 
| Chris@60 | 348 | 
| Chris@43 | 349     } else { | 
| Chris@43 | 350 	if (startFrame >= m_lastModelEndFrame) { | 
| Chris@43 | 351 	    startFrame = 0; | 
| Chris@43 | 352 	} | 
| Chris@43 | 353     } | 
| Chris@43 | 354 | 
| Chris@60 | 355     std::cerr << "play(" << startFrame << ") -> playback model "; | 
| Chris@60 | 356 | 
| Chris@60 | 357     startFrame = m_viewManager->alignReferenceToPlaybackFrame(startFrame); | 
| Chris@60 | 358 | 
| Chris@60 | 359     std::cerr << startFrame << std::endl; | 
| Chris@60 | 360 | 
| Chris@43 | 361     // The fill thread will automatically empty its buffers before | 
| Chris@43 | 362     // starting again if we have not so far been playing, but not if | 
| Chris@43 | 363     // we're just re-seeking. | 
| Chris@43 | 364 | 
| Chris@43 | 365     m_mutex.lock(); | 
| Chris@43 | 366     if (m_playing) { | 
| Chris@43 | 367 	m_readBufferFill = m_writeBufferFill = startFrame; | 
| Chris@43 | 368 	if (m_readBuffers) { | 
| Chris@43 | 369 	    for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 370 		RingBuffer<float> *rb = getReadRingBuffer(c); | 
| Chris@43 | 371 		if (rb) rb->reset(); | 
| Chris@43 | 372 	    } | 
| Chris@43 | 373 	} | 
| Chris@43 | 374 	if (m_converter) src_reset(m_converter); | 
| Chris@43 | 375         if (m_crapConverter) src_reset(m_crapConverter); | 
| Chris@43 | 376     } else { | 
| Chris@43 | 377 	if (m_converter) src_reset(m_converter); | 
| Chris@43 | 378         if (m_crapConverter) src_reset(m_crapConverter); | 
| Chris@43 | 379 	m_readBufferFill = m_writeBufferFill = startFrame; | 
| Chris@43 | 380     } | 
| Chris@43 | 381     m_mutex.unlock(); | 
| Chris@43 | 382 | 
| Chris@43 | 383     m_audioGenerator->reset(); | 
| Chris@43 | 384 | 
| Chris@43 | 385     bool changed = !m_playing; | 
| Chris@43 | 386     m_playing = true; | 
| Chris@43 | 387     m_condition.wakeAll(); | 
| Chris@43 | 388     if (changed) emit playStatusChanged(m_playing); | 
| Chris@43 | 389 } | 
| Chris@43 | 390 | 
| Chris@43 | 391 void | 
| Chris@43 | 392 AudioCallbackPlaySource::stop() | 
| Chris@43 | 393 { | 
| Chris@43 | 394     bool changed = m_playing; | 
| Chris@43 | 395     m_playing = false; | 
| Chris@43 | 396     m_condition.wakeAll(); | 
| Chris@43 | 397     if (changed) emit playStatusChanged(m_playing); | 
| Chris@43 | 398 } | 
| Chris@43 | 399 | 
| Chris@43 | 400 void | 
| Chris@43 | 401 AudioCallbackPlaySource::selectionChanged() | 
| Chris@43 | 402 { | 
| Chris@43 | 403     if (m_viewManager->getPlaySelectionMode()) { | 
| Chris@43 | 404 	clearRingBuffers(); | 
| Chris@43 | 405     } | 
| Chris@43 | 406 } | 
| Chris@43 | 407 | 
| Chris@43 | 408 void | 
| Chris@43 | 409 AudioCallbackPlaySource::playLoopModeChanged() | 
| Chris@43 | 410 { | 
| Chris@43 | 411     clearRingBuffers(); | 
| Chris@43 | 412 } | 
| Chris@43 | 413 | 
| Chris@43 | 414 void | 
| Chris@43 | 415 AudioCallbackPlaySource::playSelectionModeChanged() | 
| Chris@43 | 416 { | 
| Chris@43 | 417     if (!m_viewManager->getSelections().empty()) { | 
| Chris@43 | 418 	clearRingBuffers(); | 
| Chris@43 | 419     } | 
| Chris@43 | 420 } | 
| Chris@43 | 421 | 
| Chris@43 | 422 void | 
| Chris@43 | 423 AudioCallbackPlaySource::playParametersChanged(PlayParameters *) | 
| Chris@43 | 424 { | 
| Chris@43 | 425     clearRingBuffers(); | 
| Chris@43 | 426 } | 
| Chris@43 | 427 | 
| Chris@43 | 428 void | 
| Chris@43 | 429 AudioCallbackPlaySource::preferenceChanged(PropertyContainer::PropertyName n) | 
| Chris@43 | 430 { | 
| Chris@43 | 431     if (n == "Resample Quality") { | 
| Chris@43 | 432         setResampleQuality(Preferences::getInstance()->getResampleQuality()); | 
| Chris@43 | 433     } | 
| Chris@43 | 434 } | 
| Chris@43 | 435 | 
| Chris@43 | 436 void | 
| Chris@43 | 437 AudioCallbackPlaySource::audioProcessingOverload() | 
| Chris@43 | 438 { | 
| Chris@43 | 439     RealTimePluginInstance *ap = m_auditioningPlugin; | 
| Chris@43 | 440     if (ap && m_playing && !m_auditioningPluginBypassed) { | 
| Chris@43 | 441         m_auditioningPluginBypassed = true; | 
| Chris@43 | 442         emit audioOverloadPluginDisabled(); | 
| Chris@43 | 443     } | 
| Chris@43 | 444 } | 
| Chris@43 | 445 | 
| Chris@43 | 446 void | 
| Chris@43 | 447 AudioCallbackPlaySource::setTargetBlockSize(size_t size) | 
| Chris@43 | 448 { | 
| Chris@43 | 449 //    std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; | 
| Chris@43 | 450     assert(size < m_ringBufferSize); | 
| Chris@43 | 451     m_blockSize = size; | 
| Chris@43 | 452 } | 
| Chris@43 | 453 | 
| Chris@43 | 454 size_t | 
| Chris@43 | 455 AudioCallbackPlaySource::getTargetBlockSize() const | 
| Chris@43 | 456 { | 
| Chris@43 | 457 //    std::cout << "AudioCallbackPlaySource::getTargetBlockSize() -> " << m_blockSize << std::endl; | 
| Chris@43 | 458     return m_blockSize; | 
| Chris@43 | 459 } | 
| Chris@43 | 460 | 
| Chris@43 | 461 void | 
| Chris@43 | 462 AudioCallbackPlaySource::setTargetPlayLatency(size_t latency) | 
| Chris@43 | 463 { | 
| Chris@43 | 464     m_playLatency = latency; | 
| Chris@43 | 465 } | 
| Chris@43 | 466 | 
| Chris@43 | 467 size_t | 
| Chris@43 | 468 AudioCallbackPlaySource::getTargetPlayLatency() const | 
| Chris@43 | 469 { | 
| Chris@43 | 470     return m_playLatency; | 
| Chris@43 | 471 } | 
| Chris@43 | 472 | 
| Chris@43 | 473 size_t | 
| Chris@43 | 474 AudioCallbackPlaySource::getCurrentPlayingFrame() | 
| Chris@43 | 475 { | 
| Chris@43 | 476     bool resample = false; | 
| Chris@43 | 477     double ratio = 1.0; | 
| Chris@43 | 478 | 
| Chris@43 | 479     if (getSourceSampleRate() != getTargetSampleRate()) { | 
| Chris@43 | 480 	resample = true; | 
| Chris@43 | 481 	ratio = double(getSourceSampleRate()) / double(getTargetSampleRate()); | 
| Chris@43 | 482     } | 
| Chris@43 | 483 | 
| Chris@43 | 484     size_t readSpace = 0; | 
| Chris@43 | 485     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 486 	RingBuffer<float> *rb = getReadRingBuffer(c); | 
| Chris@43 | 487 	if (rb) { | 
| Chris@43 | 488 	    size_t spaceHere = rb->getReadSpace(); | 
| Chris@43 | 489 	    if (c == 0 || spaceHere < readSpace) readSpace = spaceHere; | 
| Chris@43 | 490 	} | 
| Chris@43 | 491     } | 
| Chris@43 | 492 | 
| Chris@43 | 493     if (resample) { | 
| Chris@43 | 494 	readSpace = size_t(readSpace * ratio + 0.1); | 
| Chris@43 | 495     } | 
| Chris@43 | 496 | 
| Chris@43 | 497     size_t latency = m_playLatency; | 
| Chris@43 | 498     if (resample) latency = size_t(m_playLatency * ratio + 0.1); | 
| Chris@43 | 499 | 
| Chris@43 | 500     PhaseVocoderTimeStretcher *timeStretcher = m_timeStretcher; | 
| Chris@43 | 501     if (timeStretcher) { | 
| Chris@43 | 502 	latency += timeStretcher->getProcessingLatency(); | 
| Chris@43 | 503     } | 
| Chris@43 | 504 | 
| Chris@43 | 505     latency += readSpace; | 
| Chris@43 | 506     size_t bufferedFrame = m_readBufferFill; | 
| Chris@43 | 507 | 
| Chris@43 | 508     bool looping = m_viewManager->getPlayLoopMode(); | 
| Chris@43 | 509     bool constrained = (m_viewManager->getPlaySelectionMode() && | 
| Chris@43 | 510 			!m_viewManager->getSelections().empty()); | 
| Chris@43 | 511 | 
| Chris@43 | 512     size_t framePlaying = bufferedFrame; | 
| Chris@43 | 513 | 
| Chris@43 | 514     if (looping && !constrained) { | 
| Chris@43 | 515 	while (framePlaying < latency) framePlaying += m_lastModelEndFrame; | 
| Chris@43 | 516     } | 
| Chris@43 | 517 | 
| Chris@43 | 518     if (framePlaying > latency) framePlaying -= latency; | 
| Chris@43 | 519     else framePlaying = 0; | 
| Chris@43 | 520 | 
| Chris@60 | 521 //    std::cerr << "framePlaying = " << framePlaying << " -> reference "; | 
| Chris@60 | 522 | 
| Chris@60 | 523     framePlaying = m_viewManager->alignPlaybackFrameToReference(framePlaying); | 
| Chris@60 | 524 | 
| Chris@60 | 525 //    std::cerr << framePlaying << std::endl; | 
| Chris@60 | 526 | 
| Chris@43 | 527     if (!constrained) { | 
| Chris@43 | 528 	if (!looping && framePlaying > m_lastModelEndFrame) { | 
| Chris@43 | 529 	    framePlaying = m_lastModelEndFrame; | 
| Chris@43 | 530 	    stop(); | 
| Chris@43 | 531 	} | 
| Chris@43 | 532 	return framePlaying; | 
| Chris@43 | 533     } | 
| Chris@43 | 534 | 
| Chris@60 | 535     bufferedFrame = m_viewManager->alignPlaybackFrameToReference(bufferedFrame); | 
| Chris@60 | 536 | 
| Chris@43 | 537     MultiSelection::SelectionList selections = m_viewManager->getSelections(); | 
| Chris@43 | 538     MultiSelection::SelectionList::const_iterator i; | 
| Chris@43 | 539 | 
| Chris@43 | 540 //    i = selections.begin(); | 
| Chris@43 | 541 //    size_t rangeStart = i->getStartFrame(); | 
| Chris@43 | 542 | 
| Chris@43 | 543     i = selections.end(); | 
| Chris@43 | 544     --i; | 
| Chris@43 | 545     size_t rangeEnd = i->getEndFrame(); | 
| Chris@43 | 546 | 
| Chris@43 | 547     for (i = selections.begin(); i != selections.end(); ++i) { | 
| Chris@43 | 548 	if (i->contains(bufferedFrame)) break; | 
| Chris@43 | 549     } | 
| Chris@43 | 550 | 
| Chris@43 | 551     size_t f = bufferedFrame; | 
| Chris@43 | 552 | 
| Chris@43 | 553 //    std::cout << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << ", rangeEnd=" << rangeEnd << std::endl; | 
| Chris@43 | 554 | 
| Chris@43 | 555     if (i == selections.end()) { | 
| Chris@43 | 556 	--i; | 
| Chris@43 | 557 	if (i->getEndFrame() + latency < f) { | 
| Chris@43 | 558 //    std::cout << "framePlaying = " << framePlaying << ", rangeEnd = " << rangeEnd << std::endl; | 
| Chris@43 | 559 | 
| Chris@43 | 560 	    if (!looping && (framePlaying > rangeEnd)) { | 
| Chris@43 | 561 //		std::cout << "STOPPING" << std::endl; | 
| Chris@43 | 562 		stop(); | 
| Chris@43 | 563 		return rangeEnd; | 
| Chris@43 | 564 	    } else { | 
| Chris@43 | 565 		return framePlaying; | 
| Chris@43 | 566 	    } | 
| Chris@43 | 567 	} else { | 
| Chris@43 | 568 //	    std::cout << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; | 
| Chris@43 | 569 	    latency -= (f - i->getEndFrame()); | 
| Chris@43 | 570 	    f = i->getEndFrame(); | 
| Chris@43 | 571 	} | 
| Chris@43 | 572     } | 
| Chris@43 | 573 | 
| Chris@43 | 574 //    std::cout << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; | 
| Chris@43 | 575 | 
| Chris@43 | 576     while (latency > 0) { | 
| Chris@43 | 577 	size_t offset = f - i->getStartFrame(); | 
| Chris@43 | 578 	if (offset >= latency) { | 
| Chris@43 | 579 	    if (f > latency) { | 
| Chris@43 | 580 		framePlaying = f - latency; | 
| Chris@43 | 581 	    } else { | 
| Chris@43 | 582 		framePlaying = 0; | 
| Chris@43 | 583 	    } | 
| Chris@43 | 584 	    break; | 
| Chris@43 | 585 	} else { | 
| Chris@43 | 586 	    if (i == selections.begin()) { | 
| Chris@43 | 587 		if (looping) { | 
| Chris@43 | 588 		    i = selections.end(); | 
| Chris@43 | 589 		} | 
| Chris@43 | 590 	    } | 
| Chris@43 | 591 	    latency -= offset; | 
| Chris@43 | 592 	    --i; | 
| Chris@43 | 593 	    f = i->getEndFrame(); | 
| Chris@43 | 594 	} | 
| Chris@43 | 595     } | 
| Chris@43 | 596 | 
| Chris@43 | 597     return framePlaying; | 
| Chris@43 | 598 } | 
| Chris@43 | 599 | 
| Chris@43 | 600 void | 
| Chris@43 | 601 AudioCallbackPlaySource::setOutputLevels(float left, float right) | 
| Chris@43 | 602 { | 
| Chris@43 | 603     m_outputLeft = left; | 
| Chris@43 | 604     m_outputRight = right; | 
| Chris@43 | 605 } | 
| Chris@43 | 606 | 
| Chris@43 | 607 bool | 
| Chris@43 | 608 AudioCallbackPlaySource::getOutputLevels(float &left, float &right) | 
| Chris@43 | 609 { | 
| Chris@43 | 610     left = m_outputLeft; | 
| Chris@43 | 611     right = m_outputRight; | 
| Chris@43 | 612     return true; | 
| Chris@43 | 613 } | 
| Chris@43 | 614 | 
| Chris@43 | 615 void | 
| Chris@43 | 616 AudioCallbackPlaySource::setTargetSampleRate(size_t sr) | 
| Chris@43 | 617 { | 
| Chris@43 | 618     m_targetSampleRate = sr; | 
| Chris@43 | 619     initialiseConverter(); | 
| Chris@43 | 620 } | 
| Chris@43 | 621 | 
| Chris@43 | 622 void | 
| Chris@43 | 623 AudioCallbackPlaySource::initialiseConverter() | 
| Chris@43 | 624 { | 
| Chris@43 | 625     m_mutex.lock(); | 
| Chris@43 | 626 | 
| Chris@43 | 627     if (m_converter) { | 
| Chris@43 | 628         src_delete(m_converter); | 
| Chris@43 | 629         src_delete(m_crapConverter); | 
| Chris@43 | 630         m_converter = 0; | 
| Chris@43 | 631         m_crapConverter = 0; | 
| Chris@43 | 632     } | 
| Chris@43 | 633 | 
| Chris@43 | 634     if (getSourceSampleRate() != getTargetSampleRate()) { | 
| Chris@43 | 635 | 
| Chris@43 | 636 	int err = 0; | 
| Chris@43 | 637 | 
| Chris@43 | 638 	m_converter = src_new(m_resampleQuality == 2 ? SRC_SINC_BEST_QUALITY : | 
| Chris@43 | 639                               m_resampleQuality == 1 ? SRC_SINC_MEDIUM_QUALITY : | 
| Chris@43 | 640                               m_resampleQuality == 0 ? SRC_SINC_FASTEST : | 
| Chris@43 | 641                                                        SRC_SINC_MEDIUM_QUALITY, | 
| Chris@43 | 642 			      getTargetChannelCount(), &err); | 
| Chris@43 | 643 | 
| Chris@43 | 644         if (m_converter) { | 
| Chris@43 | 645             m_crapConverter = src_new(SRC_LINEAR, | 
| Chris@43 | 646                                       getTargetChannelCount(), | 
| Chris@43 | 647                                       &err); | 
| Chris@43 | 648         } | 
| Chris@43 | 649 | 
| Chris@43 | 650 	if (!m_converter || !m_crapConverter) { | 
| Chris@43 | 651 	    std::cerr | 
| Chris@43 | 652 		<< "AudioCallbackPlaySource::setModel: ERROR in creating samplerate converter: " | 
| Chris@43 | 653 		<< src_strerror(err) << std::endl; | 
| Chris@43 | 654 | 
| Chris@43 | 655             if (m_converter) { | 
| Chris@43 | 656                 src_delete(m_converter); | 
| Chris@43 | 657                 m_converter = 0; | 
| Chris@43 | 658             } | 
| Chris@43 | 659 | 
| Chris@43 | 660             if (m_crapConverter) { | 
| Chris@43 | 661                 src_delete(m_crapConverter); | 
| Chris@43 | 662                 m_crapConverter = 0; | 
| Chris@43 | 663             } | 
| Chris@43 | 664 | 
| Chris@43 | 665             m_mutex.unlock(); | 
| Chris@43 | 666 | 
| Chris@43 | 667             emit sampleRateMismatch(getSourceSampleRate(), | 
| Chris@43 | 668                                     getTargetSampleRate(), | 
| Chris@43 | 669                                     false); | 
| Chris@43 | 670 	} else { | 
| Chris@43 | 671 | 
| Chris@43 | 672             m_mutex.unlock(); | 
| Chris@43 | 673 | 
| Chris@43 | 674             emit sampleRateMismatch(getSourceSampleRate(), | 
| Chris@43 | 675                                     getTargetSampleRate(), | 
| Chris@43 | 676                                     true); | 
| Chris@43 | 677         } | 
| Chris@43 | 678     } else { | 
| Chris@43 | 679         m_mutex.unlock(); | 
| Chris@43 | 680     } | 
| Chris@43 | 681 } | 
| Chris@43 | 682 | 
| Chris@43 | 683 void | 
| Chris@43 | 684 AudioCallbackPlaySource::setResampleQuality(int q) | 
| Chris@43 | 685 { | 
| Chris@43 | 686     if (q == m_resampleQuality) return; | 
| Chris@43 | 687     m_resampleQuality = q; | 
| Chris@43 | 688 | 
| Chris@43 | 689 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 690     std::cerr << "AudioCallbackPlaySource::setResampleQuality: setting to " | 
| Chris@43 | 691               << m_resampleQuality << std::endl; | 
| Chris@43 | 692 #endif | 
| Chris@43 | 693 | 
| Chris@43 | 694     initialiseConverter(); | 
| Chris@43 | 695 } | 
| Chris@43 | 696 | 
| Chris@43 | 697 void | 
| Chris@43 | 698 AudioCallbackPlaySource::setAuditioningPlugin(RealTimePluginInstance *plugin) | 
| Chris@43 | 699 { | 
| Chris@43 | 700     RealTimePluginInstance *formerPlugin = m_auditioningPlugin; | 
| Chris@43 | 701     m_auditioningPlugin = plugin; | 
| Chris@43 | 702     m_auditioningPluginBypassed = false; | 
| Chris@43 | 703     if (formerPlugin) m_pluginScavenger.claim(formerPlugin); | 
| Chris@43 | 704 } | 
| Chris@43 | 705 | 
| Chris@43 | 706 void | 
| Chris@43 | 707 AudioCallbackPlaySource::setSoloModelSet(std::set<Model *> s) | 
| Chris@43 | 708 { | 
| Chris@43 | 709     m_audioGenerator->setSoloModelSet(s); | 
| Chris@43 | 710     clearRingBuffers(); | 
| Chris@43 | 711 } | 
| Chris@43 | 712 | 
| Chris@43 | 713 void | 
| Chris@43 | 714 AudioCallbackPlaySource::clearSoloModelSet() | 
| Chris@43 | 715 { | 
| Chris@43 | 716     m_audioGenerator->clearSoloModelSet(); | 
| Chris@43 | 717     clearRingBuffers(); | 
| Chris@43 | 718 } | 
| Chris@43 | 719 | 
| Chris@43 | 720 size_t | 
| Chris@43 | 721 AudioCallbackPlaySource::getTargetSampleRate() const | 
| Chris@43 | 722 { | 
| Chris@43 | 723     if (m_targetSampleRate) return m_targetSampleRate; | 
| Chris@43 | 724     else return getSourceSampleRate(); | 
| Chris@43 | 725 } | 
| Chris@43 | 726 | 
| Chris@43 | 727 size_t | 
| Chris@43 | 728 AudioCallbackPlaySource::getSourceChannelCount() const | 
| Chris@43 | 729 { | 
| Chris@43 | 730     return m_sourceChannelCount; | 
| Chris@43 | 731 } | 
| Chris@43 | 732 | 
| Chris@43 | 733 size_t | 
| Chris@43 | 734 AudioCallbackPlaySource::getTargetChannelCount() const | 
| Chris@43 | 735 { | 
| Chris@43 | 736     if (m_sourceChannelCount < 2) return 2; | 
| Chris@43 | 737     return m_sourceChannelCount; | 
| Chris@43 | 738 } | 
| Chris@43 | 739 | 
| Chris@43 | 740 size_t | 
| Chris@43 | 741 AudioCallbackPlaySource::getSourceSampleRate() const | 
| Chris@43 | 742 { | 
| Chris@43 | 743     return m_sourceSampleRate; | 
| Chris@43 | 744 } | 
| Chris@43 | 745 | 
| Chris@43 | 746 void | 
| Chris@43 | 747 AudioCallbackPlaySource::setTimeStretch(float factor, bool sharpen, bool mono) | 
| Chris@43 | 748 { | 
| Chris@43 | 749     // Avoid locks -- create, assign, mark old one for scavenging | 
| Chris@43 | 750     // later (as a call to getSourceSamples may still be using it) | 
| Chris@43 | 751 | 
| Chris@43 | 752     PhaseVocoderTimeStretcher *existingStretcher = m_timeStretcher; | 
| Chris@43 | 753 | 
| Chris@43 | 754     size_t channels = getTargetChannelCount(); | 
| Chris@43 | 755     if (mono) channels = 1; | 
| Chris@43 | 756 | 
| Chris@43 | 757     if (existingStretcher && | 
| Chris@43 | 758         existingStretcher->getRatio() == factor && | 
| Chris@43 | 759         existingStretcher->getSharpening() == sharpen && | 
| Chris@43 | 760         existingStretcher->getChannelCount() == channels) { | 
| Chris@43 | 761 	return; | 
| Chris@43 | 762     } | 
| Chris@43 | 763 | 
| Chris@43 | 764     if (factor != 1) { | 
| Chris@43 | 765 | 
| Chris@43 | 766         if (existingStretcher && | 
| Chris@43 | 767             existingStretcher->getSharpening() == sharpen && | 
| Chris@43 | 768             existingStretcher->getChannelCount() == channels) { | 
| Chris@43 | 769             existingStretcher->setRatio(factor); | 
| Chris@43 | 770             return; | 
| Chris@43 | 771         } | 
| Chris@43 | 772 | 
| Chris@43 | 773 	PhaseVocoderTimeStretcher *newStretcher = new PhaseVocoderTimeStretcher | 
| Chris@43 | 774 	    (getTargetSampleRate(), | 
| Chris@43 | 775              channels, | 
| Chris@43 | 776              factor, | 
| Chris@43 | 777              sharpen, | 
| Chris@43 | 778              getTargetBlockSize()); | 
| Chris@43 | 779 | 
| Chris@43 | 780 	m_timeStretcher = newStretcher; | 
| Chris@43 | 781 | 
| Chris@43 | 782     } else { | 
| Chris@43 | 783 	m_timeStretcher = 0; | 
| Chris@43 | 784     } | 
| Chris@43 | 785 | 
| Chris@43 | 786     if (existingStretcher) { | 
| Chris@43 | 787 	m_timeStretcherScavenger.claim(existingStretcher); | 
| Chris@43 | 788     } | 
| Chris@43 | 789 } | 
| Chris@43 | 790 | 
| Chris@43 | 791 size_t | 
| Chris@43 | 792 AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer) | 
| Chris@43 | 793 { | 
| Chris@43 | 794     if (!m_playing) { | 
| Chris@43 | 795 	for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { | 
| Chris@43 | 796 	    for (size_t i = 0; i < count; ++i) { | 
| Chris@43 | 797 		buffer[ch][i] = 0.0; | 
| Chris@43 | 798 	    } | 
| Chris@43 | 799 	} | 
| Chris@43 | 800 	return 0; | 
| Chris@43 | 801     } | 
| Chris@43 | 802 | 
| Chris@43 | 803     // Ensure that all buffers have at least the amount of data we | 
| Chris@43 | 804     // need -- else reduce the size of our requests correspondingly | 
| Chris@43 | 805 | 
| Chris@43 | 806     for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { | 
| Chris@43 | 807 | 
| Chris@43 | 808         RingBuffer<float> *rb = getReadRingBuffer(ch); | 
| Chris@43 | 809 | 
| Chris@43 | 810         if (!rb) { | 
| Chris@43 | 811             std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: " | 
| Chris@43 | 812                       << "No ring buffer available for channel " << ch | 
| Chris@43 | 813                       << ", returning no data here" << std::endl; | 
| Chris@43 | 814             count = 0; | 
| Chris@43 | 815             break; | 
| Chris@43 | 816         } | 
| Chris@43 | 817 | 
| Chris@43 | 818         size_t rs = rb->getReadSpace(); | 
| Chris@43 | 819         if (rs < count) { | 
| Chris@43 | 820 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 821             std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: " | 
| Chris@43 | 822                       << "Ring buffer for channel " << ch << " has only " | 
| Chris@43 | 823                       << rs << " (of " << count << ") samples available, " | 
| Chris@43 | 824                       << "reducing request size" << std::endl; | 
| Chris@43 | 825 #endif | 
| Chris@43 | 826             count = rs; | 
| Chris@43 | 827         } | 
| Chris@43 | 828     } | 
| Chris@43 | 829 | 
| Chris@43 | 830     if (count == 0) return 0; | 
| Chris@43 | 831 | 
| Chris@43 | 832     PhaseVocoderTimeStretcher *ts = m_timeStretcher; | 
| Chris@43 | 833 | 
| Chris@43 | 834     if (!ts || ts->getRatio() == 1) { | 
| Chris@43 | 835 | 
| Chris@43 | 836 	size_t got = 0; | 
| Chris@43 | 837 | 
| Chris@43 | 838 	for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { | 
| Chris@43 | 839 | 
| Chris@43 | 840 	    RingBuffer<float> *rb = getReadRingBuffer(ch); | 
| Chris@43 | 841 | 
| Chris@43 | 842 	    if (rb) { | 
| Chris@43 | 843 | 
| Chris@43 | 844 		// this is marginally more likely to leave our channels in | 
| Chris@43 | 845 		// sync after a processing failure than just passing "count": | 
| Chris@43 | 846 		size_t request = count; | 
| Chris@43 | 847 		if (ch > 0) request = got; | 
| Chris@43 | 848 | 
| Chris@43 | 849 		got = rb->read(buffer[ch], request); | 
| Chris@43 | 850 | 
| Chris@43 | 851 #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING | 
| Chris@43 | 852 		std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << std::endl; | 
| Chris@43 | 853 #endif | 
| Chris@43 | 854 	    } | 
| Chris@43 | 855 | 
| Chris@43 | 856 	    for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { | 
| Chris@43 | 857 		for (size_t i = got; i < count; ++i) { | 
| Chris@43 | 858 		    buffer[ch][i] = 0.0; | 
| Chris@43 | 859 		} | 
| Chris@43 | 860 	    } | 
| Chris@43 | 861 	} | 
| Chris@43 | 862 | 
| Chris@43 | 863         applyAuditioningEffect(count, buffer); | 
| Chris@43 | 864 | 
| Chris@43 | 865         m_condition.wakeAll(); | 
| Chris@43 | 866 	return got; | 
| Chris@43 | 867     } | 
| Chris@43 | 868 | 
| Chris@43 | 869     float ratio = ts->getRatio(); | 
| Chris@43 | 870 | 
| Chris@43 | 871 //            std::cout << "ratio = " << ratio << std::endl; | 
| Chris@43 | 872 | 
| Chris@43 | 873     size_t channels = getTargetChannelCount(); | 
| Chris@43 | 874     bool mix = (channels > 1 && ts->getChannelCount() == 1); | 
| Chris@43 | 875 | 
| Chris@43 | 876     size_t available; | 
| Chris@43 | 877 | 
| Chris@43 | 878     int warned = 0; | 
| Chris@43 | 879 | 
| Chris@43 | 880     // We want output blocks of e.g. 1024 (probably fixed, certainly | 
| Chris@43 | 881     // bounded).  We can provide input blocks of any size (unbounded) | 
| Chris@43 | 882     // at the timestretcher's request.  The input block for a given | 
| Chris@43 | 883     // output is approx output / ratio, but we can't predict it | 
| Chris@43 | 884     // exactly, for an adaptive timestretcher.  The stretcher will | 
| Chris@43 | 885     // need some additional buffer space.  See the time stretcher code | 
| Chris@43 | 886     // and comments. | 
| Chris@43 | 887 | 
| Chris@43 | 888     while ((available = ts->getAvailableOutputSamples()) < count) { | 
| Chris@43 | 889 | 
| Chris@43 | 890         size_t reqd = lrintf((count - available) / ratio); | 
| Chris@43 | 891         reqd = std::max(reqd, ts->getRequiredInputSamples()); | 
| Chris@43 | 892         if (reqd == 0) reqd = 1; | 
| Chris@43 | 893 | 
| Chris@43 | 894         float *ib[channels]; | 
| Chris@43 | 895 | 
| Chris@43 | 896         size_t got = reqd; | 
| Chris@43 | 897 | 
| Chris@43 | 898         if (mix) { | 
| Chris@43 | 899             for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 900                 if (c == 0) ib[c] = new float[reqd]; //!!! fix -- this is a rt function | 
| Chris@43 | 901                 else ib[c] = 0; | 
| Chris@43 | 902                 RingBuffer<float> *rb = getReadRingBuffer(c); | 
| Chris@43 | 903                 if (rb) { | 
| Chris@43 | 904                     size_t gotHere; | 
| Chris@43 | 905                     if (c > 0) gotHere = rb->readAdding(ib[0], got); | 
| Chris@43 | 906                     else gotHere = rb->read(ib[0], got); | 
| Chris@43 | 907                     if (gotHere < got) got = gotHere; | 
| Chris@43 | 908                 } | 
| Chris@43 | 909             } | 
| Chris@43 | 910         } else { | 
| Chris@43 | 911             for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 912                 ib[c] = new float[reqd]; //!!! fix -- this is a rt function | 
| Chris@43 | 913                 RingBuffer<float> *rb = getReadRingBuffer(c); | 
| Chris@43 | 914                 if (rb) { | 
| Chris@43 | 915                     size_t gotHere = rb->read(ib[c], got); | 
| Chris@43 | 916                     if (gotHere < got) got = gotHere; | 
| Chris@43 | 917                 } | 
| Chris@43 | 918             } | 
| Chris@43 | 919         } | 
| Chris@43 | 920 | 
| Chris@43 | 921         if (got < reqd) { | 
| Chris@43 | 922             std::cerr << "WARNING: Read underrun in playback (" | 
| Chris@43 | 923                       << got << " < " << reqd << ")" << std::endl; | 
| Chris@43 | 924         } | 
| Chris@43 | 925 | 
| Chris@43 | 926         ts->putInput(ib, got); | 
| Chris@43 | 927 | 
| Chris@43 | 928         for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 929             delete[] ib[c]; | 
| Chris@43 | 930         } | 
| Chris@43 | 931 | 
| Chris@43 | 932         if (got == 0) break; | 
| Chris@43 | 933 | 
| Chris@43 | 934         if (ts->getAvailableOutputSamples() == available) { | 
| Chris@43 | 935             std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << std::endl; | 
| Chris@43 | 936             if (++warned == 5) break; | 
| Chris@43 | 937         } | 
| Chris@43 | 938     } | 
| Chris@43 | 939 | 
| Chris@43 | 940     ts->getOutput(buffer, count); | 
| Chris@43 | 941 | 
| Chris@43 | 942     if (mix) { | 
| Chris@43 | 943         for (size_t c = 1; c < channels; ++c) { | 
| Chris@43 | 944             for (size_t i = 0; i < count; ++i) { | 
| Chris@43 | 945                 buffer[c][i] = buffer[0][i] / channels; | 
| Chris@43 | 946             } | 
| Chris@43 | 947         } | 
| Chris@43 | 948         for (size_t i = 0; i < count; ++i) { | 
| Chris@43 | 949             buffer[0][i] /= channels; | 
| Chris@43 | 950         } | 
| Chris@43 | 951     } | 
| Chris@43 | 952 | 
| Chris@43 | 953     applyAuditioningEffect(count, buffer); | 
| Chris@43 | 954 | 
| Chris@43 | 955     m_condition.wakeAll(); | 
| Chris@43 | 956 | 
| Chris@43 | 957     return count; | 
| Chris@43 | 958 } | 
| Chris@43 | 959 | 
| Chris@43 | 960 void | 
| Chris@43 | 961 AudioCallbackPlaySource::applyAuditioningEffect(size_t count, float **buffers) | 
| Chris@43 | 962 { | 
| Chris@43 | 963     if (m_auditioningPluginBypassed) return; | 
| Chris@43 | 964     RealTimePluginInstance *plugin = m_auditioningPlugin; | 
| Chris@43 | 965     if (!plugin) return; | 
| Chris@43 | 966 | 
| Chris@43 | 967     if (plugin->getAudioInputCount() != getTargetChannelCount()) { | 
| Chris@43 | 968 //        std::cerr << "plugin input count " << plugin->getAudioInputCount() | 
| Chris@43 | 969 //                  << " != our channel count " << getTargetChannelCount() | 
| Chris@43 | 970 //                  << std::endl; | 
| Chris@43 | 971         return; | 
| Chris@43 | 972     } | 
| Chris@43 | 973     if (plugin->getAudioOutputCount() != getTargetChannelCount()) { | 
| Chris@43 | 974 //        std::cerr << "plugin output count " << plugin->getAudioOutputCount() | 
| Chris@43 | 975 //                  << " != our channel count " << getTargetChannelCount() | 
| Chris@43 | 976 //                  << std::endl; | 
| Chris@43 | 977         return; | 
| Chris@43 | 978     } | 
| Chris@43 | 979     if (plugin->getBufferSize() != count) { | 
| Chris@43 | 980 //        std::cerr << "plugin buffer size " << plugin->getBufferSize() | 
| Chris@43 | 981 //                  << " != our block size " << count | 
| Chris@43 | 982 //                  << std::endl; | 
| Chris@43 | 983         return; | 
| Chris@43 | 984     } | 
| Chris@43 | 985 | 
| Chris@43 | 986     float **ib = plugin->getAudioInputBuffers(); | 
| Chris@43 | 987     float **ob = plugin->getAudioOutputBuffers(); | 
| Chris@43 | 988 | 
| Chris@43 | 989     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 990         for (size_t i = 0; i < count; ++i) { | 
| Chris@43 | 991             ib[c][i] = buffers[c][i]; | 
| Chris@43 | 992         } | 
| Chris@43 | 993     } | 
| Chris@43 | 994 | 
| Chris@43 | 995     plugin->run(Vamp::RealTime::zeroTime); | 
| Chris@43 | 996 | 
| Chris@43 | 997     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 998         for (size_t i = 0; i < count; ++i) { | 
| Chris@43 | 999             buffers[c][i] = ob[c][i]; | 
| Chris@43 | 1000         } | 
| Chris@43 | 1001     } | 
| Chris@43 | 1002 } | 
| Chris@43 | 1003 | 
| Chris@43 | 1004 // Called from fill thread, m_playing true, mutex held | 
| Chris@43 | 1005 bool | 
| Chris@43 | 1006 AudioCallbackPlaySource::fillBuffers() | 
| Chris@43 | 1007 { | 
| Chris@43 | 1008     static float *tmp = 0; | 
| Chris@43 | 1009     static size_t tmpSize = 0; | 
| Chris@43 | 1010 | 
| Chris@43 | 1011     size_t space = 0; | 
| Chris@43 | 1012     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 1013 	RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1014 	if (wb) { | 
| Chris@43 | 1015 	    size_t spaceHere = wb->getWriteSpace(); | 
| Chris@43 | 1016 	    if (c == 0 || spaceHere < space) space = spaceHere; | 
| Chris@43 | 1017 	} | 
| Chris@43 | 1018     } | 
| Chris@43 | 1019 | 
| Chris@43 | 1020     if (space == 0) return false; | 
| Chris@43 | 1021 | 
| Chris@43 | 1022     size_t f = m_writeBufferFill; | 
| Chris@43 | 1023 | 
| Chris@43 | 1024     bool readWriteEqual = (m_readBuffers == m_writeBuffers); | 
| Chris@43 | 1025 | 
| Chris@43 | 1026 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1027     std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl; | 
| Chris@43 | 1028 #endif | 
| Chris@43 | 1029 | 
| Chris@43 | 1030 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1031     std::cout << "buffered to " << f << " already" << std::endl; | 
| Chris@43 | 1032 #endif | 
| Chris@43 | 1033 | 
| Chris@43 | 1034     bool resample = (getSourceSampleRate() != getTargetSampleRate()); | 
| Chris@43 | 1035 | 
| Chris@43 | 1036 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1037     std::cout << (resample ? "" : "not ") << "resampling (source " << getSourceSampleRate() << ", target " << getTargetSampleRate() << ")" << std::endl; | 
| Chris@43 | 1038 #endif | 
| Chris@43 | 1039 | 
| Chris@43 | 1040     size_t channels = getTargetChannelCount(); | 
| Chris@43 | 1041 | 
| Chris@43 | 1042     size_t orig = space; | 
| Chris@43 | 1043     size_t got = 0; | 
| Chris@43 | 1044 | 
| Chris@43 | 1045     static float **bufferPtrs = 0; | 
| Chris@43 | 1046     static size_t bufferPtrCount = 0; | 
| Chris@43 | 1047 | 
| Chris@43 | 1048     if (bufferPtrCount < channels) { | 
| Chris@43 | 1049 	if (bufferPtrs) delete[] bufferPtrs; | 
| Chris@43 | 1050 	bufferPtrs = new float *[channels]; | 
| Chris@43 | 1051 	bufferPtrCount = channels; | 
| Chris@43 | 1052     } | 
| Chris@43 | 1053 | 
| Chris@43 | 1054     size_t generatorBlockSize = m_audioGenerator->getBlockSize(); | 
| Chris@43 | 1055 | 
| Chris@43 | 1056     if (resample && !m_converter) { | 
| Chris@43 | 1057 	static bool warned = false; | 
| Chris@43 | 1058 	if (!warned) { | 
| Chris@43 | 1059 	    std::cerr << "WARNING: sample rates differ, but no converter available!" << std::endl; | 
| Chris@43 | 1060 	    warned = true; | 
| Chris@43 | 1061 	} | 
| Chris@43 | 1062     } | 
| Chris@43 | 1063 | 
| Chris@43 | 1064     if (resample && m_converter) { | 
| Chris@43 | 1065 | 
| Chris@43 | 1066 	double ratio = | 
| Chris@43 | 1067 	    double(getTargetSampleRate()) / double(getSourceSampleRate()); | 
| Chris@43 | 1068 	orig = size_t(orig / ratio + 0.1); | 
| Chris@43 | 1069 | 
| Chris@43 | 1070 	// orig must be a multiple of generatorBlockSize | 
| Chris@43 | 1071 	orig = (orig / generatorBlockSize) * generatorBlockSize; | 
| Chris@43 | 1072 	if (orig == 0) return false; | 
| Chris@43 | 1073 | 
| Chris@43 | 1074 	size_t work = std::max(orig, space); | 
| Chris@43 | 1075 | 
| Chris@43 | 1076 	// We only allocate one buffer, but we use it in two halves. | 
| Chris@43 | 1077 	// We place the non-interleaved values in the second half of | 
| Chris@43 | 1078 	// the buffer (orig samples for channel 0, orig samples for | 
| Chris@43 | 1079 	// channel 1 etc), and then interleave them into the first | 
| Chris@43 | 1080 	// half of the buffer.  Then we resample back into the second | 
| Chris@43 | 1081 	// half (interleaved) and de-interleave the results back to | 
| Chris@43 | 1082 	// the start of the buffer for insertion into the ringbuffers. | 
| Chris@43 | 1083 	// What a faff -- especially as we've already de-interleaved | 
| Chris@43 | 1084 	// the audio data from the source file elsewhere before we | 
| Chris@43 | 1085 	// even reach this point. | 
| Chris@43 | 1086 | 
| Chris@43 | 1087 	if (tmpSize < channels * work * 2) { | 
| Chris@43 | 1088 	    delete[] tmp; | 
| Chris@43 | 1089 	    tmp = new float[channels * work * 2]; | 
| Chris@43 | 1090 	    tmpSize = channels * work * 2; | 
| Chris@43 | 1091 	} | 
| Chris@43 | 1092 | 
| Chris@43 | 1093 	float *nonintlv = tmp + channels * work; | 
| Chris@43 | 1094 	float *intlv = tmp; | 
| Chris@43 | 1095 	float *srcout = tmp + channels * work; | 
| Chris@43 | 1096 | 
| Chris@43 | 1097 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1098 	    for (size_t i = 0; i < orig; ++i) { | 
| Chris@43 | 1099 		nonintlv[channels * i + c] = 0.0f; | 
| Chris@43 | 1100 	    } | 
| Chris@43 | 1101 	} | 
| Chris@43 | 1102 | 
| Chris@43 | 1103 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1104 	    bufferPtrs[c] = nonintlv + c * orig; | 
| Chris@43 | 1105 	} | 
| Chris@43 | 1106 | 
| Chris@43 | 1107 	got = mixModels(f, orig, bufferPtrs); | 
| Chris@43 | 1108 | 
| Chris@43 | 1109 	// and interleave into first half | 
| Chris@43 | 1110 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1111 	    for (size_t i = 0; i < got; ++i) { | 
| Chris@43 | 1112 		float sample = nonintlv[c * got + i]; | 
| Chris@43 | 1113 		intlv[channels * i + c] = sample; | 
| Chris@43 | 1114 	    } | 
| Chris@43 | 1115 	} | 
| Chris@43 | 1116 | 
| Chris@43 | 1117 	SRC_DATA data; | 
| Chris@43 | 1118 	data.data_in = intlv; | 
| Chris@43 | 1119 	data.data_out = srcout; | 
| Chris@43 | 1120 	data.input_frames = got; | 
| Chris@43 | 1121 	data.output_frames = work; | 
| Chris@43 | 1122 	data.src_ratio = ratio; | 
| Chris@43 | 1123 	data.end_of_input = 0; | 
| Chris@43 | 1124 | 
| Chris@43 | 1125 	int err = 0; | 
| Chris@43 | 1126 | 
| Chris@43 | 1127         if (m_timeStretcher && m_timeStretcher->getRatio() < 0.4) { | 
| Chris@43 | 1128 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1129             std::cout << "Using crappy converter" << std::endl; | 
| Chris@43 | 1130 #endif | 
| Chris@43 | 1131             err = src_process(m_crapConverter, &data); | 
| Chris@43 | 1132         } else { | 
| Chris@43 | 1133             err = src_process(m_converter, &data); | 
| Chris@43 | 1134         } | 
| Chris@43 | 1135 | 
| Chris@43 | 1136 	size_t toCopy = size_t(got * ratio + 0.1); | 
| Chris@43 | 1137 | 
| Chris@43 | 1138 	if (err) { | 
| Chris@43 | 1139 	    std::cerr | 
| Chris@43 | 1140 		<< "AudioCallbackPlaySourceFillThread: ERROR in samplerate conversion: " | 
| Chris@43 | 1141 		<< src_strerror(err) << std::endl; | 
| Chris@43 | 1142 	    //!!! Then what? | 
| Chris@43 | 1143 	} else { | 
| Chris@43 | 1144 	    got = data.input_frames_used; | 
| Chris@43 | 1145 	    toCopy = data.output_frames_gen; | 
| Chris@43 | 1146 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1147 	    std::cout << "Resampled " << got << " frames to " << toCopy << " frames" << std::endl; | 
| Chris@43 | 1148 #endif | 
| Chris@43 | 1149 	} | 
| Chris@43 | 1150 | 
| Chris@43 | 1151 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1152 	    for (size_t i = 0; i < toCopy; ++i) { | 
| Chris@43 | 1153 		tmp[i] = srcout[channels * i + c]; | 
| Chris@43 | 1154 	    } | 
| Chris@43 | 1155 	    RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1156 	    if (wb) wb->write(tmp, toCopy); | 
| Chris@43 | 1157 	} | 
| Chris@43 | 1158 | 
| Chris@43 | 1159 	m_writeBufferFill = f; | 
| Chris@43 | 1160 	if (readWriteEqual) m_readBufferFill = f; | 
| Chris@43 | 1161 | 
| Chris@43 | 1162     } else { | 
| Chris@43 | 1163 | 
| Chris@43 | 1164 	// space must be a multiple of generatorBlockSize | 
| Chris@43 | 1165 	space = (space / generatorBlockSize) * generatorBlockSize; | 
| Chris@43 | 1166 	if (space == 0) return false; | 
| Chris@43 | 1167 | 
| Chris@43 | 1168 	if (tmpSize < channels * space) { | 
| Chris@43 | 1169 	    delete[] tmp; | 
| Chris@43 | 1170 	    tmp = new float[channels * space]; | 
| Chris@43 | 1171 	    tmpSize = channels * space; | 
| Chris@43 | 1172 	} | 
| Chris@43 | 1173 | 
| Chris@43 | 1174 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1175 | 
| Chris@43 | 1176 	    bufferPtrs[c] = tmp + c * space; | 
| Chris@43 | 1177 | 
| Chris@43 | 1178 	    for (size_t i = 0; i < space; ++i) { | 
| Chris@43 | 1179 		tmp[c * space + i] = 0.0f; | 
| Chris@43 | 1180 	    } | 
| Chris@43 | 1181 	} | 
| Chris@43 | 1182 | 
| Chris@43 | 1183 	size_t got = mixModels(f, space, bufferPtrs); | 
| Chris@43 | 1184 | 
| Chris@43 | 1185 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1186 | 
| Chris@43 | 1187 	    RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1188 	    if (wb) { | 
| Chris@43 | 1189                 size_t actual = wb->write(bufferPtrs[c], got); | 
| Chris@43 | 1190 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1191 		std::cout << "Wrote " << actual << " samples for ch " << c << ", now " | 
| Chris@43 | 1192 			  << wb->getReadSpace() << " to read" | 
| Chris@43 | 1193 			  << std::endl; | 
| Chris@43 | 1194 #endif | 
| Chris@43 | 1195                 if (actual < got) { | 
| Chris@43 | 1196                     std::cerr << "WARNING: Buffer overrun in channel " << c | 
| Chris@43 | 1197                               << ": wrote " << actual << " of " << got | 
| Chris@43 | 1198                               << " samples" << std::endl; | 
| Chris@43 | 1199                 } | 
| Chris@43 | 1200             } | 
| Chris@43 | 1201 	} | 
| Chris@43 | 1202 | 
| Chris@43 | 1203 	m_writeBufferFill = f; | 
| Chris@43 | 1204 	if (readWriteEqual) m_readBufferFill = f; | 
| Chris@43 | 1205 | 
| Chris@43 | 1206 	//!!! 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 | 1207     } | 
| Chris@43 | 1208 | 
| Chris@43 | 1209     return true; | 
| Chris@43 | 1210 } | 
| Chris@43 | 1211 | 
| Chris@43 | 1212 size_t | 
| Chris@43 | 1213 AudioCallbackPlaySource::mixModels(size_t &frame, size_t count, float **buffers) | 
| Chris@43 | 1214 { | 
| Chris@43 | 1215     size_t processed = 0; | 
| Chris@43 | 1216     size_t chunkStart = frame; | 
| Chris@43 | 1217     size_t chunkSize = count; | 
| Chris@43 | 1218     size_t selectionSize = 0; | 
| Chris@43 | 1219     size_t nextChunkStart = chunkStart + chunkSize; | 
| Chris@43 | 1220 | 
| Chris@43 | 1221     bool looping = m_viewManager->getPlayLoopMode(); | 
| Chris@43 | 1222     bool constrained = (m_viewManager->getPlaySelectionMode() && | 
| Chris@43 | 1223 			!m_viewManager->getSelections().empty()); | 
| Chris@43 | 1224 | 
| Chris@43 | 1225     static float **chunkBufferPtrs = 0; | 
| Chris@43 | 1226     static size_t chunkBufferPtrCount = 0; | 
| Chris@43 | 1227     size_t channels = getTargetChannelCount(); | 
| Chris@43 | 1228 | 
| Chris@43 | 1229 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1230     std::cout << "Selection playback: start " << frame << ", size " << count <<", channels " << channels << std::endl; | 
| Chris@43 | 1231 #endif | 
| Chris@43 | 1232 | 
| Chris@43 | 1233     if (chunkBufferPtrCount < channels) { | 
| Chris@43 | 1234 	if (chunkBufferPtrs) delete[] chunkBufferPtrs; | 
| Chris@43 | 1235 	chunkBufferPtrs = new float *[channels]; | 
| Chris@43 | 1236 	chunkBufferPtrCount = channels; | 
| Chris@43 | 1237     } | 
| Chris@43 | 1238 | 
| Chris@43 | 1239     for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1240 	chunkBufferPtrs[c] = buffers[c]; | 
| Chris@43 | 1241     } | 
| Chris@43 | 1242 | 
| Chris@43 | 1243     while (processed < count) { | 
| Chris@43 | 1244 | 
| Chris@43 | 1245 	chunkSize = count - processed; | 
| Chris@43 | 1246 	nextChunkStart = chunkStart + chunkSize; | 
| Chris@43 | 1247 	selectionSize = 0; | 
| Chris@43 | 1248 | 
| Chris@43 | 1249 	size_t fadeIn = 0, fadeOut = 0; | 
| Chris@43 | 1250 | 
| Chris@43 | 1251 	if (constrained) { | 
| Chris@60 | 1252 | 
| Chris@60 | 1253             size_t rChunkStart = | 
| Chris@60 | 1254                 m_viewManager->alignPlaybackFrameToReference(chunkStart); | 
| Chris@43 | 1255 | 
| Chris@43 | 1256 	    Selection selection = | 
| Chris@60 | 1257 		m_viewManager->getContainingSelection(rChunkStart, true); | 
| Chris@43 | 1258 | 
| Chris@43 | 1259 	    if (selection.isEmpty()) { | 
| Chris@43 | 1260 		if (looping) { | 
| Chris@43 | 1261 		    selection = *m_viewManager->getSelections().begin(); | 
| Chris@60 | 1262 		    chunkStart = m_viewManager->alignReferenceToPlaybackFrame | 
| Chris@60 | 1263                         (selection.getStartFrame()); | 
| Chris@43 | 1264 		    fadeIn = 50; | 
| Chris@43 | 1265 		} | 
| Chris@43 | 1266 	    } | 
| Chris@43 | 1267 | 
| Chris@43 | 1268 	    if (selection.isEmpty()) { | 
| Chris@43 | 1269 | 
| Chris@43 | 1270 		chunkSize = 0; | 
| Chris@43 | 1271 		nextChunkStart = chunkStart; | 
| Chris@43 | 1272 | 
| Chris@43 | 1273 	    } else { | 
| Chris@43 | 1274 | 
| Chris@60 | 1275                 size_t sf = m_viewManager->alignReferenceToPlaybackFrame | 
| Chris@60 | 1276                     (selection.getStartFrame()); | 
| Chris@60 | 1277                 size_t ef = m_viewManager->alignReferenceToPlaybackFrame | 
| Chris@60 | 1278                     (selection.getEndFrame()); | 
| Chris@43 | 1279 | 
| Chris@60 | 1280 		selectionSize = ef - sf; | 
| Chris@60 | 1281 | 
| Chris@60 | 1282 		if (chunkStart < sf) { | 
| Chris@60 | 1283 		    chunkStart = sf; | 
| Chris@43 | 1284 		    fadeIn = 50; | 
| Chris@43 | 1285 		} | 
| Chris@43 | 1286 | 
| Chris@43 | 1287 		nextChunkStart = chunkStart + chunkSize; | 
| Chris@43 | 1288 | 
| Chris@60 | 1289 		if (nextChunkStart >= ef) { | 
| Chris@60 | 1290 		    nextChunkStart = ef; | 
| Chris@43 | 1291 		    fadeOut = 50; | 
| Chris@43 | 1292 		} | 
| Chris@43 | 1293 | 
| Chris@43 | 1294 		chunkSize = nextChunkStart - chunkStart; | 
| Chris@43 | 1295 	    } | 
| Chris@43 | 1296 | 
| Chris@43 | 1297 	} else if (looping && m_lastModelEndFrame > 0) { | 
| Chris@43 | 1298 | 
| Chris@43 | 1299 	    if (chunkStart >= m_lastModelEndFrame) { | 
| Chris@43 | 1300 		chunkStart = 0; | 
| Chris@43 | 1301 	    } | 
| Chris@43 | 1302 	    if (chunkSize > m_lastModelEndFrame - chunkStart) { | 
| Chris@43 | 1303 		chunkSize = m_lastModelEndFrame - chunkStart; | 
| Chris@43 | 1304 	    } | 
| Chris@43 | 1305 	    nextChunkStart = chunkStart + chunkSize; | 
| Chris@43 | 1306 	} | 
| Chris@43 | 1307 | 
| Chris@43 | 1308 //	std::cout << "chunkStart " << chunkStart << ", chunkSize " << chunkSize << ", nextChunkStart " << nextChunkStart << ", frame " << frame << ", count " << count << ", processed " << processed << std::endl; | 
| Chris@43 | 1309 | 
| Chris@43 | 1310 	if (!chunkSize) { | 
| Chris@43 | 1311 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1312 	    std::cout << "Ending selection playback at " << nextChunkStart << std::endl; | 
| Chris@43 | 1313 #endif | 
| Chris@43 | 1314 	    // We need to maintain full buffers so that the other | 
| Chris@43 | 1315 	    // thread can tell where it's got to in the playback -- so | 
| Chris@43 | 1316 	    // return the full amount here | 
| Chris@43 | 1317 	    frame = frame + count; | 
| Chris@43 | 1318 	    return count; | 
| Chris@43 | 1319 	} | 
| Chris@43 | 1320 | 
| Chris@43 | 1321 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1322 	std::cout << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << std::endl; | 
| Chris@43 | 1323 #endif | 
| Chris@43 | 1324 | 
| Chris@43 | 1325 	size_t got = 0; | 
| Chris@43 | 1326 | 
| Chris@43 | 1327 	if (selectionSize < 100) { | 
| Chris@43 | 1328 	    fadeIn = 0; | 
| Chris@43 | 1329 	    fadeOut = 0; | 
| Chris@43 | 1330 	} else if (selectionSize < 300) { | 
| Chris@43 | 1331 	    if (fadeIn > 0) fadeIn = 10; | 
| Chris@43 | 1332 	    if (fadeOut > 0) fadeOut = 10; | 
| Chris@43 | 1333 	} | 
| Chris@43 | 1334 | 
| Chris@43 | 1335 	if (fadeIn > 0) { | 
| Chris@43 | 1336 	    if (processed * 2 < fadeIn) { | 
| Chris@43 | 1337 		fadeIn = processed * 2; | 
| Chris@43 | 1338 	    } | 
| Chris@43 | 1339 	} | 
| Chris@43 | 1340 | 
| Chris@43 | 1341 	if (fadeOut > 0) { | 
| Chris@43 | 1342 	    if ((count - processed - chunkSize) * 2 < fadeOut) { | 
| Chris@43 | 1343 		fadeOut = (count - processed - chunkSize) * 2; | 
| Chris@43 | 1344 	    } | 
| Chris@43 | 1345 	} | 
| Chris@43 | 1346 | 
| Chris@43 | 1347 	for (std::set<Model *>::iterator mi = m_models.begin(); | 
| Chris@43 | 1348 	     mi != m_models.end(); ++mi) { | 
| Chris@43 | 1349 | 
| Chris@43 | 1350 	    got = m_audioGenerator->mixModel(*mi, chunkStart, | 
| Chris@43 | 1351 					     chunkSize, chunkBufferPtrs, | 
| Chris@43 | 1352 					     fadeIn, fadeOut); | 
| Chris@43 | 1353 	} | 
| Chris@43 | 1354 | 
| Chris@43 | 1355 	for (size_t c = 0; c < channels; ++c) { | 
| Chris@43 | 1356 	    chunkBufferPtrs[c] += chunkSize; | 
| Chris@43 | 1357 	} | 
| Chris@43 | 1358 | 
| Chris@43 | 1359 	processed += chunkSize; | 
| Chris@43 | 1360 	chunkStart = nextChunkStart; | 
| Chris@43 | 1361     } | 
| Chris@43 | 1362 | 
| Chris@43 | 1363 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1364     std::cout << "Returning selection playback " << processed << " frames to " << nextChunkStart << std::endl; | 
| Chris@43 | 1365 #endif | 
| Chris@43 | 1366 | 
| Chris@43 | 1367     frame = nextChunkStart; | 
| Chris@43 | 1368     return processed; | 
| Chris@43 | 1369 } | 
| Chris@43 | 1370 | 
| Chris@43 | 1371 void | 
| Chris@43 | 1372 AudioCallbackPlaySource::unifyRingBuffers() | 
| Chris@43 | 1373 { | 
| Chris@43 | 1374     if (m_readBuffers == m_writeBuffers) return; | 
| Chris@43 | 1375 | 
| Chris@43 | 1376     // only unify if there will be something to read | 
| Chris@43 | 1377     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 1378 	RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1379 	if (wb) { | 
| Chris@43 | 1380 	    if (wb->getReadSpace() < m_blockSize * 2) { | 
| Chris@43 | 1381 		if ((m_writeBufferFill + m_blockSize * 2) < | 
| Chris@43 | 1382 		    m_lastModelEndFrame) { | 
| Chris@43 | 1383 		    // OK, we don't have enough and there's more to | 
| Chris@43 | 1384 		    // read -- don't unify until we can do better | 
| Chris@43 | 1385 		    return; | 
| Chris@43 | 1386 		} | 
| Chris@43 | 1387 	    } | 
| Chris@43 | 1388 	    break; | 
| Chris@43 | 1389 	} | 
| Chris@43 | 1390     } | 
| Chris@43 | 1391 | 
| Chris@43 | 1392     size_t rf = m_readBufferFill; | 
| Chris@43 | 1393     RingBuffer<float> *rb = getReadRingBuffer(0); | 
| Chris@43 | 1394     if (rb) { | 
| Chris@43 | 1395 	size_t rs = rb->getReadSpace(); | 
| Chris@43 | 1396 	//!!! incorrect when in non-contiguous selection, see comments elsewhere | 
| Chris@43 | 1397 //	std::cout << "rs = " << rs << std::endl; | 
| Chris@43 | 1398 	if (rs < rf) rf -= rs; | 
| Chris@43 | 1399 	else rf = 0; | 
| Chris@43 | 1400     } | 
| Chris@43 | 1401 | 
| Chris@43 | 1402     //std::cout << "m_readBufferFill = " << m_readBufferFill << ", rf = " << rf << ", m_writeBufferFill = " << m_writeBufferFill << std::endl; | 
| Chris@43 | 1403 | 
| Chris@43 | 1404     size_t wf = m_writeBufferFill; | 
| Chris@43 | 1405     size_t skip = 0; | 
| Chris@43 | 1406     for (size_t c = 0; c < getTargetChannelCount(); ++c) { | 
| Chris@43 | 1407 	RingBuffer<float> *wb = getWriteRingBuffer(c); | 
| Chris@43 | 1408 	if (wb) { | 
| Chris@43 | 1409 	    if (c == 0) { | 
| Chris@43 | 1410 | 
| Chris@43 | 1411 		size_t wrs = wb->getReadSpace(); | 
| Chris@43 | 1412 //		std::cout << "wrs = " << wrs << std::endl; | 
| Chris@43 | 1413 | 
| Chris@43 | 1414 		if (wrs < wf) wf -= wrs; | 
| Chris@43 | 1415 		else wf = 0; | 
| Chris@43 | 1416 //		std::cout << "wf = " << wf << std::endl; | 
| Chris@43 | 1417 | 
| Chris@43 | 1418 		if (wf < rf) skip = rf - wf; | 
| Chris@43 | 1419 		if (skip == 0) break; | 
| Chris@43 | 1420 	    } | 
| Chris@43 | 1421 | 
| Chris@43 | 1422 //	    std::cout << "skipping " << skip << std::endl; | 
| Chris@43 | 1423 	    wb->skip(skip); | 
| Chris@43 | 1424 	} | 
| Chris@43 | 1425     } | 
| Chris@43 | 1426 | 
| Chris@43 | 1427     m_bufferScavenger.claim(m_readBuffers); | 
| Chris@43 | 1428     m_readBuffers = m_writeBuffers; | 
| Chris@43 | 1429     m_readBufferFill = m_writeBufferFill; | 
| Chris@43 | 1430 //    std::cout << "unified" << std::endl; | 
| Chris@43 | 1431 } | 
| Chris@43 | 1432 | 
| Chris@43 | 1433 void | 
| Chris@43 | 1434 AudioCallbackPlaySource::FillThread::run() | 
| Chris@43 | 1435 { | 
| Chris@43 | 1436     AudioCallbackPlaySource &s(m_source); | 
| Chris@43 | 1437 | 
| Chris@43 | 1438 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1439     std::cout << "AudioCallbackPlaySourceFillThread starting" << std::endl; | 
| Chris@43 | 1440 #endif | 
| Chris@43 | 1441 | 
| Chris@43 | 1442     s.m_mutex.lock(); | 
| Chris@43 | 1443 | 
| Chris@43 | 1444     bool previouslyPlaying = s.m_playing; | 
| Chris@43 | 1445     bool work = false; | 
| Chris@43 | 1446 | 
| Chris@43 | 1447     while (!s.m_exiting) { | 
| Chris@43 | 1448 | 
| Chris@43 | 1449 	s.unifyRingBuffers(); | 
| Chris@43 | 1450 	s.m_bufferScavenger.scavenge(); | 
| Chris@43 | 1451         s.m_pluginScavenger.scavenge(); | 
| Chris@43 | 1452 	s.m_timeStretcherScavenger.scavenge(); | 
| Chris@43 | 1453 | 
| Chris@43 | 1454 	if (work && s.m_playing && s.getSourceSampleRate()) { | 
| Chris@43 | 1455 | 
| Chris@43 | 1456 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1457 	    std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl; | 
| Chris@43 | 1458 #endif | 
| Chris@43 | 1459 | 
| Chris@43 | 1460 	    s.m_mutex.unlock(); | 
| Chris@43 | 1461 	    s.m_mutex.lock(); | 
| Chris@43 | 1462 | 
| Chris@43 | 1463 	} else { | 
| Chris@43 | 1464 | 
| Chris@43 | 1465 	    float ms = 100; | 
| Chris@43 | 1466 	    if (s.getSourceSampleRate() > 0) { | 
| Chris@43 | 1467 		ms = float(m_ringBufferSize) / float(s.getSourceSampleRate()) * 1000.0; | 
| Chris@43 | 1468 	    } | 
| Chris@43 | 1469 | 
| Chris@43 | 1470 	    if (s.m_playing) ms /= 10; | 
| Chris@43 | 1471 | 
| Chris@43 | 1472 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1473             if (!s.m_playing) std::cout << std::endl; | 
| Chris@43 | 1474 	    std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms << "ms..." << std::endl; | 
| Chris@43 | 1475 #endif | 
| Chris@43 | 1476 | 
| Chris@43 | 1477 	    s.m_condition.wait(&s.m_mutex, size_t(ms)); | 
| Chris@43 | 1478 	} | 
| Chris@43 | 1479 | 
| Chris@43 | 1480 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1481 	std::cout << "AudioCallbackPlaySourceFillThread: awoken" << std::endl; | 
| Chris@43 | 1482 #endif | 
| Chris@43 | 1483 | 
| Chris@43 | 1484 	work = false; | 
| Chris@43 | 1485 | 
| Chris@43 | 1486 	if (!s.getSourceSampleRate()) continue; | 
| Chris@43 | 1487 | 
| Chris@43 | 1488 	bool playing = s.m_playing; | 
| Chris@43 | 1489 | 
| Chris@43 | 1490 	if (playing && !previouslyPlaying) { | 
| Chris@43 | 1491 #ifdef DEBUG_AUDIO_PLAY_SOURCE | 
| Chris@43 | 1492 	    std::cout << "AudioCallbackPlaySourceFillThread: playback state changed, resetting" << std::endl; | 
| Chris@43 | 1493 #endif | 
| Chris@43 | 1494 	    for (size_t c = 0; c < s.getTargetChannelCount(); ++c) { | 
| Chris@43 | 1495 		RingBuffer<float> *rb = s.getReadRingBuffer(c); | 
| Chris@43 | 1496 		if (rb) rb->reset(); | 
| Chris@43 | 1497 	    } | 
| Chris@43 | 1498 	} | 
| Chris@43 | 1499 	previouslyPlaying = playing; | 
| Chris@43 | 1500 | 
| Chris@43 | 1501 	work = s.fillBuffers(); | 
| Chris@43 | 1502     } | 
| Chris@43 | 1503 | 
| Chris@43 | 1504     s.m_mutex.unlock(); | 
| Chris@43 | 1505 } | 
| Chris@43 | 1506 |