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

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