annotate audioio/AudioCallbackPlaySource.cpp @ 141:9a8c73ffdce0

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