comparison audio/AudioGenerator.cpp @ 682:161063152ddd by-id

Overhaul audio generator for ModelById
author Chris Cannam
date Tue, 02 Jul 2019 21:10:25 +0100
parents ed9cb577eb7c
children 610fa108fbcc
comparison
equal deleted inserted replaced
681:c7406ebcd51c 682:161063152ddd
54 m_channelBufCount(0) 54 m_channelBufCount(0)
55 { 55 {
56 initialiseSampleDir(); 56 initialiseSampleDir();
57 57
58 connect(PlayParameterRepository::getInstance(), 58 connect(PlayParameterRepository::getInstance(),
59 SIGNAL(playClipIdChanged(const Playable *, QString)), 59 SIGNAL(playClipIdChanged(int, QString)),
60 this, 60 this,
61 SLOT(playClipIdChanged(const Playable *, QString))); 61 SLOT(playClipIdChanged(int, QString)));
62 } 62 }
63 63
64 AudioGenerator::~AudioGenerator() 64 AudioGenerator::~AudioGenerator()
65 { 65 {
66 #ifdef DEBUG_AUDIO_GENERATOR 66 #ifdef DEBUG_AUDIO_GENERATOR
109 } 109 }
110 } 110 }
111 } 111 }
112 112
113 bool 113 bool
114 AudioGenerator::addModel(Model *model) 114 AudioGenerator::addModel(ModelId modelId)
115 { 115 {
116 auto model = ModelById::get(modelId);
117 if (!model) return false;
118 if (!model->canPlay()) return false;
119
116 if (m_sourceSampleRate == 0) { 120 if (m_sourceSampleRate == 0) {
117 121
118 m_sourceSampleRate = model->getSampleRate(); 122 m_sourceSampleRate = model->getSampleRate();
119 123
120 } else { 124 } else {
121 125
122 DenseTimeValueModel *dtvm = 126 auto dtvm = std::dynamic_pointer_cast<DenseTimeValueModel>(model);
123 dynamic_cast<DenseTimeValueModel *>(model);
124 127
125 if (dtvm) { 128 if (dtvm) {
126 m_sourceSampleRate = model->getSampleRate(); 129 m_sourceSampleRate = model->getSampleRate();
127 return true; 130 return true;
128 } 131 }
129 } 132 }
130 133
131 const Playable *playable = model;
132 if (!playable || !playable->canPlay()) return 0;
133
134 PlayParameters *parameters = 134 PlayParameters *parameters =
135 PlayParameterRepository::getInstance()->getPlayParameters(playable); 135 PlayParameterRepository::getInstance()->getPlayParameters
136 (modelId.untyped);
137
138 if (!parameters) {
139 SVCERR << "WARNING: Model with canPlay true is not known to PlayParameterRepository" << endl;
140 return false;
141 }
136 142
137 bool willPlay = !parameters->isPlayMuted(); 143 bool willPlay = !parameters->isPlayMuted();
138 144
139 if (usesClipMixer(model)) { 145 if (usesClipMixer(modelId)) {
140 ClipMixer *mixer = makeClipMixerFor(model); 146 ClipMixer *mixer = makeClipMixerFor(modelId);
141 if (mixer) { 147 if (mixer) {
142 QMutexLocker locker(&m_mutex); 148 QMutexLocker locker(&m_mutex);
143 m_clipMixerMap[model->getId()] = mixer; 149 m_clipMixerMap[modelId] = mixer;
144 return willPlay; 150 return willPlay;
145 } 151 }
146 } 152 }
147 153
148 if (usesContinuousSynth(model)) { 154 if (usesContinuousSynth(modelId)) {
149 ContinuousSynth *synth = makeSynthFor(model); 155 ContinuousSynth *synth = makeSynthFor(modelId);
150 if (synth) { 156 if (synth) {
151 QMutexLocker locker(&m_mutex); 157 QMutexLocker locker(&m_mutex);
152 m_continuousSynthMap[model->getId()] = synth; 158 m_continuousSynthMap[modelId] = synth;
153 return willPlay; 159 return willPlay;
154 } 160 }
155 } 161 }
156 162
157 return false; 163 return false;
158 } 164 }
159 165
160 void 166 void
161 AudioGenerator::playClipIdChanged(const Playable *playable, QString) 167 AudioGenerator::playClipIdChanged(int playableId, QString)
162 { 168 {
169 /*!!!
163 const Model *model = dynamic_cast<const Model *>(playable); 170 const Model *model = dynamic_cast<const Model *>(playable);
164 if (!model) { 171 if (!model) {
165 cerr << "WARNING: AudioGenerator::playClipIdChanged: playable " 172 cerr << "WARNING: AudioGenerator::playClipIdChanged: playable "
166 << playable << " is not a supported model type" 173 << playable << " is not a supported model type"
167 << endl; 174 << endl;
168 return; 175 return;
169 } 176 }
170 177 */
171 if (m_clipMixerMap.find(model->getId()) == m_clipMixerMap.end()) { 178 ModelId modelId;
179 modelId.untyped = playableId;
180
181 if (m_clipMixerMap.find(modelId) == m_clipMixerMap.end()) {
172 return; 182 return;
173 } 183 }
174 184
175 ClipMixer *mixer = makeClipMixerFor(model); 185 ClipMixer *mixer = makeClipMixerFor(modelId);
176 if (mixer) { 186 if (mixer) {
177 QMutexLocker locker(&m_mutex); 187 QMutexLocker locker(&m_mutex);
178 m_clipMixerMap[model->getId()] = mixer; 188 ClipMixer *oldMixer = m_clipMixerMap[modelId];
189 m_clipMixerMap[modelId] = mixer;
190 delete oldMixer;
179 } 191 }
180 } 192 }
181 193
182 bool 194 bool
183 AudioGenerator::usesClipMixer(const Model *model) 195 AudioGenerator::usesClipMixer(ModelId modelId)
184 { 196 {
185 bool clip = 197 bool clip =
186 (qobject_cast<const SparseOneDimensionalModel *>(model) || 198 (ModelById::isa<SparseOneDimensionalModel>(modelId) ||
187 qobject_cast<const NoteModel *>(model)); 199 ModelById::isa<NoteModel>(modelId));
188 return clip; 200 return clip;
189 } 201 }
190 202
191 bool 203 bool
192 AudioGenerator::wantsQuieterClips(const Model *model) 204 AudioGenerator::wantsQuieterClips(ModelId modelId)
193 { 205 {
194 // basically, anything that usually has sustain (like notes) or 206 // basically, anything that usually has sustain (like notes) or
195 // often has multiple sounds at once (like notes) wants to use a 207 // often has multiple sounds at once (like notes) wants to use a
196 // quieter level than simple click tracks 208 // quieter level than simple click tracks
197 bool does = (qobject_cast<const NoteModel *>(model)); 209 bool does = (ModelById::isa<NoteModel>(modelId));
198 return does; 210 return does;
199 } 211 }
200 212
201 bool 213 bool
202 AudioGenerator::usesContinuousSynth(const Model *model) 214 AudioGenerator::usesContinuousSynth(ModelId modelId)
203 { 215 {
204 bool cont = 216 bool cont = (ModelById::isa<SparseTimeValueModel>(modelId));
205 (qobject_cast<const SparseTimeValueModel *>(model));
206 return cont; 217 return cont;
207 } 218 }
208 219
209 ClipMixer * 220 ClipMixer *
210 AudioGenerator::makeClipMixerFor(const Model *model) 221 AudioGenerator::makeClipMixerFor(ModelId modelId)
211 { 222 {
212 QString clipId; 223 QString clipId;
213 224
214 const Playable *playable = model;
215 if (!playable || !playable->canPlay()) return nullptr;
216
217 PlayParameters *parameters = 225 PlayParameters *parameters =
218 PlayParameterRepository::getInstance()->getPlayParameters(playable); 226 PlayParameterRepository::getInstance()->getPlayParameters
227 (modelId.untyped);
219 if (parameters) { 228 if (parameters) {
220 clipId = parameters->getPlayClipId(); 229 clipId = parameters->getPlayClipId();
221 } 230 }
222 231
223 #ifdef DEBUG_AUDIO_GENERATOR 232 #ifdef DEBUG_AUDIO_GENERATOR
224 std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): sample id = " << clipId << std::endl; 233 std::cerr << "AudioGenerator::makeClipMixerFor(" << modelId << "): sample id = " << clipId << std::endl;
225 #endif 234 #endif
226 235
227 if (clipId == "") { 236 if (clipId == "") {
228 SVDEBUG << "AudioGenerator::makeClipMixerFor(" << model << "): no sample, skipping" << endl; 237 SVDEBUG << "AudioGenerator::makeClipMixerFor(" << modelId << "): no sample, skipping" << endl;
229 return nullptr; 238 return nullptr;
230 } 239 }
231 240
232 ClipMixer *mixer = new ClipMixer(m_targetChannelCount, 241 ClipMixer *mixer = new ClipMixer(m_targetChannelCount,
233 m_sourceSampleRate, 242 m_sourceSampleRate,
235 244
236 double clipF0 = Pitch::getFrequencyForPitch(60, 0, 440.0); // required 245 double clipF0 = Pitch::getFrequencyForPitch(60, 0, 440.0); // required
237 246
238 QString clipPath = QString("%1/%2.wav").arg(m_sampleDir).arg(clipId); 247 QString clipPath = QString("%1/%2.wav").arg(m_sampleDir).arg(clipId);
239 248
240 double level = wantsQuieterClips(model) ? 0.5 : 1.0; 249 double level = wantsQuieterClips(modelId) ? 0.5 : 1.0;
241 if (!mixer->loadClipData(clipPath, clipF0, level)) { 250 if (!mixer->loadClipData(clipPath, clipF0, level)) {
242 delete mixer; 251 delete mixer;
243 return nullptr; 252 return nullptr;
244 } 253 }
245 254
249 258
250 return mixer; 259 return mixer;
251 } 260 }
252 261
253 ContinuousSynth * 262 ContinuousSynth *
254 AudioGenerator::makeSynthFor(const Model *model) 263 AudioGenerator::makeSynthFor(ModelId)
255 { 264 {
256 const Playable *playable = model;
257 if (!playable || !playable->canPlay()) return nullptr;
258
259 ContinuousSynth *synth = new ContinuousSynth(m_targetChannelCount, 265 ContinuousSynth *synth = new ContinuousSynth(m_targetChannelCount,
260 m_sourceSampleRate, 266 m_sourceSampleRate,
261 m_processingBlockSize, 267 m_processingBlockSize,
262 m_waveType); 268 m_waveType);
263 269
267 273
268 return synth; 274 return synth;
269 } 275 }
270 276
271 void 277 void
272 AudioGenerator::removeModel(Model *model) 278 AudioGenerator::removeModel(ModelId modelId)
273 { 279 {
274 SparseOneDimensionalModel *sodm =
275 dynamic_cast<SparseOneDimensionalModel *>(model);
276 if (!sodm) return; // nothing to do
277
278 QMutexLocker locker(&m_mutex); 280 QMutexLocker locker(&m_mutex);
279 281
280 if (m_clipMixerMap.find(sodm->getId()) == m_clipMixerMap.end()) { 282 if (m_clipMixerMap.find(modelId) == m_clipMixerMap.end()) {
281 return; 283 return;
282 } 284 }
283 285
284 ClipMixer *mixer = m_clipMixerMap[sodm->getId()]; 286 ClipMixer *mixer = m_clipMixerMap[modelId];
285 m_clipMixerMap.erase(sodm->getId()); 287 m_clipMixerMap.erase(modelId);
286 delete mixer; 288 delete mixer;
287 } 289 }
288 290
289 void 291 void
290 AudioGenerator::clearModels() 292 AudioGenerator::clearModels()
337 { 339 {
338 return m_processingBlockSize; 340 return m_processingBlockSize;
339 } 341 }
340 342
341 void 343 void
342 AudioGenerator::setSoloModelSet(std::set<Model *> s) 344 AudioGenerator::setSoloModelSet(std::set<ModelId> s)
343 { 345 {
344 QMutexLocker locker(&m_mutex); 346 QMutexLocker locker(&m_mutex);
345 347
346 m_soloModelSet = s; 348 m_soloModelSet = s;
347 m_soloing = true; 349 m_soloing = true;
355 m_soloModelSet.clear(); 357 m_soloModelSet.clear();
356 m_soloing = false; 358 m_soloing = false;
357 } 359 }
358 360
359 sv_frame_t 361 sv_frame_t
360 AudioGenerator::mixModel(Model *model, 362 AudioGenerator::mixModel(ModelId modelId,
361 sv_frame_t startFrame, sv_frame_t frameCount, 363 sv_frame_t startFrame, sv_frame_t frameCount,
362 float **buffer, 364 float **buffer,
363 sv_frame_t fadeIn, sv_frame_t fadeOut) 365 sv_frame_t fadeIn, sv_frame_t fadeOut)
364 { 366 {
365 if (m_sourceSampleRate == 0) { 367 if (m_sourceSampleRate == 0) {
367 return frameCount; 369 return frameCount;
368 } 370 }
369 371
370 QMutexLocker locker(&m_mutex); 372 QMutexLocker locker(&m_mutex);
371 373
372 Playable *playable = model; 374 auto model = ModelById::get(modelId);
373 if (!playable || !playable->canPlay()) return frameCount; 375 if (!model || !model->canPlay()) return frameCount;
374 376
375 PlayParameters *parameters = 377 PlayParameters *parameters =
376 PlayParameterRepository::getInstance()->getPlayParameters(playable); 378 PlayParameterRepository::getInstance()->getPlayParameters
379 (modelId.untyped);
377 if (!parameters) return frameCount; 380 if (!parameters) return frameCount;
378 381
379 bool playing = !parameters->isPlayMuted(); 382 bool playing = !parameters->isPlayMuted();
380 if (!playing) { 383 if (!playing) {
381 #ifdef DEBUG_AUDIO_GENERATOR 384 #ifdef DEBUG_AUDIO_GENERATOR
382 cout << "AudioGenerator::mixModel(" << model << "): muted" << endl; 385 cout << "AudioGenerator::mixModel(" << modelId << "): muted" << endl;
383 #endif 386 #endif
384 return frameCount; 387 return frameCount;
385 } 388 }
386 389
387 if (m_soloing) { 390 if (m_soloing) {
388 if (m_soloModelSet.find(model) == m_soloModelSet.end()) { 391 if (m_soloModelSet.find(modelId) == m_soloModelSet.end()) {
389 #ifdef DEBUG_AUDIO_GENERATOR 392 #ifdef DEBUG_AUDIO_GENERATOR
390 cout << "AudioGenerator::mixModel(" << model << "): not one of the solo'd models" << endl; 393 cout << "AudioGenerator::mixModel(" << modelId << "): not one of the solo'd models" << endl;
391 #endif 394 #endif
392 return frameCount; 395 return frameCount;
393 } 396 }
394 } 397 }
395 398
396 float gain = parameters->getPlayGain(); 399 float gain = parameters->getPlayGain();
397 float pan = parameters->getPlayPan(); 400 float pan = parameters->getPlayPan();
398 401
399 DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); 402 if (std::dynamic_pointer_cast<DenseTimeValueModel>(model)) {
400 if (dtvm) { 403 return mixDenseTimeValueModel(modelId, startFrame, frameCount,
401 return mixDenseTimeValueModel(dtvm, startFrame, frameCount,
402 buffer, gain, pan, fadeIn, fadeOut); 404 buffer, gain, pan, fadeIn, fadeOut);
403 } 405 }
404 406
405 if (usesClipMixer(model)) { 407 if (usesClipMixer(modelId)) {
406 return mixClipModel(model, startFrame, frameCount, 408 return mixClipModel(modelId, startFrame, frameCount,
407 buffer, gain, pan); 409 buffer, gain, pan);
408 } 410 }
409 411
410 if (usesContinuousSynth(model)) { 412 if (usesContinuousSynth(modelId)) {
411 return mixContinuousSynthModel(model, startFrame, frameCount, 413 return mixContinuousSynthModel(modelId, startFrame, frameCount,
412 buffer, gain, pan); 414 buffer, gain, pan);
413 } 415 }
414 416
415 std::cerr << "AudioGenerator::mixModel: WARNING: Model " << model << " of type " << model->getTypeName() << " is marked as playable, but I have no mechanism to play it" << std::endl; 417 std::cerr << "AudioGenerator::mixModel: WARNING: Model " << modelId << " of type " << model->getTypeName() << " is marked as playable, but I have no mechanism to play it" << std::endl;
416 418
417 return frameCount; 419 return frameCount;
418 } 420 }
419 421
420 sv_frame_t 422 sv_frame_t
421 AudioGenerator::mixDenseTimeValueModel(DenseTimeValueModel *dtvm, 423 AudioGenerator::mixDenseTimeValueModel(ModelId modelId,
422 sv_frame_t startFrame, sv_frame_t frames, 424 sv_frame_t startFrame, sv_frame_t frames,
423 float **buffer, float gain, float pan, 425 float **buffer, float gain, float pan,
424 sv_frame_t fadeIn, sv_frame_t fadeOut) 426 sv_frame_t fadeIn, sv_frame_t fadeOut)
425 { 427 {
426 sv_frame_t maxFrames = frames + std::max(fadeIn, fadeOut); 428 sv_frame_t maxFrames = frames + std::max(fadeIn, fadeOut);
427 429
430 auto dtvm = ModelById::getAs<DenseTimeValueModel>(modelId);
431 if (!dtvm) return 0;
432
428 int modelChannels = dtvm->getChannelCount(); 433 int modelChannels = dtvm->getChannelCount();
429 434
430 if (m_channelBufSiz < maxFrames || m_channelBufCount < modelChannels) { 435 if (m_channelBufSiz < maxFrames || m_channelBufCount < modelChannels) {
431 436
432 for (int c = 0; c < m_channelBufCount; ++c) { 437 for (int c = 0; c < m_channelBufCount; ++c) {
517 522
518 return got; 523 return got;
519 } 524 }
520 525
521 sv_frame_t 526 sv_frame_t
522 AudioGenerator::mixClipModel(Model *model, 527 AudioGenerator::mixClipModel(ModelId modelId,
523 sv_frame_t startFrame, sv_frame_t frames, 528 sv_frame_t startFrame, sv_frame_t frames,
524 float **buffer, float gain, float pan) 529 float **buffer, float gain, float pan)
525 { 530 {
526 ClipMixer *clipMixer = m_clipMixerMap[model->getId()]; 531 ClipMixer *clipMixer = m_clipMixerMap[modelId];
527 if (!clipMixer) return 0; 532 if (!clipMixer) return 0;
528 533
534 auto exportable = ModelById::getAs<NoteExportable>(modelId);
535
529 int blocks = int(frames / m_processingBlockSize); 536 int blocks = int(frames / m_processingBlockSize);
530 537
531 //!!! todo: the below -- it matters 538 //!!! todo: the below -- it matters
532 539
533 //!!! hang on -- the fact that the audio callback play source's 540 //!!! hang on -- the fact that the audio callback play source's
549 #endif 556 #endif
550 557
551 ClipMixer::NoteStart on; 558 ClipMixer::NoteStart on;
552 ClipMixer::NoteEnd off; 559 ClipMixer::NoteEnd off;
553 560
554 NoteOffSet &noteOffs = m_noteOffs[model->getId()]; 561 NoteOffSet &noteOffs = m_noteOffs[modelId];
555 562
556 float **bufferIndexes = new float *[m_targetChannelCount]; 563 float **bufferIndexes = new float *[m_targetChannelCount];
557 564
558 //!!! + for first block, prime with notes already active 565 //!!! + for first block, prime with notes already active
559 566
560 for (int i = 0; i < blocks; ++i) { 567 for (int i = 0; i < blocks; ++i) {
561 568
562 sv_frame_t reqStart = startFrame + i * m_processingBlockSize; 569 sv_frame_t reqStart = startFrame + i * m_processingBlockSize;
563 570
564 NoteList notes; 571 NoteList notes;
565 NoteExportable *exportable = dynamic_cast<NoteExportable *>(model);
566 if (exportable) { 572 if (exportable) {
567 notes = exportable->getNotesStartingWithin(reqStart, 573 notes = exportable->getNotesStartingWithin(reqStart,
568 m_processingBlockSize); 574 m_processingBlockSize);
569 } 575 }
570 576
675 681
676 return got; 682 return got;
677 } 683 }
678 684
679 sv_frame_t 685 sv_frame_t
680 AudioGenerator::mixContinuousSynthModel(Model *model, 686 AudioGenerator::mixContinuousSynthModel(ModelId modelId,
681 sv_frame_t startFrame, 687 sv_frame_t startFrame,
682 sv_frame_t frames, 688 sv_frame_t frames,
683 float **buffer, 689 float **buffer,
684 float gain, 690 float gain,
685 float pan) 691 float pan)
686 { 692 {
687 ContinuousSynth *synth = m_continuousSynthMap[model->getId()]; 693 ContinuousSynth *synth = m_continuousSynthMap[modelId];
688 if (!synth) return 0; 694 if (!synth) return 0;
689 695
690 // only type we support here at the moment 696 // only type we support here at the moment
691 SparseTimeValueModel *stvm = qobject_cast<SparseTimeValueModel *>(model); 697 auto stvm = ModelById::getAs<SparseTimeValueModel>(modelId);
698 if (!stvm) return 0;
692 if (stvm->getScaleUnits() != "Hz") return 0; 699 if (stvm->getScaleUnits() != "Hz") return 0;
693 700
694 int blocks = int(frames / m_processingBlockSize); 701 int blocks = int(frames / m_processingBlockSize);
695 702
696 //!!! todo: see comment in mixClipModel 703 //!!! todo: see comment in mixClipModel