annotate audioio/AudioCallbackPlaySource.cpp @ 45:6b6bca31ad53

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