Mercurial > hg > svapp
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 ¬eOffs = m_noteOffs[model->getId()]; | 561 NoteOffSet ¬eOffs = 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 |