annotate audioio/AudioGenerator.cpp @ 23:cb23944ce9e2

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