annotate audioio/AudioCallbackPlaySource.cpp @ 78:9918c8a3f904

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