annotate audioio/AudioGenerator.cpp @ 304:c837368b1faf

Remove OSC option from constructor -- call startOSCQueue() after construction if you want it. This needs to be handled this way in SV because we need to ask for network permission before opening the OSC port (that's the thing that gets the app blocked by e.g. Windows Firewall).
author Chris Cannam
date Thu, 12 Dec 2013 15:20:14 +0000
parents 933b5aed341a
children 14b1768e5406
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@233 69 SVDEBUG << "AudioGenerator::~AudioGenerator" << 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@293 81 cerr << "WARNING: AudioGenerator::initialiseSampleDir:"
Chris@108 82 << " Failed to create temporary sample directory"
Chris@293 83 << 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@293 97 cerr << "WARNING: AudioGenerator::getSampleDir: "
Chris@294 98 << "Unable to copy " << fileName
Chris@108 99 << " into temporary directory \""
Chris@293 100 << m_sampleDir << "\"" << 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@293 143 cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable "
Chris@108 144 << playable << " is not a supported model type"
Chris@293 145 << 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@233 163 // SVDEBUG << "AudioGenerator::playPluginConfigurationChanged" << endl;
Chris@43 164
Chris@108 165 const Model *model = dynamic_cast<const Model *>(playable);
Chris@108 166 if (!model) {
Chris@293 167 cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable "
Chris@108 168 << playable << " is not a supported model type"
Chris@293 169 << endl;
Chris@108 170 return;
Chris@108 171 }
Chris@108 172
Chris@43 173 if (m_synthMap.find(model) == m_synthMap.end()) {
Chris@233 174 SVDEBUG << "AudioGenerator::playPluginConfigurationChanged: We don't know about this plugin" << 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@293 234 cerr << "Failed to get plugin factory" << endl;
Chris@274 235 return 0;
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@293 243 cerr << "Failed to instantiate plugin " << pluginId << 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@293 254 // cerr << "first selecting default program " << defaultProgram << endl;
Chris@43 255 instance->selectProgram(defaultProgram);
Chris@43 256 }
Chris@43 257 if (program != "") {
Chris@293 258 // cerr << "now selecting desired program " << program << 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@233 312 // SVDEBUG << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << 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@293 351 cerr << "WARNING: AudioGenerator::mixModel: No base source sample rate available" << 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@293 367 cout << "AudioGenerator::mixModel(" << model << "): muted" << 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@293 375 cout << "AudioGenerator::mixModel(" << model << "): not one of the solo'd models" << 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@279 390 bool synthetic =
Chris@279 391 (qobject_cast<SparseOneDimensionalModel *>(model) ||
Chris@279 392 qobject_cast<NoteModel *>(model));
Chris@43 393
Chris@279 394 if (synthetic) {
Chris@275 395 return mixSyntheticNoteModel(model, startFrame, frameCount,
Chris@275 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@233 463 // SVDEBUG << "mixing channel " << c << " from source channel " << sourceChannel << 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@275 498 AudioGenerator::mixSyntheticNoteModel(Model *model,
Chris@275 499 size_t startFrame, size_t frames,
Chris@275 500 float **buffer, float gain, float pan,
Chris@275 501 size_t /* fadeIn */,
Chris@275 502 size_t /* fadeOut */)
Chris@43 503 {
Chris@275 504 RealTimePluginInstance *plugin = m_synthMap[model];
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@293 523 cout << "mixModel [synthetic note]: frames " << frames
Chris@293 524 << ", blocks " << blocks << 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
Chris@43 531 snd_seq_event_t offEv;
Chris@43 532 offEv.type = SND_SEQ_EVENT_NOTEOFF;
Chris@43 533 offEv.data.note.channel = 0;
Chris@43 534 offEv.data.note.velocity = 0;
Chris@43 535
Chris@275 536 NoteOffSet &noteOffs = m_noteOffs[model];
Chris@43 537
Chris@43 538 for (size_t i = 0; i < blocks; ++i) {
Chris@43 539
Chris@43 540 size_t reqStart = startFrame + i * m_pluginBlockSize;
Chris@43 541
Chris@275 542 NoteList notes = getNotes(model,
Chris@275 543 reqStart + latency,
Chris@275 544 reqStart + latency + m_pluginBlockSize);
Chris@43 545
Chris@43 546 Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
Chris@43 547 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
Chris@43 548
Chris@275 549 for (NoteList::const_iterator ni = notes.begin();
Chris@275 550 ni != notes.end(); ++ni) {
Chris@43 551
Chris@275 552 size_t noteFrame = ni->start;
Chris@43 553
Chris@275 554 if (noteFrame >= latency) noteFrame -= latency;
Chris@43 555
Chris@275 556 if (noteFrame < reqStart ||
Chris@275 557 noteFrame >= reqStart + m_pluginBlockSize) continue;
Chris@43 558
Chris@43 559 while (noteOffs.begin() != noteOffs.end() &&
Chris@275 560 noteOffs.begin()->frame <= noteFrame) {
Chris@43 561
Chris@43 562 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
Chris@43 563 (noteOffs.begin()->frame, m_sourceSampleRate);
Chris@43 564
Chris@43 565 offEv.data.note.note = noteOffs.begin()->pitch;
Chris@43 566
Chris@43 567 #ifdef DEBUG_AUDIO_GENERATOR
Chris@293 568 cerr << "mixModel [synthetic]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl;
Chris@43 569 #endif
Chris@43 570
Chris@43 571 plugin->sendEvent(eventTime, &offEv);
Chris@43 572 noteOffs.erase(noteOffs.begin());
Chris@43 573 }
Chris@43 574
Chris@43 575 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
Chris@275 576 (noteFrame, m_sourceSampleRate);
Chris@43 577
Chris@275 578 if (ni->isMidiPitchQuantized) {
Chris@275 579 onEv.data.note.note = ni->midiPitch;
Chris@275 580 } else {
Chris@275 581 #ifdef DEBUG_AUDIO_GENERATOR
Chris@293 582 cerr << "mixModel [synthetic]: non-pitch-quantized notes are not supported [yet], quantizing" << endl;
Chris@275 583 #endif
Chris@275 584 onEv.data.note.note = Pitch::getPitchForFrequency(ni->frequency);
Chris@275 585 }
Chris@275 586
Chris@275 587 onEv.data.note.velocity = ni->velocity;
Chris@275 588
Chris@43 589 plugin->sendEvent(eventTime, &onEv);
Chris@43 590
Chris@43 591 #ifdef DEBUG_AUDIO_GENERATOR
Chris@293 592 cout << "mixModel [synthetic]: note at frame " << noteFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << endl;
Chris@43 593 #endif
Chris@43 594
Chris@275 595 noteOffs.insert
Chris@275 596 (NoteOff(onEv.data.note.note, noteFrame + ni->duration));
Chris@43 597 }
Chris@43 598
Chris@43 599 while (noteOffs.begin() != noteOffs.end() &&
Chris@43 600 noteOffs.begin()->frame <=
Chris@43 601 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) {
Chris@43 602
Chris@43 603 Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
Chris@43 604 (noteOffs.begin()->frame, m_sourceSampleRate);
Chris@43 605
Chris@43 606 offEv.data.note.note = noteOffs.begin()->pitch;
Chris@43 607
Chris@43 608 #ifdef DEBUG_AUDIO_GENERATOR
Chris@293 609 cerr << "mixModel [synthetic]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl;
Chris@43 610 #endif
Chris@43 611
Chris@43 612 plugin->sendEvent(eventTime, &offEv);
Chris@43 613 noteOffs.erase(noteOffs.begin());
Chris@43 614 }
Chris@43 615
Chris@43 616 plugin->run(blockTime);
Chris@43 617 float **outs = plugin->getAudioOutputBuffers();
Chris@43 618
Chris@43 619 for (size_t c = 0; c < m_targetChannelCount; ++c) {
Chris@43 620 #ifdef DEBUG_AUDIO_GENERATOR
Chris@293 621 cout << "mixModel [synthetic]: adding " << m_pluginBlockSize << " samples from plugin output " << c << endl;
Chris@43 622 #endif
Chris@43 623
Chris@43 624 size_t sourceChannel = (c % plugin->getAudioOutputCount());
Chris@43 625
Chris@43 626 float channelGain = gain;
Chris@43 627 if (pan != 0.0) {
Chris@43 628 if (c == 0) {
Chris@43 629 if (pan > 0.0) channelGain *= 1.0 - pan;
Chris@43 630 } else {
Chris@43 631 if (pan < 0.0) channelGain *= pan + 1.0;
Chris@43 632 }
Chris@43 633 }
Chris@43 634
Chris@43 635 for (size_t j = 0; j < m_pluginBlockSize; ++j) {
Chris@43 636 buffer[c][i * m_pluginBlockSize + j] +=
Chris@43 637 channelGain * outs[sourceChannel][j];
Chris@43 638 }
Chris@43 639 }
Chris@43 640 }
Chris@43 641
Chris@43 642 return got;
Chris@43 643 }
Chris@43 644
Chris@275 645 AudioGenerator::NoteList
Chris@275 646 AudioGenerator::getNotes(Model *model,
Chris@275 647 size_t startFrame,
Chris@275 648 size_t endFrame)
Chris@43 649 {
Chris@275 650 NoteList notes;
Chris@43 651
Chris@275 652 SparseOneDimensionalModel *sodm =
Chris@275 653 qobject_cast<SparseOneDimensionalModel *>(model);
Chris@43 654
Chris@275 655 if (sodm) {
Chris@275 656
Chris@275 657 SparseOneDimensionalModel::PointList points =
Chris@275 658 sodm->getPoints(startFrame, endFrame);
Chris@43 659
Chris@275 660 for (SparseOneDimensionalModel::PointList::iterator pli =
Chris@43 661 points.begin(); pli != points.end(); ++pli) {
Chris@43 662
Chris@275 663 notes.push_back
Chris@275 664 (NoteData(pli->frame,
Chris@275 665 m_sourceSampleRate / 6, // arbitrary short duration
Chris@275 666 64, // default pitch
Chris@275 667 100)); // default velocity
Chris@275 668 }
Chris@43 669
Chris@275 670 return notes;
Chris@275 671 }
Chris@43 672
Chris@275 673 NoteModel *nm = qobject_cast<NoteModel *>(model);
Chris@43 674
Chris@275 675 if (nm) {
Chris@275 676
Chris@275 677 NoteModel::PointList points =
Chris@275 678 nm->getPoints(startFrame, endFrame);
Chris@43 679
Chris@275 680 for (NoteModel::PointList::iterator pli =
Chris@275 681 points.begin(); pli != points.end(); ++pli) {
Chris@43 682
Chris@43 683 size_t duration = pli->duration;
Chris@43 684 if (duration == 0 || duration == 1) {
Chris@43 685 duration = m_sourceSampleRate / 20;
Chris@43 686 }
Chris@179 687
Chris@275 688 int pitch = lrintf(pli->value);
Chris@43 689
Chris@275 690 int velocity = 100;
Chris@275 691 if (pli->level > 0.f && pli->level <= 1.f) {
Chris@275 692 velocity = lrintf(pli->level * 127);
Chris@275 693 }
Chris@43 694
Chris@275 695 NoteData note(pli->frame,
Chris@275 696 duration,
Chris@275 697 pitch,
Chris@275 698 velocity);
Chris@43 699
Chris@275 700 if (nm->getScaleUnits() == "Hz") {
Chris@275 701 note.frequency = pli->value;
Chris@275 702 note.isMidiPitchQuantized = false;
Chris@275 703 }
Chris@275 704
Chris@275 705 notes.push_back(note);
Chris@275 706 }
Chris@43 707
Chris@275 708 return notes;
Chris@43 709 }
Chris@43 710
Chris@275 711 return notes;
Chris@43 712 }
Chris@43 713