annotate audioio/AudioCallbackPlaySource.cpp @ 122:ab861544f998

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