annotate audioio/AudioCallbackPlaySource.cpp @ 285:32d8084f8543

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