annotate audioio/AudioCallbackPlaySource.cpp @ 201:de783e8ee5f0

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