annotate audioio/AudioCallbackPlaySource.cpp @ 25:e74f508db18c

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