annotate audioio/AudioCallbackPlaySource.cpp @ 191:3bd87e04f060

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