annotate audioio/AudioCallbackPlaySource.cpp @ 35:06787742542a

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