annotate audio/AudioCallbackPlaySource.cpp @ 725:16f6737fa557 spectrogram-export

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