annotate audio/AudioCallbackPlaySource.cpp @ 562:3c846b06c518 3.0-integration

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