annotate audio/AudioCallbackPlaySource.cpp @ 738:48001ed9143b audio-source-refactor

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