annotate audioio/AudioGenerator.cpp @ 140:9ccaa8fd9b9f

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