annotate audioio/AudioGenerator.cpp @ 191:3bd87e04f060

* Move query for latency and other stream attributes from contextStateChanged to streamStateChanged (they did not work previously, as they were trying to query the stream too soon)
author Chris Cannam
date Tue, 15 Jun 2010 11:36:02 +0000
parents 0db3fc28a2a1
children 67cea66bd588
rev   line source
Chris@43 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@43 2
Chris@43 3 /*
Chris@43 4 Sonic Visualiser
Chris@43 5 An audio file viewer and annotation editor.
Chris@43 6 Centre for Digital Music, Queen Mary, University of London.
Chris@43 7 This file copyright 2006 Chris Cannam.
Chris@43 8
Chris@43 9 This program is free software; you can redistribute it and/or
Chris@43 10 modify it under the terms of the GNU General Public License as
Chris@43 11 published by the Free Software Foundation; either version 2 of the
Chris@43 12 License, or (at your option) any later version. See the file
Chris@43 13 COPYING included with this distribution for more information.
Chris@43 14 */
Chris@43 15
Chris@43 16 #include "AudioGenerator.h"
Chris@43 17
Chris@43 18 #include "base/TempDirectory.h"
Chris@43 19 #include "base/PlayParameters.h"
Chris@43 20 #include "base/PlayParameterRepository.h"
Chris@43 21 #include "base/Pitch.h"
Chris@43 22 #include "base/Exceptions.h"
Chris@43 23
Chris@43 24 #include "data/model/NoteModel.h"
Chris@43 25 #include "data/model/DenseTimeValueModel.h"
Chris@43 26 #include "data/model/SparseOneDimensionalModel.h"
Chris@43 27
Chris@43 28 #include "plugin/RealTimePluginFactory.h"
Chris@43 29 #include "plugin/RealTimePluginInstance.h"
Chris@43 30 #include "plugin/PluginIdentifier.h"
Chris@43 31 #include "plugin/PluginXml.h"
Chris@43 32 #include "plugin/api/alsa/seq_event.h"
Chris@43 33
Chris@43 34 #include <iostream>
Chris@167 35 #include <cmath>
Chris@43 36
Chris@43 37 #include <QDir>
Chris@43 38 #include <QFile>
Chris@43 39
Chris@43 40 const size_t
Chris@43 41 AudioGenerator::m_pluginBlockSize = 2048;
Chris@43 42
Chris@43 43 QString
Chris@43 44 AudioGenerator::m_sampleDir = "";
Chris@43 45
Chris@43 46 //#define DEBUG_AUDIO_GENERATOR 1
Chris@43 47
Chris@43 48 AudioGenerator::AudioGenerator() :
Chris@43 49 m_sourceSampleRate(0),
Chris@43 50 m_targetChannelCount(1),
Chris@43 51 m_soloing(false)
Chris@43 52 {
Chris@108 53 initialiseSampleDir();
Chris@43 54
Chris@43 55 connect(PlayParameterRepository::getInstance(),
Chris@108 56 SIGNAL(playPluginIdChanged(const Playable *, QString)),
Chris@43 57 this,
Chris@108 58 SLOT(playPluginIdChanged(const Playable *, QString)));
Chris@108 59
Chris@108 60 connect(PlayParameterRepository::getInstance(),
Chris@108 61 SIGNAL(playPluginConfigurationChanged(const Playable *, QString)),
Chris@108 62 this,
Chris@108 63 SLOT(playPluginConfigurationChanged(const Playable *, QString)));
Chris@43 64 }
Chris@43 65
Chris@43 66 AudioGenerator::~AudioGenerator()
Chris@43 67 {
Chris@177 68 #ifdef DEBUG_AUDIO_GENERATOR
Chris@177 69 std::cerr << "AudioGenerator::~AudioGenerator" << std::endl;
Chris@177 70 #endif
Chris@43 71 }
Chris@43 72
Chris@108 73 void
Chris@108 74 AudioGenerator::initialiseSampleDir()
Chris@43 75 {
Chris@108 76 if (m_sampleDir != "") return;
Chris@108 77
Chris@108 78 try {
Chris@108 79 m_sampleDir = TempDirectory::getInstance()->getSubDirectoryPath("samples");
Chris@108 80 } catch (DirectoryCreationFailed f) {
Chris@108 81 std::cerr << "WARNING: AudioGenerator::initialiseSampleDir:"
Chris@108 82 << " Failed to create temporary sample directory"
Chris@108 83 << std::endl;
Chris@108 84 m_sampleDir = "";
Chris@108 85 return;
Chris@108 86 }
Chris@108 87
Chris@108 88 QDir sampleResourceDir(":/samples", "*.wav");
Chris@108 89
Chris@108 90 for (unsigned int i = 0; i < sampleResourceDir.count(); ++i) {
Chris@108 91
Chris@108 92 QString fileName(sampleResourceDir[i]);
Chris@108 93 QFile file(sampleResourceDir.filePath(fileName));
Chris@151 94 QString target = QDir(m_sampleDir).filePath(fileName);
Chris@108 95
Chris@151 96 if (!file.copy(target)) {
Chris@108 97 std::cerr << "WARNING: AudioGenerator::getSampleDir: "
Chris@108 98 << "Unable to copy " << fileName.toStdString()
Chris@108 99 << " into temporary directory \""
Chris@108 100 << m_sampleDir.toStdString() << "\"" << std::endl;
Chris@151 101 } else {
Chris@151 102 QFile tf(target);
Chris@151 103 tf.setPermissions(tf.permissions() |
Chris@151 104 QFile::WriteOwner |
Chris@151 105 QFile::WriteUser);
Chris@108 106 }
Chris@43 107 }
Chris@43 108 }
Chris@43 109
Chris@43 110 bool
Chris@43 111 AudioGenerator::addModel(Model *model)
Chris@43 112 {
Chris@43 113 if (m_sourceSampleRate == 0) {
Chris@43 114
Chris@43 115 m_sourceSampleRate = model->getSampleRate();
Chris@43 116
Chris@43 117 } else {
Chris@43 118
Chris@43 119 DenseTimeValueModel *dtvm =
Chris@43 120 dynamic_cast<DenseTimeValueModel *>(model);
Chris@43 121
Chris@43 122 if (dtvm) {
Chris@43 123 m_sourceSampleRate = model->getSampleRate();
Chris@43 124 return true;
Chris@43 125 }
Chris@43 126 }
Chris@43 127
Chris@43 128 RealTimePluginInstance *plugin = loadPluginFor(model);
Chris@43 129 if (plugin) {
Chris@43 130 QMutexLocker locker(&m_mutex);
Chris@43 131 m_synthMap[model] = plugin;
Chris@43 132 return true;
Chris@43 133 }
Chris@43 134
Chris@43 135 return false;
Chris@43 136 }
Chris@43 137
Chris@43 138 void
Chris@108 139 AudioGenerator::playPluginIdChanged(const Playable *playable, QString)
Chris@43 140 {
Chris@108 141 const Model *model = dynamic_cast<const Model *>(playable);
Chris@108 142 if (!model) {
Chris@108 143 std::cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable "
Chris@108 144 << playable << " is not a supported model type"
Chris@108 145 << std::endl;
Chris@108 146 return;
Chris@108 147 }
Chris@108 148
Chris@43 149 if (m_synthMap.find(model) == m_synthMap.end()) return;
Chris@43 150
Chris@43 151 RealTimePluginInstance *plugin = loadPluginFor(model);
Chris@43 152 if (plugin) {
Chris@43 153 QMutexLocker locker(&m_mutex);
Chris@43 154 delete m_synthMap[model];
Chris@43 155 m_synthMap[model] = plugin;
Chris@43 156 }
Chris@43 157 }
Chris@43 158
Chris@43 159 void
Chris@108 160 AudioGenerator::playPluginConfigurationChanged(const Playable *playable,
Chris@43 161 QString configurationXml)
Chris@43 162 {
Chris@43 163 // std::cerr << "AudioGenerator::playPluginConfigurationChanged" << std::endl;
Chris@43 164
Chris@108 165 const Model *model = dynamic_cast<const Model *>(playable);
Chris@108 166 if (!model) {
Chris@108 167 std::cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable "
Chris@108 168 << playable << " is not a supported model type"
Chris@108 169 << std::endl;
Chris@108 170 return;
Chris@108 171 }
Chris@108 172
Chris@43 173 if (m_synthMap.find(model) == m_synthMap.end()) {
Chris@43 174 std::cerr << "AudioGenerator::playPluginConfigurationChanged: We don't know about this plugin" << std::endl;
Chris@43 175 return;
Chris@43 176 }
Chris@43 177
Chris@43 178 RealTimePluginInstance *plugin = m_synthMap[model];
Chris@43 179 if (plugin) {
Chris@43 180 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@43 181 }
Chris@43 182 }
Chris@43 183
Chris@43 184 void
Chris@43 185 AudioGenerator::setSampleDir(RealTimePluginInstance *plugin)
Chris@43 186 {
Chris@108 187 if (m_sampleDir != "") {
Chris@108 188 plugin->configure("sampledir", m_sampleDir.toStdString());
Chris@108 189 }
Chris@43 190 }
Chris@43 191
Chris@43 192 RealTimePluginInstance *
Chris@43 193 AudioGenerator::loadPluginFor(const Model *model)
Chris@43 194 {
Chris@43 195 QString pluginId, configurationXml;
Chris@43 196
Chris@108 197 const Playable *playable = model;
Chris@108 198 if (!playable || !playable->canPlay()) return 0;
Chris@108 199
Chris@43 200 PlayParameters *parameters =
Chris@108 201 PlayParameterRepository::getInstance()->getPlayParameters(playable);
Chris@43 202 if (parameters) {
Chris@43 203 pluginId = parameters->getPlayPluginId();
Chris@43 204 configurationXml = parameters->getPlayPluginConfiguration();
Chris@43 205 }
Chris@43 206
Chris@43 207 if (pluginId == "") return 0;
Chris@43 208
Chris@43 209 RealTimePluginInstance *plugin = loadPlugin(pluginId, "");
Chris@43 210 if (!plugin) return 0;
Chris@43 211
Chris@43 212 if (configurationXml != "") {
Chris@43 213 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@179 214 setSampleDir(plugin);
Chris@43 215 }
Chris@43 216
Chris@108 217 configurationXml = PluginXml(plugin).toXmlString();
Chris@108 218
Chris@43 219 if (parameters) {
Chris@43 220 parameters->setPlayPluginId(pluginId);
Chris@43 221 parameters->setPlayPluginConfiguration(configurationXml);
Chris@43 222 }
Chris@43 223
Chris@43 224 return plugin;
Chris@43 225 }
Chris@43 226
Chris@43 227 RealTimePluginInstance *
Chris@43 228 AudioGenerator::loadPlugin(QString pluginId, QString program)
Chris@43 229 {
Chris@43 230 RealTimePluginFactory *factory =
Chris@43 231 RealTimePluginFactory::instanceFor(pluginId);
Chris@43 232
Chris@43 233 if (!factory) {
Chris@43 234 std::cerr << "Failed to get plugin factory" << std::endl;
Chris@43 235 return false;
Chris@43 236 }
Chris@43 237
Chris@43 238 RealTimePluginInstance *instance =
Chris@43 239 factory->instantiatePlugin
Chris@43 240 (pluginId, 0, 0, m_sourceSampleRate, m_pluginBlockSize, m_targetChannelCount);
Chris@43 241
Chris@43 242 if (!instance) {
Chris@43 243 std::cerr << "Failed to instantiate plugin " << pluginId.toStdString() << std::endl;
Chris@43 244 return 0;
Chris@43 245 }
Chris@43 246
Chris@43 247 setSampleDir(instance);
Chris@43 248
Chris@43 249 for (unsigned int i = 0; i < instance->getParameterCount(); ++i) {
Chris@43 250 instance->setParameterValue(i, instance->getParameterDefault(i));
Chris@43 251 }
Chris@43 252 std::string defaultProgram = instance->getProgram(0, 0);
Chris@43 253 if (defaultProgram != "") {
Chris@43 254 // std::cerr << "first selecting default program " << defaultProgram << std::endl;
Chris@43 255 instance->selectProgram(defaultProgram);
Chris@43 256 }
Chris@43 257 if (program != "") {
Chris@43 258 // std::cerr << "now selecting desired program " << program.toStdString() << std::endl;
Chris@43 259 instance->selectProgram(program.toStdString());
Chris@43 260 }
Chris@43 261 instance->setIdealChannelCount(m_targetChannelCount); // reset!
Chris@43 262
Chris@43 263 return instance;
Chris@43 264 }
Chris@43 265
Chris@43 266 void
Chris@43 267 AudioGenerator::removeModel(Model *model)
Chris@43 268 {
Chris@43 269 SparseOneDimensionalModel *sodm =
Chris@43 270 dynamic_cast<SparseOneDimensionalModel *>(model);
Chris@43 271 if (!sodm) return; // nothing to do
Chris@43 272
Chris@43 273 QMutexLocker locker(&m_mutex);
Chris@43 274
Chris@43 275 if (m_synthMap.find(sodm) == m_synthMap.end()) return;
Chris@43 276
Chris@43 277 RealTimePluginInstance *instance = m_synthMap[sodm];
Chris@43 278 m_synthMap.erase(sodm);
Chris@43 279 delete instance;
Chris@43 280 }
Chris@43 281
Chris@43 282 void
Chris@43 283 AudioGenerator::clearModels()
Chris@43 284 {
Chris@43 285 QMutexLocker locker(&m_mutex);
Chris@43 286 while (!m_synthMap.empty()) {
Chris@43 287 RealTimePluginInstance *instance = m_synthMap.begin()->second;
Chris@43 288 m_synthMap.erase(m_synthMap.begin());
Chris@43 289 delete instance;
Chris@43 290 }
Chris@43 291 }
Chris@43 292
Chris@43 293 void
Chris@43 294 AudioGenerator::reset()
Chris@43 295 {
Chris@43 296 QMutexLocker locker(&m_mutex);
Chris@43 297 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) {
Chris@43 298 if (i->second) {
Chris@43 299 i->second->silence();
Chris@43 300 i->second->discardEvents();
Chris@43 301 }
Chris@43 302 }
Chris@43 303
Chris@43 304 m_noteOffs.clear();
Chris@43 305 }
Chris@43 306
Chris@43 307 void
Chris@43 308 AudioGenerator::setTargetChannelCount(size_t targetChannelCount)
Chris@43 309 {
Chris@43 310 if (m_targetChannelCount == targetChannelCount) return;
Chris@43 311
Chris@43 312 // std::cerr << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << std::endl;
Chris@43 313
Chris@43 314 QMutexLocker locker(&m_mutex);
Chris@43 315 m_targetChannelCount = targetChannelCount;
Chris@43 316
Chris@43 317 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) {
Chris@43 318 if (i->second) i->second->setIdealChannelCount(targetChannelCount);
Chris@43 319 }
Chris@43 320 }
Chris@43 321
Chris@43 322 size_t
Chris@43 323 AudioGenerator::getBlockSize() const
Chris@43 324 {
Chris@43 325 return m_pluginBlockSize;
Chris@43 326 }
Chris@43 327
Chris@43 328 void
Chris@43 329 AudioGenerator::setSoloModelSet(std::set<Model *> s)
Chris@43 330 {
Chris@43 331 QMutexLocker locker(&m_mutex);
Chris@43 332
Chris@43 333 m_soloModelSet = s;
Chris@43 334 m_soloing = true;
Chris@43 335 }
Chris@43 336
Chris@43 337 void
Chris@43 338 AudioGenerator::clearSoloModelSet()
Chris@43 339 {
Chris@43 340 QMutexLocker locker(&m_mutex);
Chris@43 341
Chris@43 342 m_soloModelSet.clear();
Chris@43 343 m_soloing = false;
Chris@43 344 }
Chris@43 345
Chris@43 346 size_t
Chris@43 347 AudioGenerator::mixModel(Model *model, size_t startFrame, size_t frameCount,
Chris@43 348 float **buffer, size_t fadeIn, size_t fadeOut)
Chris@43 349 {
Chris@43 350 if (m_sourceSampleRate == 0) {
Chris@43 351 std::cerr << "WARNING: AudioGenerator::mixModel: No base source sample rate available" << std::endl;
Chris@43 352 return frameCount;
Chris@43 353 }
Chris@43 354
Chris@43 355 QMutexLocker locker(&m_mutex);
Chris@43 356
Chris@108 357 Playable *playable = model;
Chris@108 358 if (!playable || !playable->canPlay()) return frameCount;
Chris@108 359
Chris@43 360 PlayParameters *parameters =
Chris@108 361 PlayParameterRepository::getInstance()->getPlayParameters(playable);
Chris@43 362 if (!parameters) return frameCount;
Chris@43 363
Chris@43 364 bool playing = !parameters->isPlayMuted();
Chris@43 365 if (!playing) {
Chris@43 366 #ifdef DEBUG_AUDIO_GENERATOR
Chris@43 367 std::cout << "AudioGenerator::mixModel(" << model << "): muted" << std::endl;
Chris@43 368 #endif
Chris@43 369 return frameCount;
Chris@43 370 }
Chris@43 371
Chris@43 372 if (m_soloing) {
Chris@43 373 if (m_soloModelSet.find(model) == m_soloModelSet.end()) {
Chris@43 374 #ifdef DEBUG_AUDIO_GENERATOR
Chris@43 375 std::cout << "AudioGenerator::mixModel(" << model << "): not one of the solo'd models" << std::endl;
Chris@43 376 #endif
Chris@43 377 return frameCount;
Chris@43 378 }
Chris@43 379 }
Chris@43 380
Chris@43 381 float gain = parameters->getPlayGain();
Chris@43 382 float pan = parameters->getPlayPan();
Chris@43 383
Chris@43 384 DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
Chris@43 385 if (dtvm) {
Chris@43 386 return mixDenseTimeValueModel(dtvm, startFrame, frameCount,
Chris@43 387 buffer, gain, pan, fadeIn, fadeOut);
Chris@43 388 }
Chris@43 389
Chris@43 390 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
Chris@43 391 (model);
Chris@43 392 if (sodm) {
Chris@43 393 return mixSparseOneDimensionalModel(sodm, startFrame, frameCount,
Chris@43 394 buffer, gain, pan, fadeIn, fadeOut);
Chris@43 395 }
Chris@43 396
Chris@43 397 NoteModel *nm = dynamic_cast<NoteModel *>(model);
Chris@43 398 if (nm) {
Chris@43 399 return mixNoteModel(nm, startFrame, frameCount,
Chris@43 400 buffer, gain, pan, fadeIn, fadeOut);
Chris@43 401 }
Chris@43 402
Chris@43 403 return frameCount;
Chris@43 404 }
Chris@43 405
Chris@43 406 size_t
Chris@43 407 AudioGenerator::mixDenseTimeValueModel(DenseTimeValueModel *dtvm,
Chris@43 408 size_t startFrame, size_t frames,
Chris@43 409 float **buffer, float gain, float pan,
Chris@43 410 size_t fadeIn, size_t fadeOut)
Chris@43 411 {
Chris@80 412 static float **channelBuffer = 0;
Chris@80 413 static size_t channelBufSiz = 0;
Chris@80 414 static size_t channelBufCount = 0;
Chris@43 415
Chris@43 416 size_t totalFrames = frames + fadeIn/2 + fadeOut/2;
Chris@43 417
Chris@80 418 size_t modelChannels = dtvm->getChannelCount();
Chris@80 419
Chris@80 420 if (channelBufSiz < totalFrames || channelBufCount < modelChannels) {
Chris@80 421
Chris@80 422 for (size_t c = 0; c < channelBufCount; ++c) {
Chris@80 423 delete[] channelBuffer[c];
Chris@80 424 }
Chris@80 425
Chris@43 426 delete[] channelBuffer;
Chris@80 427 channelBuffer = new float *[modelChannels];
Chris@80 428
Chris@80 429 for (size_t c = 0; c < modelChannels; ++c) {
Chris@80 430 channelBuffer[c] = new float[totalFrames];
Chris@80 431 }
Chris@80 432
Chris@80 433 channelBufCount = modelChannels;
Chris@43 434 channelBufSiz = totalFrames;
Chris@43 435 }
Chris@80 436
Chris@43 437 size_t got = 0;
Chris@80 438
Chris@80 439 if (startFrame >= fadeIn/2) {
Chris@80 440 got = dtvm->getData(0, modelChannels - 1,
Chris@80 441 startFrame - fadeIn/2,
Chris@80 442 frames + fadeOut/2 + fadeIn/2,
Chris@80 443 channelBuffer);
Chris@80 444 } else {
Chris@80 445 size_t missing = fadeIn/2 - startFrame;
Chris@80 446
Chris@80 447 for (size_t c = 0; c < modelChannels; ++c) {
Chris@80 448 channelBuffer[c] += missing;
Chris@80 449 }
Chris@80 450
Chris@80 451 got = dtvm->getData(0, modelChannels - 1,
Chris@80 452 startFrame,
Chris@80 453 frames + fadeOut/2,
Chris@80 454 channelBuffer);
Chris@80 455
Chris@80 456 for (size_t c = 0; c < modelChannels; ++c) {
Chris@80 457 channelBuffer[c] -= missing;
Chris@80 458 }
Chris@80 459
Chris@80 460 got += missing;
Chris@80 461 }
Chris@43 462
Chris@43 463 for (size_t c = 0; c < m_targetChannelCount; ++c) {
Chris@43 464
Chris@80 465 size_t sourceChannel = (c % modelChannels);
Chris@43 466
Chris@43 467 // std::cerr << "mixing channel " << c << " from source channel " << sourceChannel << std::endl;
Chris@43 468
Chris@43 469 float channelGain = gain;
Chris@43 470 if (pan != 0.0) {
Chris@43 471 if (c == 0) {
Chris@43 472 if (pan > 0.0) channelGain *= 1.0 - pan;
Chris@43 473 } else {
Chris@43 474 if (pan < 0.0) channelGain *= pan + 1.0;
Chris@43 475 }
Chris@43 476 }
Chris@43 477
Chris@43 478 for (size_t i = 0; i < fadeIn/2; ++i) {
Chris@43 479 float *back = buffer[c];
Chris@43 480 back -= fadeIn/2;
Chris@80 481 back[i] += (channelGain * channelBuffer[sourceChannel][i] * i) / fadeIn;
Chris@43 482 }
Chris@43 483
Chris@43 484 for (size_t i = 0; i < frames + fadeOut/2; ++i) {
Chris@43 485 float mult = channelGain;
Chris@43 486 if (i < fadeIn/2) {
Chris@43 487 mult = (mult * i) / fadeIn;
Chris@43 488 }
Chris@43 489 if (i > frames - fadeOut/2) {
Chris@43 490 mult = (mult * ((frames + fadeOut/2) - i)) / fadeOut;
Chris@43 491 }
Chris@80 492 float val = channelBuffer[sourceChannel][i];
Chris@80 493 if (i >= got) val = 0.f;
Chris@80 494 buffer[c][i] += mult * val;
Chris@43 495 }
Chris@43 496 }
Chris@43 497
Chris@43 498 return got;
Chris@43 499 }
Chris@43 500
Chris@43 501 size_t
Chris@43 502 AudioGenerator::mixSparseOneDimensionalModel(SparseOneDimensionalModel *sodm,
Chris@43 503 size_t startFrame, size_t frames,
Chris@43 504 float **buffer, float gain, float pan,
Chris@43 505 size_t /* fadeIn */,
Chris@43 506 size_t /* fadeOut */)
Chris@43 507 {
Chris@43 508 RealTimePluginInstance *plugin = m_synthMap[sodm];
Chris@43 509 if (!plugin) return 0;
Chris@43 510
Chris@43 511 size_t latency = plugin->getLatency();
Chris@43 512 size_t blocks = frames / m_pluginBlockSize;
Chris@43 513
Chris@43 514 //!!! hang on -- the fact that the audio callback play source's
Chris@43 515 //buffer is a multiple of the plugin's buffer size doesn't mean
Chris@43 516 //that we always get called for a multiple of it here (because it
Chris@43 517 //also depends on the JACK block size). how should we ensure that
Chris@43 518 //all models write the same amount in to the mix, and that we
Chris@43 519 //always have a multiple of the plugin buffer size? I guess this
Chris@43 520 //class has to be queryable for the plugin buffer size & the
Chris@43 521 //callback play source has to use that as a multiple for all the
Chris@43 522 //calls to mixModel
Chris@43 523
Chris@43 524 size_t got = blocks * m_pluginBlockSize;
Chris@43 525
Chris@43 526 #ifdef DEBUG_AUDIO_GENERATOR
Chris@43 527 std::cout << "mixModel [sparse]: frames " << frames
Chris@43 528 << ", blocks " << blocks << std::endl;
Chris@43 529 #endif
Chris@43 530
Chris@43 531 snd_seq_event_t onEv;
Chris@43 532 onEv.type = SND_SEQ_EVENT_NOTEON;
Chris@43 533 onEv.data.note.channel = 0;
Chris@43 534 onEv.data.note.note = 64;
Chris@43 535 onEv.data.note.velocity = 100;
Chris@43 536
Chris@43 537 snd_seq_event_t offEv;
Chris@43 538 offEv.type = SND_SEQ_EVENT_NOTEOFF;
Chris@43 539 offEv.data.note.channel = 0;
Chris@43 540 offEv.data.note.velocity = 0;
Chris@43 541
Chris@43 542 NoteOffSet &noteOffs = m_noteOffs[sodm];
Chris@43 543
Chris@43 544 for (size_t i = 0; i < blocks; ++i) {
Chris@43 545
Chris@43 546 size_t reqStart = startFrame + i * m_pluginBlockSize;
Chris@43 547
Chris@43 548 SparseOneDimensionalModel::PointList points =
Chris@43 549 sodm->getPoints(reqStart + latency,
Chris@43 550 reqStart + latency + m_pluginBlockSize);
Chris@43 551
Chris@43 552 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
Chris@43 553 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
Chris@43 554
Chris@43 555 for (SparseOneDimensionalModel::PointList::iterator pli =
Chris@43 556 points.begin(); pli != points.end(); ++pli) {
Chris@43 557
Chris@43 558 size_t pliFrame = pli->frame;
Chris@43 559
Chris@43 560 if (pliFrame >= latency) pliFrame -= latency;
Chris@43 561
Chris@43 562 if (pliFrame < reqStart ||
Chris@43 563 pliFrame >= reqStart + m_pluginBlockSize) continue;
Chris@43 564
Chris@43 565 while (noteOffs.begin() != noteOffs.end() &&
Chris@43 566 noteOffs.begin()->frame <= pliFrame) {
Chris@43 567
Chris@43 568 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
Chris@43 569 (noteOffs.begin()->frame, m_sourceSampleRate);
Chris@43 570
Chris@43 571 offEv.data.note.note = noteOffs.begin()->pitch;
Chris@43 572
Chris@43 573 #ifdef DEBUG_AUDIO_GENERATOR
Chris@179 574 std::cerr << "mixModel [sparse]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl;
Chris@43 575 #endif
Chris@43 576
Chris@43 577 plugin->sendEvent(eventTime, &offEv);
Chris@43 578 noteOffs.erase(noteOffs.begin());
Chris@43 579 }
Chris@43 580
Chris@43 581 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
Chris@43 582 (pliFrame, m_sourceSampleRate);
Chris@43 583
Chris@43 584 plugin->sendEvent(eventTime, &onEv);
Chris@43 585
Chris@43 586 #ifdef DEBUG_AUDIO_GENERATOR
Chris@43 587 std::cout << "mixModel [sparse]: point at frame " << pliFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl;
Chris@43 588 #endif
Chris@43 589
Chris@43 590 size_t duration = 7000; // frames [for now]
Chris@43 591 NoteOff noff;
Chris@43 592 noff.pitch = onEv.data.note.note;
Chris@43 593 noff.frame = pliFrame + duration;
Chris@43 594 noteOffs.insert(noff);
Chris@43 595 }
Chris@43 596
Chris@43 597 while (noteOffs.begin() != noteOffs.end() &&
Chris@43 598 noteOffs.begin()->frame <=
Chris@43 599 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) {
Chris@43 600
Chris@43 601 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
Chris@43 602 (noteOffs.begin()->frame, m_sourceSampleRate);
Chris@43 603
Chris@43 604 offEv.data.note.note = noteOffs.begin()->pitch;
Chris@43 605
Chris@43 606 #ifdef DEBUG_AUDIO_GENERATOR
Chris@179 607 std::cerr << "mixModel [sparse]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl;
Chris@43 608 #endif
Chris@43 609
Chris@43 610 plugin->sendEvent(eventTime, &offEv);
Chris@43 611 noteOffs.erase(noteOffs.begin());
Chris@43 612 }
Chris@43 613
Chris@43 614 plugin->run(blockTime);
Chris@43 615 float **outs = plugin->getAudioOutputBuffers();
Chris@43 616
Chris@43 617 for (size_t c = 0; c < m_targetChannelCount; ++c) {
Chris@43 618 #ifdef DEBUG_AUDIO_GENERATOR
Chris@43 619 std::cout << "mixModel [sparse]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl;
Chris@43 620 #endif
Chris@43 621
Chris@43 622 size_t sourceChannel = (c % plugin->getAudioOutputCount());
Chris@43 623
Chris@43 624 float channelGain = gain;
Chris@43 625 if (pan != 0.0) {
Chris@43 626 if (c == 0) {
Chris@43 627 if (pan > 0.0) channelGain *= 1.0 - pan;
Chris@43 628 } else {
Chris@43 629 if (pan < 0.0) channelGain *= pan + 1.0;
Chris@43 630 }
Chris@43 631 }
Chris@43 632
Chris@43 633 for (size_t j = 0; j < m_pluginBlockSize; ++j) {
Chris@43 634 buffer[c][i * m_pluginBlockSize + j] +=
Chris@43 635 channelGain * outs[sourceChannel][j];
Chris@43 636 }
Chris@43 637 }
Chris@43 638 }
Chris@43 639
Chris@43 640 return got;
Chris@43 641 }
Chris@43 642
Chris@43 643
Chris@43 644 //!!! mucho duplication with above -- refactor
Chris@43 645 size_t
Chris@43 646 AudioGenerator::mixNoteModel(NoteModel *nm,
Chris@43 647 size_t startFrame, size_t frames,
Chris@43 648 float **buffer, float gain, float pan,
Chris@43 649 size_t /* fadeIn */,
Chris@43 650 size_t /* fadeOut */)
Chris@43 651 {
Chris@43 652 RealTimePluginInstance *plugin = m_synthMap[nm];
Chris@43 653 if (!plugin) return 0;
Chris@43 654
Chris@43 655 size_t latency = plugin->getLatency();
Chris@43 656 size_t blocks = frames / m_pluginBlockSize;
Chris@43 657
Chris@43 658 //!!! hang on -- the fact that the audio callback play source's
Chris@43 659 //buffer is a multiple of the plugin's buffer size doesn't mean
Chris@43 660 //that we always get called for a multiple of it here (because it
Chris@43 661 //also depends on the JACK block size). how should we ensure that
Chris@43 662 //all models write the same amount in to the mix, and that we
Chris@43 663 //always have a multiple of the plugin buffer size? I guess this
Chris@43 664 //class has to be queryable for the plugin buffer size & the
Chris@43 665 //callback play source has to use that as a multiple for all the
Chris@43 666 //calls to mixModel
Chris@43 667
Chris@43 668 size_t got = blocks * m_pluginBlockSize;
Chris@43 669
Chris@43 670 #ifdef DEBUG_AUDIO_GENERATOR
Chris@179 671 Vamp::RealTime startTime = Vamp::RealTime::frame2RealTime
Chris@179 672 (startFrame, m_sourceSampleRate);
Chris@179 673
Chris@179 674 std::cout << "mixModel [note]: frames " << frames << " from " << startFrame
Chris@179 675 << " (time " << startTime << "), blocks " << blocks << std::endl;
Chris@43 676 #endif
Chris@43 677
Chris@43 678 snd_seq_event_t onEv;
Chris@43 679 onEv.type = SND_SEQ_EVENT_NOTEON;
Chris@43 680 onEv.data.note.channel = 0;
Chris@43 681 onEv.data.note.note = 64;
Chris@43 682 onEv.data.note.velocity = 100;
Chris@43 683
Chris@43 684 snd_seq_event_t offEv;
Chris@43 685 offEv.type = SND_SEQ_EVENT_NOTEOFF;
Chris@43 686 offEv.data.note.channel = 0;
Chris@43 687 offEv.data.note.velocity = 0;
Chris@43 688
Chris@43 689 NoteOffSet &noteOffs = m_noteOffs[nm];
Chris@43 690
Chris@43 691 for (size_t i = 0; i < blocks; ++i) {
Chris@43 692
Chris@43 693 size_t reqStart = startFrame + i * m_pluginBlockSize;
Chris@43 694
Chris@43 695 NoteModel::PointList points =
Chris@43 696 nm->getPoints(reqStart + latency,
Chris@43 697 reqStart + latency + m_pluginBlockSize);
Chris@43 698
Chris@43 699 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
Chris@43 700 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
Chris@43 701
Chris@43 702 for (NoteModel::PointList::iterator pli =
Chris@43 703 points.begin(); pli != points.end(); ++pli) {
Chris@43 704
Chris@43 705 size_t pliFrame = pli->frame;
Chris@43 706
Chris@43 707 if (pliFrame >= latency) pliFrame -= latency;
Chris@43 708
Chris@43 709 if (pliFrame < reqStart ||
Chris@43 710 pliFrame >= reqStart + m_pluginBlockSize) continue;
Chris@43 711
Chris@43 712 while (noteOffs.begin() != noteOffs.end() &&
Chris@43 713 noteOffs.begin()->frame <= pliFrame) {
Chris@43 714
Chris@43 715 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
Chris@43 716 (noteOffs.begin()->frame, m_sourceSampleRate);
Chris@43 717
Chris@43 718 offEv.data.note.note = noteOffs.begin()->pitch;
Chris@43 719
Chris@43 720 #ifdef DEBUG_AUDIO_GENERATOR
Chris@179 721 std::cerr << "mixModel [note]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl;
Chris@43 722 #endif
Chris@43 723
Chris@43 724 plugin->sendEvent(eventTime, &offEv);
Chris@43 725 noteOffs.erase(noteOffs.begin());
Chris@43 726 }
Chris@43 727
Chris@43 728 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
Chris@43 729 (pliFrame, m_sourceSampleRate);
Chris@43 730
Chris@43 731 if (nm->getScaleUnits() == "Hz") {
Chris@43 732 onEv.data.note.note = Pitch::getPitchForFrequency(pli->value);
Chris@43 733 } else {
Chris@43 734 onEv.data.note.note = lrintf(pli->value);
Chris@43 735 }
Chris@43 736
Chris@61 737 if (pli->level > 0.f && pli->level <= 1.f) {
Chris@61 738 onEv.data.note.velocity = lrintf(pli->level * 127);
Chris@61 739 } else {
Chris@61 740 onEv.data.note.velocity = 100;
Chris@61 741 }
Chris@61 742
Chris@43 743 plugin->sendEvent(eventTime, &onEv);
Chris@43 744
Chris@43 745 #ifdef DEBUG_AUDIO_GENERATOR
Chris@179 746 std::cout << "mixModel [note]: point at frame " << pliFrame << ", pitch " << (int)onEv.data.note.note << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl;
Chris@43 747 #endif
Chris@43 748
Chris@43 749 size_t duration = pli->duration;
Chris@43 750 if (duration == 0 || duration == 1) {
Chris@43 751 duration = m_sourceSampleRate / 20;
Chris@43 752 }
Chris@43 753 NoteOff noff;
Chris@43 754 noff.pitch = onEv.data.note.note;
Chris@43 755 noff.frame = pliFrame + duration;
Chris@43 756 noteOffs.insert(noff);
Chris@179 757
Chris@179 758 #ifdef DEBUG_AUDIO_GENERATOR
Chris@179 759 std::cout << "mixModel [note]: recording note off at " << noff.frame << std::endl;
Chris@179 760 #endif
Chris@43 761 }
Chris@43 762
Chris@43 763 while (noteOffs.begin() != noteOffs.end() &&
Chris@43 764 noteOffs.begin()->frame <=
Chris@43 765 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) {
Chris@43 766
Chris@43 767 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
Chris@43 768 (noteOffs.begin()->frame, m_sourceSampleRate);
Chris@43 769
Chris@43 770 offEv.data.note.note = noteOffs.begin()->pitch;
Chris@43 771
Chris@43 772 #ifdef DEBUG_AUDIO_GENERATOR
Chris@179 773 std::cerr << "mixModel [note]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << std::endl;
Chris@43 774 #endif
Chris@43 775
Chris@43 776 plugin->sendEvent(eventTime, &offEv);
Chris@43 777 noteOffs.erase(noteOffs.begin());
Chris@43 778 }
Chris@43 779
Chris@43 780 plugin->run(blockTime);
Chris@43 781 float **outs = plugin->getAudioOutputBuffers();
Chris@43 782
Chris@43 783 for (size_t c = 0; c < m_targetChannelCount; ++c) {
Chris@43 784 #ifdef DEBUG_AUDIO_GENERATOR
Chris@43 785 std::cout << "mixModel [note]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl;
Chris@43 786 #endif
Chris@43 787
Chris@43 788 size_t sourceChannel = (c % plugin->getAudioOutputCount());
Chris@43 789
Chris@43 790 float channelGain = gain;
Chris@43 791 if (pan != 0.0) {
Chris@43 792 if (c == 0) {
Chris@43 793 if (pan > 0.0) channelGain *= 1.0 - pan;
Chris@43 794 } else {
Chris@43 795 if (pan < 0.0) channelGain *= pan + 1.0;
Chris@43 796 }
Chris@43 797 }
Chris@43 798
Chris@43 799 for (size_t j = 0; j < m_pluginBlockSize; ++j) {
Chris@43 800 buffer[c][i * m_pluginBlockSize + j] +=
Chris@43 801 channelGain * outs[sourceChannel][j];
Chris@43 802 }
Chris@43 803 }
Chris@43 804 }
Chris@43 805
Chris@43 806 return got;
Chris@43 807 }
Chris@43 808