annotate audio/AudioCallbackPlaySource.cpp @ 570:6f54789f3127 3.0-integration

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