annotate audioio/AudioCallbackPlaySource.cpp @ 403:eb84b06301da

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