annotate audioio/AudioCallbackPlaySource.cpp @ 38:54287e5e7451 sv1-v0.9rc1 sv1-v0.9rc2

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