Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2022 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "musesamplerwrapper.h"
24 :
25 : #include <cstring>
26 :
27 : #include "audio/common/audioerrors.h"
28 :
29 : #include "global/serialization/json.h"
30 : #include "global/realfn.h"
31 :
32 : using namespace muse;
33 : using namespace muse::audio;
34 : using namespace muse::musesampler;
35 :
36 : static constexpr int AUDIO_CHANNELS_COUNT = 2;
37 :
38 0 : static InputProcessingProgress::StatusInfo::StatusData parseStatusData(const std::string& json)
39 : {
40 0 : if (json.empty()) {
41 0 : return {};
42 : }
43 :
44 0 : std::string err;
45 0 : ByteArray ba(json.c_str());
46 0 : JsonDocument doc = JsonDocument::fromJson(ba, &err);
47 :
48 0 : if (!err.empty() || !doc.isObject()) {
49 0 : LOGE() << "JSON parse error: " << err << ", json: " << json;
50 0 : return {};
51 : }
52 :
53 0 : JsonObject obj = doc.rootObject();
54 0 : InputProcessingProgress::StatusInfo::StatusData data;
55 :
56 0 : if (obj.contains("libraryName")) {
57 0 : data["libraryName"] = obj.value("libraryName").toStdString();
58 : }
59 :
60 0 : if (obj.contains("date")) {
61 0 : data["date"] = obj.value("date").toStdString();
62 : }
63 :
64 0 : if (obj.contains("url")) {
65 0 : data["url"] = obj.value("url").toStdString();
66 : }
67 :
68 0 : return data;
69 0 : }
70 :
71 0 : MuseSamplerWrapper::MuseSamplerWrapper(MuseSamplerLibHandlerPtr samplerLib,
72 : const InstrumentInfo& instrument,
73 0 : const AudioSourceParams& params)
74 : : AbstractSynthesizer(params),
75 0 : m_samplerLib(samplerLib),
76 0 : m_instrument(instrument)
77 : {
78 0 : if (!m_samplerLib || !m_samplerLib->isValid()) {
79 0 : return;
80 : }
81 :
82 0 : m_sequencer.setOnOffStreamFlushed([this]() {
83 0 : m_allNotesOffRequested = true;
84 0 : });
85 0 : }
86 :
87 0 : MuseSamplerWrapper::~MuseSamplerWrapper()
88 : {
89 0 : if (!m_samplerLib || !m_sampler) {
90 0 : return;
91 : }
92 :
93 0 : if (m_inputProcessingProgress.isStarted) {
94 0 : m_inputProcessingProgress.finish((int)Ret::Code::Cancel);
95 : }
96 :
97 0 : m_samplerLib->destroy(m_sampler);
98 0 : }
99 :
100 0 : void MuseSamplerWrapper::setMode(const muse::audio::ProcessMode mode)
101 : {
102 0 : AbstractSynthesizer::setMode(mode);
103 :
104 0 : if (!m_samplerLib || !m_sampler) {
105 0 : return;
106 : }
107 :
108 0 : m_sequencer.updateMainStream();
109 :
110 0 : const bool isOffline = m_mode == ProcessMode::PlayingOffline;
111 :
112 : //! NOTE The export spec reaches the graph after offline mode has already been started at the
113 : //! driver rate, so restart it to hand the new rate to startOfflineMode.
114 0 : const bool offlineSampleRateChanged = m_offlineModeStarted && m_offlineSampleRate != m_outputSpec.sampleRate;
115 :
116 0 : if (m_offlineModeStarted && (!isOffline || offlineSampleRateChanged)) {
117 0 : m_samplerLib->stopOfflineMode(m_sampler);
118 0 : m_offlineModeStarted = false;
119 : }
120 :
121 0 : if (isOffline && !m_offlineModeStarted) {
122 0 : LOGI() << "Start offline mode, sampleRate: " << m_outputSpec.sampleRate;
123 0 : m_samplerLib->startOfflineMode(m_sampler, m_outputSpec.sampleRate);
124 0 : m_offlineModeStarted = true;
125 0 : m_offlineSampleRate = m_outputSpec.sampleRate;
126 : }
127 :
128 0 : setIsActive(isModePlaying(mode));
129 : }
130 :
131 0 : void MuseSamplerWrapper::setOutputSpec(const audio::OutputSpec& spec)
132 : {
133 0 : const bool isOffline = m_mode == ProcessMode::PlayingOffline;
134 0 : const bool shouldReinitSampler = !m_sampler
135 0 : || (m_outputSpec.sampleRate != spec.sampleRate && !isOffline)
136 0 : || (m_outputSpec.samplesPerChannel != spec.samplesPerChannel && !isOffline);
137 :
138 0 : if (shouldReinitSampler) {
139 0 : if (!initSampler(spec.sampleRate, spec.samplesPerChannel)) {
140 0 : return;
141 : }
142 : }
143 :
144 0 : m_outputSpec = spec;
145 :
146 0 : setMode(m_mode);
147 : }
148 :
149 0 : samples_t MuseSamplerWrapper::process(float* buffer, samples_t samplesPerChannel)
150 : {
151 0 : if (!m_samplerLib || !m_sampler) {
152 0 : return 0;
153 : }
154 :
155 0 : if (m_allNotesOffRequested) {
156 0 : m_samplerLib->allNotesOff(m_sampler);
157 0 : m_allNotesOffRequested = false;
158 : }
159 :
160 0 : prepareOutputBuffer(samplesPerChannel);
161 :
162 0 : bool active = isActive();
163 :
164 0 : if (!active) {
165 0 : msecs_t nextMicros = samplesToMsecs(samplesPerChannel, m_outputSpec.sampleRate);
166 0 : MuseSamplerSequencer::EventSequenceMap sequences = m_sequencer.movePlaybackForward(nextMicros);
167 :
168 0 : for (const auto& pair : sequences) {
169 0 : for (const MuseSamplerSequencer::EventType& event : pair.second) {
170 0 : handleAuditionEvents(event);
171 : }
172 : }
173 0 : }
174 :
175 0 : if (m_mode == ProcessMode::PlayingOffline) {
176 0 : if (m_samplerLib->processOffline(m_sampler, m_bus) != ms_Result_OK) {
177 0 : return 0;
178 : }
179 : } else {
180 0 : if (m_samplerLib->process(m_sampler, m_bus, m_currentPosition.samples()) != ms_Result_OK) {
181 0 : return 0;
182 : }
183 : }
184 :
185 0 : extractOutputSamples(samplesPerChannel, buffer);
186 :
187 0 : if (active) {
188 0 : m_currentPosition.forward(samplesPerChannel);
189 : }
190 :
191 0 : return samplesPerChannel;
192 : }
193 :
194 0 : std::string MuseSamplerWrapper::name() const
195 : {
196 0 : return "musesampler";
197 : }
198 :
199 0 : AudioSourceType MuseSamplerWrapper::type() const
200 : {
201 0 : return AudioSourceType::MuseSampler;
202 : }
203 :
204 0 : void MuseSamplerWrapper::flushSound()
205 : {
206 0 : m_sequencer.flushOffstream();
207 0 : m_allNotesOffRequested = true;
208 0 : }
209 :
210 0 : bool MuseSamplerWrapper::isValid() const
211 : {
212 0 : return m_samplerLib && m_sampler;
213 : }
214 :
215 0 : void MuseSamplerWrapper::setupSound(const mpe::PlaybackSetupData& setupData)
216 : {
217 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
218 0 : return;
219 : }
220 :
221 0 : m_tracks.clear();
222 :
223 0 : std::string scoreId = setupData.scoreId.value_or(std::string());
224 0 : if (!scoreId.empty()) {
225 0 : m_samplerLib->setScoreId(m_sampler, scoreId.c_str());
226 : }
227 :
228 0 : ms_Track track = addTrack();
229 0 : if (!track) {
230 0 : LOGE() << "Could not add track for instrument: " << m_instrument.instrumentId << "; falling back to MPE info";
231 0 : m_instrument = resolveInstrument(setupData);
232 :
233 0 : track = addTrack();
234 0 : if (!track) {
235 0 : LOGE() << "Could not init instrument: " << m_instrument.instrumentId;
236 0 : return;
237 : }
238 : }
239 :
240 0 : m_sequencer.init(m_samplerLib, m_sampler, this, resolveDefaultPresetCode(m_instrument));
241 :
242 0 : if (m_instrument.isValid() && m_instrument.isOnline) {
243 0 : setupOnlineSound();
244 : }
245 0 : }
246 :
247 0 : void MuseSamplerWrapper::setupEvents(const mpe::PlaybackData& playbackData)
248 : {
249 : ONLY_AUDIO_ENGINE_THREAD;
250 :
251 0 : m_sequencer.load(playbackData);
252 0 : }
253 :
254 0 : const mpe::PlaybackData& MuseSamplerWrapper::playbackData() const
255 : {
256 : ONLY_AUDIO_ENGINE_THREAD;
257 :
258 0 : return m_sequencer.playbackData();
259 : }
260 :
261 0 : const TrackList& MuseSamplerWrapper::allTracks() const
262 : {
263 0 : return m_tracks;
264 : }
265 :
266 0 : ms_Track MuseSamplerWrapper::addTrack()
267 : {
268 0 : TRACEFUNC;
269 :
270 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
271 0 : return nullptr;
272 : }
273 :
274 0 : ms_Track track = m_samplerLib->addTrack(m_sampler, m_instrument.instrumentId);
275 0 : if (track) {
276 0 : m_tracks.push_back(track);
277 : }
278 :
279 0 : return track;
280 0 : }
281 :
282 0 : muse::audio::TimePosition MuseSamplerWrapper::playbackPosition() const
283 : {
284 0 : return m_currentPosition;
285 : }
286 :
287 0 : void MuseSamplerWrapper::setPlaybackPosition(const muse::audio::TimePosition& position)
288 : {
289 0 : IF_ASSERT_FAILED(position.isValid()) {
290 0 : return;
291 : }
292 :
293 : //! NOTE Don't trust that msecs_t is used everywhere here,
294 : // in fact, usecs_t (microseconds) is stored there.
295 0 : const usecs_t usecs = muse::secs_to_usecs(position.time());
296 0 : m_sequencer.setPlaybackPosition(msecs_t(usecs.raw()));
297 :
298 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
299 0 : return;
300 : }
301 :
302 0 : if (m_currentPosition == position) {
303 0 : return;
304 : }
305 :
306 0 : m_currentPosition = position;
307 0 : m_pendingSetPosition = true;
308 :
309 0 : if (isActive() || m_instrument.isOnline) {
310 0 : doCurrentSetPosition();
311 : }
312 : }
313 :
314 0 : bool MuseSamplerWrapper::isActive() const
315 : {
316 0 : return m_sequencer.isActive();
317 : }
318 :
319 0 : void MuseSamplerWrapper::setIsActive(bool active)
320 : {
321 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
322 0 : return;
323 : }
324 :
325 0 : if (isActive() == active) {
326 0 : return;
327 : }
328 :
329 0 : if (active && m_pendingSetPosition) {
330 0 : doCurrentSetPosition();
331 : }
332 :
333 0 : m_sequencer.setActive(active);
334 0 : m_samplerLib->setPlaying(m_sampler, active);
335 : }
336 :
337 0 : bool MuseSamplerWrapper::initSampler(const sample_rate_t sampleRate, const samples_t blockSize)
338 : {
339 0 : TRACEFUNC;
340 :
341 0 : IF_ASSERT_FAILED(sampleRate != 0 && blockSize != 0) {
342 0 : return false;
343 : }
344 :
345 0 : IF_ASSERT_FAILED(m_samplerLib) {
346 0 : return false;
347 : }
348 :
349 0 : if (!m_sampler) {
350 0 : m_sampler = m_samplerLib->create();
351 0 : IF_ASSERT_FAILED(m_sampler) {
352 0 : return false;
353 : }
354 : }
355 :
356 0 : if (!m_samplerLib->initSampler(m_sampler, sampleRate, blockSize, AUDIO_CHANNELS_COUNT)) {
357 0 : LOGE() << "Unable to init MuseSampler, sampleRate: " << sampleRate << ", blockSize: " << blockSize;
358 0 : return false;
359 : } else {
360 0 : LOGI() << "Successfully initialized sampler, sampleRate: " << sampleRate << ", blockSize: " << blockSize;
361 : }
362 :
363 0 : prepareOutputBuffer(blockSize);
364 :
365 0 : return true;
366 0 : }
367 :
368 0 : void MuseSamplerWrapper::setupOnlineSound()
369 : {
370 0 : constexpr double AUTO_PROCESS_INTERVAL = 3.0;
371 0 : constexpr double NO_AUTO_PROCESS = -1.0; // interval < 0 -> no auto process
372 0 : const bool autoProcess = config()->autoProcessOnlineSoundsInBackground();
373 0 : const bool lazyProcess = config()->isLazyProcessingOfOnlineSoundsEnabled();
374 :
375 0 : m_samplerLib->setLazyRender(m_sampler, lazyProcess);
376 0 : m_sequencer.setUpdateMainStreamWhenInactive(autoProcess);
377 0 : m_samplerLib->setAutoRenderInterval(m_sampler, autoProcess ? AUTO_PROCESS_INTERVAL : NO_AUTO_PROCESS);
378 :
379 : //! NOTE: update progress on the worker thread
380 0 : m_renderingStateChanged.onReceive(this, [this](ms_RenderingRangeList list, int size) {
381 0 : updateRenderingProgress(list, size);
382 0 : });
383 :
384 0 : m_samplerLib->setRenderingStateChangedCallback(m_sampler, [](void* data, ms_RenderingRangeList list, int size) {
385 : //! NOTE: move call to the worker thread
386 0 : RenderingStateChangedChannel* channel = reinterpret_cast<RenderingStateChangedChannel*>(data);
387 0 : channel->send(list, size);
388 0 : }, &m_renderingStateChanged);
389 :
390 0 : config()->autoProcessOnlineSoundsInBackgroundChanged().onReceive(this, [this](bool on) {
391 0 : m_sequencer.setUpdateMainStreamWhenInactive(on);
392 0 : m_sequencer.updateMainStream();
393 0 : m_samplerLib->setAutoRenderInterval(m_sampler, on ? AUTO_PROCESS_INTERVAL : NO_AUTO_PROCESS);
394 0 : });
395 :
396 0 : config()->isLazyProcessingOfOnlineSoundsEnabledChanged().onReceive(this, [this](bool on) {
397 0 : m_samplerLib->setLazyRender(m_sampler, on);
398 0 : });
399 0 : }
400 :
401 0 : void MuseSamplerWrapper::updateRenderingProgress(ms_RenderingRangeList list, int size)
402 : {
403 : ONLY_AUDIO_ENGINE_THREAD;
404 :
405 0 : IF_ASSERT_FAILED(m_samplerLib) {
406 0 : return;
407 : }
408 :
409 0 : InputProcessingProgress::ChunkInfoList chunks;
410 0 : chunks.reserve(size);
411 :
412 0 : long long chunksDurationUs = 0;
413 0 : bool isRendering = false;
414 0 : m_hasPendingChunks = false;
415 :
416 : // Call it N + 1 times so that the sampler can delete the list to avoid memory leak
417 0 : for (int i = 0; i <= size; ++i) {
418 0 : const RenderRangeInfo info = m_samplerLib->getNextRenderProgressInfo(list);
419 0 : if (i == size) {
420 0 : break;
421 : }
422 :
423 0 : switch (info._state) {
424 0 : case ms_RenderingState_Rendering:
425 0 : isRendering = true;
426 0 : break;
427 0 : case ms_RenderingState_OutOfRange:
428 0 : m_hasPendingChunks = true;
429 0 : break;
430 0 : case ms_RenderingState_ErrorNetwork:
431 0 : m_renderingInfo.errorCode = (int)Err::OnlineSoundsProcessingError;
432 0 : m_renderingInfo.errorText = "Network error";
433 0 : break;
434 0 : case ms_RenderingState_ErrorRendering:
435 0 : m_renderingInfo.errorCode = (int)Err::OnlineSoundsProcessingError;
436 0 : m_renderingInfo.errorText = "Rendering error";
437 0 : break;
438 0 : case ms_RenderingState_ErrorFileIO:
439 0 : m_renderingInfo.errorCode = (int)Err::OnlineSoundsProcessingError;
440 0 : m_renderingInfo.errorText = "File IO error";
441 0 : break;
442 0 : case ms_RenderingState_ErrorTimeOut:
443 0 : m_renderingInfo.errorCode = (int)Err::OnlineSoundsProcessingError;
444 0 : m_renderingInfo.errorText = "Timeout";
445 0 : break;
446 0 : case ms_RenderingState_ErrorLimitReached:
447 0 : m_renderingInfo.errorCode = (int)Err::OnlineSoundsLimitReached;
448 0 : m_renderingInfo.errorText = "Limit reached";
449 0 : break;
450 : }
451 :
452 0 : if (info._error_message) {
453 0 : m_renderingInfo.errorData = info._error_message;
454 : }
455 :
456 : // Failed regions remain in the list, but should be excluded when
457 : // calculating the total remaining rendering duration
458 0 : if (info._state != ms_RenderingState_Rendering) {
459 0 : continue;
460 : }
461 :
462 0 : chunksDurationUs += info._end_us - info._start_us;
463 0 : chunks.push_back({ muse::usecs_to_secs(info._start_us), muse::usecs_to_secs(info._end_us) });
464 : }
465 :
466 : // Start progress
467 0 : if (!m_inputProcessingProgress.isStarted) {
468 : // Rendering has started on the sampler side, but it is not yet ready to report progress
469 0 : if ((chunksDurationUs <= 0 && isRendering) || size == 0) {
470 0 : return;
471 : }
472 :
473 0 : m_inputProcessingProgress.start();
474 : }
475 :
476 0 : m_renderingInfo.maxChunksDurationUs = std::max(m_renderingInfo.maxChunksDurationUs, chunksDurationUs);
477 :
478 0 : bool isChanged = false;
479 0 : if (m_renderingInfo.lastReceivedChunks != chunks) {
480 0 : m_renderingInfo.lastReceivedChunks = chunks;
481 0 : isChanged = true;
482 : }
483 :
484 : // Update percentage
485 0 : int64_t percentage = 0;
486 0 : if (m_renderingInfo.maxChunksDurationUs != 0) {
487 0 : percentage = std::lround(100.f - (float)chunksDurationUs / (float)m_renderingInfo.maxChunksDurationUs * 100.f);
488 : }
489 :
490 0 : if (percentage != m_renderingInfo.percentage) {
491 0 : m_renderingInfo.percentage = percentage;
492 0 : isChanged = true;
493 : }
494 :
495 0 : if (isChanged) {
496 0 : m_inputProcessingProgress.process(chunks, percentage, 100);
497 : }
498 :
499 0 : if (m_hasPendingChunks && !config()->isLazyProcessingOfOnlineSoundsEnabled()) {
500 0 : return;
501 : }
502 :
503 : // Finish progress
504 0 : if (chunksDurationUs <= 0) {
505 0 : m_inputProcessingProgress.finish(m_renderingInfo.errorCode,
506 0 : m_renderingInfo.errorText,
507 0 : parseStatusData(m_renderingInfo.errorData));
508 0 : m_renderingInfo.clear();
509 : }
510 0 : }
511 :
512 0 : InstrumentInfo MuseSamplerWrapper::resolveInstrument(const mpe::PlaybackSetupData& setupData) const
513 : {
514 0 : IF_ASSERT_FAILED(m_samplerLib) {
515 0 : return InstrumentInfo();
516 : }
517 :
518 0 : if (!setupData.musicXmlSoundId.has_value()) {
519 0 : return InstrumentInfo();
520 : }
521 :
522 0 : String soundId = setupData.toString();
523 :
524 0 : auto matchingInstrumentList = m_samplerLib->getMatchingInstrumentList(soundId.toAscii().constChar(),
525 : setupData.musicXmlSoundId->c_str());
526 :
527 0 : if (matchingInstrumentList == nullptr) {
528 0 : LOGE() << "Unable to get instrument list";
529 0 : return InstrumentInfo();
530 : } else {
531 0 : LOGD() << "Successfully got instrument list";
532 : }
533 :
534 : // TODO: display all of these in MuseScore, and let the user choose!
535 0 : while (auto instrument = m_samplerLib->getNextInstrument(matchingInstrumentList)) {
536 0 : InstrumentInfo info;
537 0 : info.instrumentId = m_samplerLib->getInstrumentId(instrument);
538 0 : info.msInstrument = instrument;
539 :
540 : // For now, hack to just choose first instrument:
541 0 : if (info.isValid()) {
542 0 : return info;
543 : }
544 0 : }
545 :
546 0 : LOGE() << "Unable to find sound for " << soundId;
547 :
548 0 : return InstrumentInfo();
549 0 : }
550 :
551 0 : std::string MuseSamplerWrapper::resolveDefaultPresetCode(const InstrumentInfo& instrument) const
552 : {
553 0 : if (!instrument.isValid()) {
554 0 : return std::string();
555 : }
556 :
557 0 : ms_PresetList presets = m_samplerLib->getPresetList(instrument.msInstrument);
558 :
559 0 : if (const char* presetCode = m_samplerLib->getNextPreset(presets)) {
560 0 : return presetCode;
561 : }
562 :
563 0 : return std::string();
564 : }
565 :
566 0 : void MuseSamplerWrapper::prepareOutputBuffer(const samples_t samples)
567 : {
568 0 : if (m_leftChannel.size() < samples) {
569 0 : m_leftChannel.resize(samples, 0.f);
570 : }
571 :
572 0 : if (m_rightChannel.size() < samples) {
573 0 : m_rightChannel.resize(samples, 0.f);
574 : }
575 :
576 0 : m_bus._num_channels = AUDIO_CHANNELS_COUNT;
577 0 : m_bus._num_data_pts = samples;
578 :
579 0 : m_internalBuffer[0] = m_leftChannel.data();
580 0 : m_internalBuffer[1] = m_rightChannel.data();
581 0 : m_bus._channels = m_internalBuffer.data();
582 0 : }
583 :
584 0 : void MuseSamplerWrapper::handleAuditionEvents(const MuseSamplerSequencer::EventType& event)
585 : {
586 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
587 0 : return;
588 : }
589 :
590 0 : if (std::holds_alternative<AuditionStartNoteEvent>(event)) {
591 0 : const AuditionStartNoteEvent& noteOn = std::get<AuditionStartNoteEvent>(event);
592 0 : IF_ASSERT_FAILED(noteOn.msTrack) {
593 0 : return;
594 : }
595 :
596 0 : m_samplerLib->startAuditionNote(m_sampler, noteOn.msTrack, noteOn.msEvent);
597 0 : return;
598 : }
599 :
600 0 : if (std::holds_alternative<AuditionStopNoteEvent>(event)) {
601 0 : const AuditionStopNoteEvent& noteOff = std::get<AuditionStopNoteEvent>(event);
602 0 : IF_ASSERT_FAILED(noteOff.msTrack) {
603 0 : return;
604 : }
605 :
606 0 : m_samplerLib->stopAuditionNote(m_sampler, noteOff.msTrack, noteOff.msEvent);
607 0 : return;
608 : }
609 :
610 0 : if (std::holds_alternative<AuditionCCEvent>(event)) {
611 0 : const AuditionCCEvent& ccEvent = std::get<AuditionCCEvent>(event);
612 0 : IF_ASSERT_FAILED(ccEvent.msTrack) {
613 0 : return;
614 : }
615 :
616 0 : m_samplerLib->addAuditionCCEvent(m_sampler, ccEvent.msTrack, ccEvent.cc, ccEvent.value);
617 0 : return;
618 : }
619 : }
620 :
621 0 : void MuseSamplerWrapper::doCurrentSetPosition()
622 : {
623 : //! NOTE: very CPU-intensive operation; should be called as infrequently as possible
624 0 : m_samplerLib->setPosition(m_sampler, m_currentPosition.samples());
625 0 : m_pendingSetPosition = false;
626 0 : }
627 :
628 0 : void MuseSamplerWrapper::extractOutputSamples(samples_t samples, float* output)
629 : {
630 0 : for (samples_t sampleIndex = 0; sampleIndex < samples; ++sampleIndex) {
631 0 : size_t offset = sampleIndex * m_bus._num_channels;
632 :
633 0 : for (audioch_t audioChannelIndex = 0; audioChannelIndex < m_bus._num_channels; ++audioChannelIndex) {
634 0 : float sample = m_bus._channels[audioChannelIndex][sampleIndex];
635 0 : output[offset + audioChannelIndex] += sample;
636 : }
637 : }
638 0 : }
639 :
640 0 : void MuseSamplerWrapper::prepareToPlay()
641 : {
642 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
643 0 : return;
644 : }
645 :
646 0 : m_sequencer.updateMainStream();
647 0 : doCurrentSetPosition();
648 :
649 0 : if (readyToPlay()) {
650 0 : m_checkReadyToPlayTimer.reset();
651 0 : return;
652 : }
653 :
654 0 : m_checkReadyToPlayTimer.reset(new Timer(std::chrono::microseconds(10000)));
655 0 : m_checkReadyToPlayTimer->onTimeout(this, [this]() {
656 0 : if (readyToPlay()) {
657 0 : m_readyToPlayChanged.notify();
658 0 : m_checkReadyToPlayTimer->stop();
659 : }
660 0 : });
661 :
662 0 : m_checkReadyToPlayTimer->start();
663 : }
664 :
665 0 : bool MuseSamplerWrapper::readyToPlay() const
666 : {
667 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
668 0 : return false;
669 : }
670 :
671 0 : return m_samplerLib->readyToPlay(m_sampler);
672 : }
673 :
674 0 : void MuseSamplerWrapper::processInput()
675 : {
676 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
677 0 : return;
678 : }
679 :
680 0 : m_sequencer.updateMainStream();
681 0 : m_samplerLib->triggerRender(m_sampler);
682 : }
683 :
684 0 : void MuseSamplerWrapper::clearCache()
685 : {
686 0 : IF_ASSERT_FAILED(m_samplerLib && m_sampler) {
687 0 : return;
688 : }
689 :
690 0 : m_samplerLib->clearOnlineCache(m_sampler);
691 0 : m_sequencer.updateMainStream();
692 0 : m_samplerLib->triggerRender(m_sampler);
693 : }
|