annotate audioio/AudioCallbackPlaySource.cpp @ 332:151b7c5864e3 tonioni

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