annotate audioio/AudioGenerator.cpp @ 156:89737ffa4580

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