LCOV - code coverage report
Current view: top level - muse/framework/musesampler/internal - musesamplerwrapper.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 369 0
Test Date: 2026-11-05 04:07:02

            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              : }
        

Generated by: LCOV version 2.0-1