annotate audioio/AudioCallbackPlaySource.cpp @ 77:0535c49069ba

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