annotate audioio/AudioCallbackPlaySource.cpp @ 60:7b71da2d0631

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