LCOV - code coverage report
Current view: top level - muse/framework/audio/engine/internal - enginerpccontroller.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 32.4 % 540 175
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) 2025 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              : #include "enginerpccontroller.h"
      23              : 
      24              : #include "audio/common/audiosanitizer.h"
      25              : #include "audio/common/rpc/rpcpacker.h"
      26              : #include "audio/common/audioerrors.h"
      27              : 
      28              : #include "log.h"
      29              : 
      30              : //#define CHECK_METHODS_DURATION
      31              : 
      32              : #ifdef CHECK_METHODS_DURATION
      33              : #include <chrono>
      34              : #define BEGIN_METHOD_DURATION \
      35              :     auto _start_clock = std::chrono::high_resolution_clock::now();
      36              : #define END_METHOD_DURATION(method) \
      37              :     auto _end_clock = std::chrono::high_resolution_clock::now(); \
      38              :     auto _duration_us = std::chrono::duration_cast<std::chrono::microseconds>(_end_clock - _start_clock); \
      39              :     LOGDA() << rpc::to_string(method) << " duration: " << (_duration_us.count() / 1000.0) << " ms";
      40              : #else
      41              : #define BEGIN_METHOD_DURATION
      42              : #define END_METHOD_DURATION(method)
      43              : #endif
      44              : 
      45              : using namespace muse::audio::engine;
      46              : using namespace muse::audio::rpc;
      47              : 
      48            3 : EngineRpcController::EngineRpcController(IExecOperation* execOperation)
      49            6 :     : m_execOperation(execOperation)
      50              : {
      51            3 :     IF_ASSERT_FAILED(m_execOperation) {
      52            0 :         return;
      53              :     }
      54            3 : }
      55              : 
      56           14 : std::shared_ptr<IAudioContext> EngineRpcController::audioContext(rpc::CtxId ctxId) const
      57              : {
      58           14 :     IF_ASSERT_FAILED(ctxId > 0) {
      59            0 :         return nullptr;
      60              :     }
      61           14 :     auto audioContext = audioEngine()->context(static_cast<AudioCtxId>(ctxId));
      62           14 :     IF_ASSERT_FAILED(audioContext) {
      63            0 :         return nullptr;
      64              :     }
      65           14 :     return audioContext;
      66           14 : }
      67              : 
      68            3 : void EngineRpcController::init()
      69              : {
      70              :     ONLY_AUDIO_RPC_THREAD;
      71              : 
      72              :     // AudioEngine
      73            3 :     onLongRequest(GLOBAL_CTX_ID, MsgCode::SetOutputSpec, [this](const Msg& msg) {
      74            0 :         OutputSpec spec;
      75            0 :         IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, spec)) {
      76            0 :             return make_response_ret(msg, make_ret(Err::InvalidRpcData));
      77              :         }
      78            0 :         audioEngine()->setOutputSpec(spec);
      79              : 
      80            0 :         return make_response_ret(msg, make_ok());
      81              :     });
      82              : 
      83              :     // Soundfont
      84            3 :     onLongRequest(GLOBAL_CTX_ID, MsgCode::LoadSoundFonts, [this](const Msg& msg) {
      85            0 :         std::vector<synth::SoundFontUri> uris;
      86            0 :         IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, uris)) {
      87            0 :             return make_response_ret(msg, make_ret(Err::InvalidRpcData));
      88              :         }
      89              : 
      90            0 :         soundFontRepository()->loadSoundFonts(uris);
      91              : 
      92            0 :         return make_response_ret(msg, make_ok());
      93            0 :     });
      94              : 
      95            3 :     onLongRequest(GLOBAL_CTX_ID, MsgCode::AddSoundFont, [this](const Msg& msg) {
      96            0 :         synth::SoundFontUri uri;
      97            0 :         IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, uri)) {
      98            0 :             return make_response_ret(msg, make_ret(Err::InvalidRpcData));
      99              :         }
     100              : 
     101            0 :         soundFontRepository()->addSoundFont(uri);
     102              : 
     103            0 :         return make_response_ret(msg, make_ok());
     104            0 :     });
     105              : 
     106            3 :     onLongRequest(GLOBAL_CTX_ID, MsgCode::AddSoundFontData, [this](const Msg& msg) {
     107            0 :         synth::SoundFontUri uri;
     108            0 :         ByteArray data;
     109            0 :         IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, uri, data)) {
     110            0 :             return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     111              :         }
     112              : 
     113            0 :         soundFontRepository()->addSoundFontData(uri, data);
     114              : 
     115            0 :         return make_response_ret(msg, make_ok());
     116            0 :     });
     117              : 
     118              :     // Engine Conf
     119            3 :     onLongRequest(GLOBAL_CTX_ID, MsgCode::EngineConfigChanged, [this](const Msg& msg) {
     120            0 :         AudioEngineConfig conf;
     121            0 :         IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, conf)) {
     122            0 :             return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     123              :         }
     124              : 
     125            0 :         configuration()->setConfig(conf);
     126              : 
     127            0 :         return make_response_ret(msg, make_ok());
     128              :     });
     129              : 
     130              :     // AudioContext
     131              : 
     132              :     // Init / Deinit
     133            3 :     onLongRequest(GLOBAL_CTX_ID, MsgCode::ContextInit, [this](const Msg& msg) {
     134              :         ONLY_AUDIO_RPC_THREAD;
     135              : 
     136            4 :         rpc::CtxId ctxId = 0;
     137            4 :         IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, ctxId)) {
     138            0 :             return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     139              :         }
     140              : 
     141            4 :         RetVal<std::shared_ptr<IAudioContext> > ret = audioEngine()->addAudioContext(ctxId);
     142            4 :         if (!ret.ret || !ret.val) {
     143            0 :             return make_response_ret(msg, ret.ret);
     144              :         }
     145            4 :         auto acontext = ret.val;
     146              : 
     147              :         // Requests to audio context
     148              : 
     149              :         // Resources
     150            4 :         onQuickRequest(ctxId, MsgCode::GetAvailableInputResources, [this](const Msg& msg) {
     151              :             ONLY_AUDIO_RPC_THREAD;
     152            0 :             if (auto actx = audioContext(msg.ctxId)) {
     153            0 :                 AudioResourceMetaList list = actx->availableInputResources();
     154            0 :                 return make_response_ret(msg, RetVal<AudioResourceMetaList>::make_ok(list));
     155            0 :             } else {
     156            0 :                 return make_response_ret(msg, RetVal<AudioResourceMetaList>::make_ret(Err::InvalidContext));
     157            0 :             }
     158              :         });
     159              : 
     160            4 :         onQuickRequest(ctxId, MsgCode::GetAvailableSoundPresets, [this](const Msg& msg) {
     161              :             ONLY_AUDIO_RPC_THREAD;
     162            0 :             AudioResourceMeta meta;
     163            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, meta)) {
     164            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     165              :             }
     166            0 :             if (auto actx = audioContext(msg.ctxId)) {
     167            0 :                 SoundPresetList list = actx->availableSoundPresets(meta);
     168            0 :                 return make_response_ret(msg, RetVal<SoundPresetList>::make_ok(list));
     169            0 :             } else {
     170            0 :                 return make_response_ret(msg, RetVal<SoundPresetList>::make_ret(Err::InvalidContext));
     171            0 :             }
     172            0 :         });
     173              : 
     174            4 :         onQuickRequest(ctxId, MsgCode::GetAvailableOutputResources, [this](const Msg& msg) {
     175              :             ONLY_AUDIO_RPC_THREAD;
     176            0 :             if (auto actx = audioContext(msg.ctxId)) {
     177            0 :                 AudioResourceMetaList list = actx->availableOutputResources();
     178            0 :                 return make_response_ret(msg, RetVal<AudioResourceMetaList>::make_ok(list));
     179            0 :             } else {
     180            0 :                 return make_response_ret(msg, RetVal<AudioResourceMetaList>::make_ret(Err::InvalidContext));
     181            0 :             }
     182              :         });
     183              : 
     184              :         // Tracks
     185            4 :         onLongRequest(ctxId, MsgCode::AddTrackWithPlaybackData, [this](const Msg& msg) {
     186              :             ONLY_AUDIO_RPC_THREAD;
     187              : 
     188              :             using RetType = RetVal2<TrackId, TrackParams>;
     189              : 
     190            0 :             TrackName trackName;
     191            0 :             mpe::PlaybackData playbackData;
     192            0 :             TrackParams params;
     193            0 :             rpc::StreamId mainStreamId = 0;
     194            0 :             rpc::StreamId offStreamId = 0;
     195            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackName, playbackData, params, mainStreamId, offStreamId)) {
     196            0 :                 return make_response(msg, RpcPacker::pack(RetType::make_ret(Err::InvalidRpcData)));
     197              :             }
     198              : 
     199            0 :             RpcStreamExec mainExec = [this](const std::function<void()>& func) {
     200            0 :                 IF_ASSERT_FAILED(m_execOperation) {
     201            0 :                     func();
     202            0 :                     return;
     203              :                 }
     204            0 :                 m_execOperation->execOperation(OperationType::QuickOperation, func);
     205            0 :             };
     206              : 
     207            0 :             RpcStreamExec offExec = [this](const std::function<void()>& func) {
     208            0 :                 IF_ASSERT_FAILED(m_execOperation) {
     209            0 :                     func();
     210            0 :                     return;
     211              :                 }
     212            0 :                 m_execOperation->execOperation(OperationType::QuickOperation, func);
     213            0 :             };
     214              : 
     215            0 :             channel()->addReceiveStream(msg.ctxId, StreamName::PlaybackDataMainStream, mainStreamId, playbackData.mainStream, mainExec);
     216            0 :             channel()->addReceiveStream(msg.ctxId, StreamName::PlaybackDataOffStream, offStreamId, playbackData.offStream, offExec);
     217              : 
     218            0 :             auto addTrackAndSendResponse = [this](const Msg& msg, const TrackName& trackName,
     219              :                                                   const mpe::PlaybackData& playbackData, const TrackParams& params) {
     220            0 :                 if (auto actx = audioContext(msg.ctxId)) {
     221            0 :                     RetType ret = actx->addTrack(trackName, playbackData, params);
     222            0 :                     channel()->send(make_response(msg, RpcPacker::pack(ret)));
     223            0 :                 } else {
     224            0 :                     channel()->send(make_response(msg, RpcPacker::pack(RetType::make_ret(Err::InvalidContext))));
     225            0 :                 }
     226            0 :             };
     227              : 
     228              :             // Not Fluid
     229            0 :             if (!isResourceType(params.source.resourceMeta, AudioResourceType::FluidSoundfont)) {
     230            0 :                 addTrackAndSendResponse(msg, trackName, playbackData, params);
     231            0 :                 return make_response_delayed(msg);
     232              :             }
     233              : 
     234              :             // Fluid
     235            0 :             std::string sfname = params.source.resourceMeta.attributeVal(synth::SOUNDFONT_NAME_ATTRIBUTE).toStdString();
     236            0 :             if (sfname.empty()) {
     237            0 :                 sfname = params.source.resourceMeta.id;
     238              :             }
     239              : 
     240            0 :             if (soundFontRepository()->isSoundFontLoaded(sfname)) {
     241            0 :                 addTrackAndSendResponse(msg, trackName, playbackData, params);
     242            0 :                 return make_response_delayed(msg);
     243              :             }
     244              :             // Waiting for SF to load
     245            0 :             else if (soundFontRepository()->isLoadingSoundFonts()) {
     246            0 :                 LOGI() << "Waiting for SF to load, trackName: " << trackName << ", SF name: " << sfname;
     247            0 :                 m_pendingTracks[sfname].emplace_back(PendingTrack { msg, trackName, playbackData, params });
     248              : 
     249              :                 //! NOTE We subscribe for the first track for which a soundfont is not found.
     250              :                 //! When the notification is triggered, processing will be called for all tracks.
     251            0 :                 if (!m_soundFontsChangedSubscribed) {
     252            0 :                     m_soundFontsChangedSubscribed = true;
     253            0 :                     soundFontRepository()->soundFontsChanged().onNotify(this, [this, addTrackAndSendResponse]() {
     254            0 :                         std::vector<std::string> toRemove;
     255            0 :                         for (auto& p : m_pendingTracks) {
     256            0 :                             const std::string& sfname = p.first;
     257            0 :                             if (soundFontRepository()->isSoundFontLoaded(sfname)) {
     258            0 :                                 for (const PendingTrack& t : p.second) {
     259            0 :                                     addTrackAndSendResponse(t.msg, t.trackName, t.playbackData, t.params);
     260              :                                 }
     261            0 :                                 toRemove.push_back(sfname);
     262              :                             }
     263              :                         }
     264              : 
     265            0 :                         for (const std::string& sf : toRemove) {
     266            0 :                             m_pendingTracks.erase(sf);
     267              :                         }
     268              : 
     269            0 :                         if (m_pendingTracks.empty()) {
     270            0 :                             soundFontRepository()->soundFontsChanged().disconnect(this);
     271            0 :                             m_soundFontsChangedSubscribed = false;
     272              :                         }
     273            0 :                     });
     274              :                 }
     275              :             } else { // Attempt to add it anyway (most likely fallback will be used)
     276            0 :                 addTrackAndSendResponse(msg, trackName, playbackData, params);
     277              :             }
     278              : 
     279            0 :             return make_response_delayed(msg);
     280            0 :         });
     281              : 
     282            4 :         onLongRequest(ctxId, MsgCode::AddTrackWithIODevice, [this](const Msg& msg) {
     283              :             ONLY_AUDIO_RPC_THREAD;
     284              : 
     285              :             using RetType = RetVal2<TrackId, TrackParams>;
     286              : 
     287            0 :             TrackName trackName;
     288            0 :             uint64_t devicePtr = 0;
     289            0 :             TrackParams params;
     290            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackName, devicePtr, params)) {
     291            0 :                 return make_response(msg, RpcPacker::pack(RetType::make_ret(Err::InvalidRpcData)));
     292              :             }
     293            0 :             io::IODevice* device = reinterpret_cast<io::IODevice*>(devicePtr);
     294              : 
     295            0 :             if (auto actx = audioContext(msg.ctxId)) {
     296            0 :                 RetType ret = actx->addTrack(trackName, device, params);
     297            0 :                 return make_response(msg, RpcPacker::pack(ret));
     298            0 :             } else {
     299            0 :                 return make_response(msg, RpcPacker::pack(RetType::make_ret(Err::InvalidContext)));
     300            0 :             }
     301            0 :         });
     302              : 
     303            4 :         onLongRequest(ctxId, MsgCode::AddAuxTrack, [this](const Msg& msg) {
     304              :             ONLY_AUDIO_RPC_THREAD;
     305              : 
     306              :             using RetType = RetVal2<TrackId, TrackParams>;
     307              : 
     308            0 :             TrackName trackName;
     309            0 :             TrackParams params;
     310            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackName, params)) {
     311            0 :                 return make_response(msg, RpcPacker::pack(RetType::make_ret(Err::InvalidRpcData)));
     312              :             }
     313              : 
     314            0 :             if (auto actx = audioContext(msg.ctxId)) {
     315            0 :                 RetType ret = actx->addAuxTrack(trackName, params);
     316            0 :                 return make_response(msg, RpcPacker::pack(ret));
     317            0 :             } else {
     318            0 :                 return make_response(msg, RpcPacker::pack(RetType::make_ret(Err::InvalidContext)));
     319            0 :             }
     320            0 :         });
     321              : 
     322            4 :         onLongRequest(ctxId, MsgCode::RemoveTrack, [this](const Msg& msg) {
     323              :             ONLY_AUDIO_RPC_THREAD;
     324            0 :             TrackId trackId = 0;
     325            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
     326            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     327              :             }
     328              : 
     329            0 :             if (auto actx = audioContext(msg.ctxId)) {
     330            0 :                 actx->removeTrack(trackId);
     331            0 :                 return make_response_ret(msg, make_ok());
     332              :             } else {
     333            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     334            0 :             }
     335              :         });
     336              : 
     337            4 :         onLongRequest(ctxId, MsgCode::RemoveAllTracks, [this](const Msg& msg) {
     338              :             ONLY_AUDIO_RPC_THREAD;
     339            0 :             if (auto actx = audioContext(msg.ctxId)) {
     340            0 :                 actx->removeAllTracks();
     341            0 :                 return make_response_ret(msg, make_ok());
     342              :             } else {
     343            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     344            0 :             }
     345              :         });
     346              : 
     347            4 :         acontext->trackAdded().onReceive(this, [this, ctxId](TrackId trackId) {
     348            0 :             channel()->send(rpc::make_notification(ctxId, MsgCode::TrackAdded, RpcPacker::pack(trackId)));
     349            0 :         });
     350              : 
     351            4 :         acontext->trackRemoved().onReceive(this, [this, ctxId](TrackId trackId) {
     352            2 :             removeTrackStreams(ctxId, trackId);
     353            2 :             channel()->send(rpc::make_notification(ctxId, MsgCode::TrackRemoved, RpcPacker::pack(trackId)));
     354            2 :         });
     355              : 
     356            4 :         onQuickRequest(ctxId, MsgCode::GetTrackIdList, [this](const Msg& msg) {
     357              :             ONLY_AUDIO_RPC_THREAD;
     358            0 :             if (auto actx = audioContext(msg.ctxId)) {
     359            0 :                 RetVal<TrackIdList> ret = actx->trackIdList();
     360            0 :                 return make_response_ret(msg, ret);
     361            0 :             } else {
     362            0 :                 return make_response_ret(msg, RetVal<TrackIdList>::make_ret(Err::InvalidContext));
     363            0 :             }
     364              :         });
     365              : 
     366            4 :         onQuickRequest(ctxId, MsgCode::GetTrackName, [this](const Msg& msg) {
     367              :             ONLY_AUDIO_RPC_THREAD;
     368              : 
     369            0 :             TrackId trackId = 0;
     370            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
     371            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     372              :             }
     373              : 
     374            0 :             if (auto actx = audioContext(msg.ctxId)) {
     375            0 :                 RetVal<TrackName> ret = actx->trackName(trackId);
     376            0 :                 return make_response_ret(msg, ret);
     377            0 :             } else {
     378            0 :                 return make_response_ret(msg, RetVal<TrackName>::make_ret(Err::InvalidContext));
     379            0 :             }
     380              :         });
     381              : 
     382              :         // Params
     383              : 
     384            4 :         onQuickRequest(ctxId, MsgCode::GetTrackParams, [this](const Msg& msg) {
     385              :             ONLY_AUDIO_RPC_THREAD;
     386              : 
     387              :             using RetType = RetVal<TrackParams>;
     388              : 
     389            0 :             TrackId trackId = 0;
     390            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
     391            0 :                 return make_response_ret(msg, RetType::make_ret(Err::InvalidRpcData));
     392              :             }
     393              : 
     394            0 :             if (auto actx = audioContext(msg.ctxId)) {
     395            0 :                 RetType ret = actx->params(trackId);
     396            0 :                 return make_response_ret(msg, ret);
     397            0 :             } else {
     398            0 :                 return make_response_ret(msg, RetType::make_ret(Err::InvalidContext));
     399            0 :             }
     400              :         });
     401              : 
     402            4 :         onLongRequest(ctxId, MsgCode::SetSourceParams, [this](const Msg& msg) {
     403              :             ONLY_AUDIO_RPC_THREAD;
     404            0 :             TrackId trackId = 0;
     405            0 :             AudioSourceParams params;
     406            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId, params)) {
     407            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     408              :             }
     409              : 
     410            0 :             if (auto actx = audioContext(msg.ctxId)) {
     411            0 :                 actx->setSourceParams(trackId, params);
     412            0 :                 return make_response_ret(msg, make_ok());
     413              :             } else {
     414            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     415            0 :             }
     416            0 :         });
     417              : 
     418            4 :         onQuickRequest(ctxId, MsgCode::SetControlParams, [this](const Msg& msg) {
     419              :             ONLY_AUDIO_RPC_THREAD;
     420            0 :             TrackId trackId = 0;
     421            0 :             ControlParams params;
     422            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId, params)) {
     423            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     424              :             }
     425              : 
     426            0 :             if (auto actx = audioContext(msg.ctxId)) {
     427            0 :                 actx->setControlParams(trackId, params);
     428            0 :                 return make_response_ret(msg, make_ok());
     429              :             } else {
     430            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     431            0 :             }
     432            0 :         });
     433              : 
     434            4 :         onLongRequest(ctxId, MsgCode::SetFxChainParams, [this](const Msg& msg) {
     435              :             ONLY_AUDIO_RPC_THREAD;
     436            0 :             TrackId trackId = 0;
     437            0 :             AudioFxChain params;
     438            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId, params)) {
     439            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     440              :             }
     441              : 
     442            0 :             if (auto actx = audioContext(msg.ctxId)) {
     443            0 :                 actx->setFxChainParams(trackId, params);
     444            0 :                 return make_response_ret(msg, make_ok());
     445              :             } else {
     446            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     447            0 :             }
     448            0 :         });
     449              : 
     450            4 :         onLongRequest(ctxId, MsgCode::SetAuxSendsParams, [this](const Msg& msg) {
     451              :             ONLY_AUDIO_RPC_THREAD;
     452            0 :             TrackId trackId = 0;
     453            0 :             AuxSendsParams params;
     454            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId, params)) {
     455            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     456              :             }
     457              : 
     458            0 :             if (auto actx = audioContext(msg.ctxId)) {
     459            0 :                 actx->setAuxSendsParams(trackId, params);
     460            0 :                 return make_response_ret(msg, make_ok());
     461              :             } else {
     462            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     463            0 :             }
     464            0 :         });
     465              : 
     466            4 :         acontext->fxChainParamsChanged().onReceive(this, [this, ctxId](TrackId trackId, const AudioFxChain& params) {
     467            0 :             channel()->send(rpc::make_notification(ctxId, MsgCode::FxChainParamsChanged, RpcPacker::pack(trackId, params)));
     468            0 :         });
     469              : 
     470            4 :         acontext->sourceParamsChanged().onReceive(this, [this, ctxId](TrackId trackId, const AudioSourceParams& params) {
     471            0 :             channel()->send(rpc::make_notification(ctxId, MsgCode::SourceParamsChanged, RpcPacker::pack(trackId, params)));
     472            0 :         });
     473              : 
     474            4 :         acontext->controlParamsChanged().onReceive(this, [this, ctxId](TrackId trackId, const ControlParams& params) {
     475            0 :             channel()->send(rpc::make_notification(ctxId, MsgCode::ControlParamsChanged, RpcPacker::pack(trackId, params)));
     476            0 :         });
     477              : 
     478            4 :         acontext->auxSendsParamsChanged().onReceive(this, [this, ctxId](TrackId trackId, const AuxSendsParams& params) {
     479            0 :             channel()->send(rpc::make_notification(ctxId, MsgCode::AuxSendsParamsChanged, RpcPacker::pack(trackId, params)));
     480            0 :         });
     481              : 
     482              :         // Input processing
     483            4 :         onLongRequest(ctxId, MsgCode::ProcessInput, [this](const Msg& msg) {
     484              :             ONLY_AUDIO_RPC_THREAD;
     485            0 :             TrackId trackId = 0;
     486            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
     487            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     488              :             }
     489              : 
     490            0 :             if (auto actx = audioContext(msg.ctxId)) {
     491            0 :                 actx->processInput(trackId);
     492            0 :                 return make_response_ret(msg, make_ok());
     493              :             } else {
     494            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     495            0 :             }
     496              :         });
     497              : 
     498            4 :         onQuickRequest(ctxId, MsgCode::GetInputProcessingProgress, [this](const Msg& msg) {
     499              :             ONLY_AUDIO_RPC_THREAD;
     500            1 :             TrackId trackId = 0;
     501            1 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
     502            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     503              :             }
     504              : 
     505            1 :             if (auto actx = audioContext(msg.ctxId)) {
     506            1 :                 RetVal<InputProcessingProgress> ret = actx->inputProcessingProgress(trackId);
     507            1 :                 StreamId streamId = 0;
     508            1 :                 if (ret.ret) {
     509            1 :                     streamId = channel()->addSendStream(msg.ctxId, StreamName::InputProcessingProgressStream, ret.val.processedChannel);
     510            1 :                     addTrackStream(msg.ctxId, trackId, streamId);
     511              :                 }
     512            1 :                 return make_response(msg, RpcPacker::pack(ret.ret, ret.val.isStarted, streamId));
     513            1 :             } else {
     514            0 :                 return make_response(msg, RpcPacker::pack(make_ret(Err::InvalidContext), false, 0));
     515            1 :             }
     516              :         });
     517              : 
     518            4 :         onLongRequest(ctxId, MsgCode::ClearCache, [this](const Msg& msg) {
     519              :             ONLY_AUDIO_RPC_THREAD;
     520            0 :             TrackId trackId = 0;
     521            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
     522            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     523              :             }
     524              : 
     525            0 :             if (auto actx = audioContext(msg.ctxId)) {
     526            0 :                 actx->clearCache(trackId);
     527            0 :                 return make_response_ret(msg, make_ok());
     528              :             } else {
     529            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     530            0 :             }
     531              :         });
     532              : 
     533            4 :         onLongRequest(ctxId, MsgCode::ClearSources, [this](const Msg& msg) {
     534              :             ONLY_AUDIO_RPC_THREAD;
     535            0 :             if (auto actx = audioContext(msg.ctxId)) {
     536            0 :                 actx->clearSources();
     537            0 :                 return make_response_ret(msg, make_ok());
     538              :             } else {
     539            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     540            0 :             }
     541              :         });
     542              : 
     543            4 :         onQuickRequest(ctxId, MsgCode::ClearMasterOutputParams, [this](const Msg& msg) {
     544              :             ONLY_AUDIO_RPC_THREAD;
     545            0 :             if (auto actx = audioContext(msg.ctxId)) {
     546            0 :                 actx->clearMasterOutputParams();
     547            0 :                 return make_response_ret(msg, make_ok());
     548              :             } else {
     549            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     550            0 :             }
     551              :         });
     552              : 
     553            4 :         onLongRequest(ctxId, MsgCode::ClearAllFx, [this](const Msg& msg) {
     554              :             ONLY_AUDIO_RPC_THREAD;
     555            0 :             if (auto actx = audioContext(msg.ctxId)) {
     556            0 :                 actx->clearAllFx();
     557            0 :                 return make_response_ret(msg, make_ok());
     558              :             } else {
     559            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     560            0 :             }
     561              :         });
     562              : 
     563              :         // Signals
     564            4 :         onQuickRequest(ctxId, MsgCode::GetSignalChanges, [this](const Msg& msg) {
     565              :             ONLY_AUDIO_RPC_THREAD;
     566            3 :             TrackId trackId = 0;
     567            3 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
     568            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     569              :             }
     570              : 
     571            3 :             if (auto actx = audioContext(msg.ctxId)) {
     572            3 :                 RetVal<AudioSignalChanges> ret = audioContext(msg.ctxId)->signalChanges(trackId);
     573            3 :                 StreamId streamId = 0;
     574            3 :                 if (ret.ret) {
     575            3 :                     streamId = channel()->addSendStream(msg.ctxId, StreamName::AudioSignalStream, ret.val);
     576            3 :                     addTrackStream(msg.ctxId, trackId, streamId);
     577              :                 }
     578              : 
     579            3 :                 RetVal<StreamId> res;
     580            3 :                 res.ret = ret.ret;
     581            3 :                 res.val = streamId;
     582            3 :                 return make_response_ret(msg, res);
     583            3 :             } else {
     584            0 :                 return make_response_ret(msg, RetVal<StreamId>::make_ret(Err::InvalidContext));
     585            3 :             }
     586              :         });
     587              : 
     588            4 :         onQuickRequest(ctxId, MsgCode::GetAutomatedControlParamsChanges, [this](const Msg& msg) {
     589              :             ONLY_AUDIO_RPC_THREAD;
     590            1 :             TrackId trackId = 0;
     591            1 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
     592            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     593              :             }
     594              : 
     595            1 :             if (auto actx = audioContext(msg.ctxId)) {
     596            1 :                 RetVal<AutomatedControlParamsChanges> ret = audioContext(msg.ctxId)->automatedControlParamsChanges(trackId);
     597            1 :                 StreamId streamId = 0;
     598            1 :                 if (ret.ret) {
     599            1 :                     streamId = channel()->addSendStream(msg.ctxId, StreamName::AutomatedControlParamsStream, ret.val);
     600            1 :                     addTrackStream(msg.ctxId, trackId, streamId);
     601              :                 }
     602              : 
     603            1 :                 RetVal<StreamId> res;
     604            1 :                 res.ret = ret.ret;
     605            1 :                 res.val = streamId;
     606            1 :                 return make_response_ret(msg, res);
     607            1 :             } else {
     608            0 :                 return make_response_ret(msg, RetVal<StreamId>::make_ret(Err::InvalidContext));
     609            1 :             }
     610              :         });
     611              : 
     612              :         // Play
     613            4 :         onQuickRequest(ctxId, MsgCode::PrepareToPlay, [this](const Msg& msg) {
     614              :             ONLY_AUDIO_RPC_THREAD;
     615            0 :             if (auto actx = audioContext(msg.ctxId)) {
     616            0 :                 actx->prepareToPlay().onResolve(this, [this, msg](const Ret& ret) {
     617            0 :                     channel()->send(make_response_ret(msg, ret));
     618            0 :                 });
     619            0 :                 return make_response_delayed(msg);
     620              :             } else {
     621            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     622            0 :             }
     623              :         });
     624              : 
     625            4 :         onQuickRequest(ctxId, MsgCode::Play, [this](const Msg& msg) {
     626              :             ONLY_AUDIO_RPC_THREAD;
     627            0 :             secs_t delay = 0;
     628            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, delay)) {
     629            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     630              :             }
     631              : 
     632            0 :             if (auto actx = audioContext(msg.ctxId)) {
     633            0 :                 actx->play(delay);
     634            0 :                 return make_response_ret(msg, make_ok());
     635              :             } else {
     636            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     637            0 :             }
     638              :         });
     639              : 
     640            4 :         onQuickRequest(ctxId, MsgCode::Seek, [this](const Msg& msg) {
     641              :             ONLY_AUDIO_RPC_THREAD;
     642            0 :             secs_t newPosition = 0;
     643            0 :             bool flushSound = false;
     644            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, newPosition, flushSound)) {
     645            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     646              :             }
     647            0 :             if (auto actx = audioContext(msg.ctxId)) {
     648            0 :                 actx->seek(newPosition, flushSound);
     649            0 :                 return make_response_ret(msg, make_ok());
     650              :             } else {
     651            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     652            0 :             }
     653              :         });
     654              : 
     655            4 :         onQuickRequest(ctxId, MsgCode::Stop, [this](const Msg& msg) {
     656              :             ONLY_AUDIO_RPC_THREAD;
     657            0 :             if (auto actx = audioContext(msg.ctxId)) {
     658            0 :                 actx->stop();
     659            0 :                 return make_response_ret(msg, make_ok());
     660              :             } else {
     661            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     662            0 :             }
     663              :         });
     664              : 
     665            4 :         onQuickRequest(ctxId, MsgCode::Pause, [this](const Msg& msg) {
     666              :             ONLY_AUDIO_RPC_THREAD;
     667            0 :             if (auto actx = audioContext(msg.ctxId)) {
     668            0 :                 actx->pause();
     669            0 :                 return make_response_ret(msg, make_ok());
     670              :             } else {
     671            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     672            0 :             }
     673              :         });
     674              : 
     675            4 :         onQuickRequest(ctxId, MsgCode::Resume, [this](const Msg& msg) {
     676              :             ONLY_AUDIO_RPC_THREAD;
     677            0 :             secs_t delay = 0;
     678            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, delay)) {
     679            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     680              :             }
     681            0 :             if (auto actx = audioContext(msg.ctxId)) {
     682            0 :                 actx->resume(delay);
     683            0 :                 return make_response_ret(msg, make_ok());
     684              :             } else {
     685            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     686            0 :             }
     687              :         });
     688              : 
     689            4 :         onQuickRequest(ctxId, MsgCode::SetDuration, [this](const Msg& msg) {
     690              :             ONLY_AUDIO_RPC_THREAD;
     691            0 :             secs_t duration = 0;
     692            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, duration)) {
     693            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     694              :             }
     695            0 :             if (auto actx = audioContext(msg.ctxId)) {
     696            0 :                 actx->setDuration(duration);
     697            0 :                 return make_response_ret(msg, make_ok());
     698              :             } else {
     699            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     700            0 :             }
     701              :         });
     702              : 
     703            4 :         onQuickRequest(ctxId, MsgCode::SetLoop, [this](const Msg& msg) {
     704              :             ONLY_AUDIO_RPC_THREAD;
     705            0 :             secs_t from = 0;
     706            0 :             secs_t to = 0;
     707            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, from, to)) {
     708            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     709              :             }
     710            0 :             if (auto actx = audioContext(msg.ctxId)) {
     711            0 :                 Ret ret = actx->setLoop(from, to);
     712            0 :                 return make_response_ret(msg, ret);
     713            0 :             } else {
     714            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     715            0 :             }
     716              :         });
     717              : 
     718            4 :         onQuickRequest(ctxId, MsgCode::ResetLoop, [this](const Msg& msg) {
     719              :             ONLY_AUDIO_RPC_THREAD;
     720            0 :             if (auto actx = audioContext(msg.ctxId)) {
     721            0 :                 actx->resetLoop();
     722            0 :                 return make_response_ret(msg, make_ok());
     723              :             } else {
     724            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     725            0 :             }
     726              :         });
     727              : 
     728            4 :         onQuickRequest(ctxId, MsgCode::GetPlaybackStatus, [this](const Msg& msg) {
     729              :             ONLY_AUDIO_RPC_THREAD;
     730              : 
     731            1 :             if (auto actx = audioContext(msg.ctxId)) {
     732            1 :                 PlaybackStatus status = actx->playbackStatus();
     733            1 :                 async::Channel<PlaybackStatus> ch = actx->playbackStatusChanged();
     734            1 :                 StreamId streamId = channel()->addSendStream(msg.ctxId, StreamName::PlaybackStatusStream, ch);
     735            1 :                 return make_response(msg, RpcPacker::pack(make_ok(), status, streamId));
     736            1 :             } else {
     737            0 :                 return make_response(msg, RpcPacker::pack(make_ret(Err::InvalidContext), PlaybackStatus::Stopped, 0));
     738            1 :             }
     739              :         });
     740              : 
     741            4 :         onQuickRequest(ctxId, MsgCode::GetPlaybackPosition, [this](const Msg& msg) {
     742              :             ONLY_AUDIO_RPC_THREAD;
     743              : 
     744            1 :             if (auto actx = audioContext(msg.ctxId)) {
     745            1 :                 secs_t pos = actx->playbackPosition();
     746            1 :                 async::Channel<secs_t> ch = actx->playbackPositionChanged();
     747            1 :                 StreamId streamId = channel()->addSendStream(msg.ctxId, StreamName::PlaybackPositionStream, ch);
     748            1 :                 return make_response(msg, RpcPacker::pack(make_ok(), pos, streamId));
     749            1 :             } else {
     750            0 :                 return make_response(msg, RpcPacker::pack(make_ret(Err::InvalidContext), 0, 0));
     751            1 :             }
     752              :         });
     753              : 
     754              :         // Export
     755              : 
     756            4 :         onLongRequest(ctxId, MsgCode::SaveSoundTrack, [this](const Msg& msg) {
     757              :             ONLY_AUDIO_RPC_THREAD;
     758            0 :             SoundTrackFormat format;
     759            0 :             uintptr_t dstDevicePtr = 0;
     760            0 :             IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, format, dstDevicePtr)) {
     761            0 :                 return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     762              :             }
     763            0 :             io::IODevice& dstDevice = *reinterpret_cast<io::IODevice*>(dstDevicePtr);
     764            0 :             if (auto actx = audioContext(msg.ctxId)) {
     765            0 :                 actx->saveSoundTrack(dstDevice, format).onResolve(this, [this, msg](const Ret& ret) {
     766            0 :                     channel()->send(make_response_ret(msg, ret));
     767            0 :                 });
     768            0 :                 return make_response_delayed(msg);
     769              :             } else {
     770            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     771            0 :             }
     772              :         });
     773              : 
     774            4 :         onLongRequest(ctxId, MsgCode::AbortSavingAllSoundTracks, [this](const Msg& msg) {
     775              :             ONLY_AUDIO_RPC_THREAD;
     776            0 :             if (auto actx = audioContext(msg.ctxId)) {
     777            0 :                 actx->abortSavingAllSoundTracks();
     778            0 :                 return make_response_ret(msg, make_ok());
     779              :             } else {
     780            0 :                 return make_response_ret(msg, make_ret(Err::InvalidContext));
     781            0 :             }
     782              :         });
     783              : 
     784            4 :         onQuickRequest(ctxId, MsgCode::GetSaveSoundTrackProgress, [this](const Msg& msg) {
     785              :             ONLY_AUDIO_RPC_THREAD;
     786            0 :             if (auto actx = audioContext(msg.ctxId)) {
     787            0 :                 SaveSoundTrackProgress ch = actx->saveSoundTrackProgressChanged();
     788            0 :                 ch.onReceive(this, [this](int64_t current, int64_t total, SaveSoundTrackStage stage) {
     789              :                     ONLY_AUDIO_RPC_THREAD;
     790            0 :                     m_saveSoundTrackProgressStream.send(current, total, stage);
     791            0 :                 });
     792              : 
     793            0 :                 if (m_saveSoundTrackProgressStreamId == 0) {
     794            0 :                     m_saveSoundTrackProgressStreamId = channel()->addSendStream(GLOBAL_CTX_ID, StreamName::SaveSoundTrackProgressStream,
     795            0 :                                                                                 m_saveSoundTrackProgressStream);
     796              :                 }
     797              : 
     798            0 :                 return make_response_ret(msg, RetVal<StreamId>::make_ok(m_saveSoundTrackProgressStreamId));
     799            0 :             } else {
     800            0 :                 return make_response_ret(msg, RetVal<StreamId>::make_ret(Err::InvalidContext));
     801            0 :             }
     802              :         });
     803              : 
     804              :         // response on ContextInit request
     805            4 :         return make_response_ret(msg, ret.ret);
     806            4 :     });
     807              : 
     808            3 :     onQuickRequest(GLOBAL_CTX_ID, MsgCode::ContextDeinit, [this](const Msg& msg) {
     809              :         ONLY_AUDIO_RPC_THREAD;
     810              : 
     811            3 :         rpc::CtxId ctxId = 0;
     812            3 :         IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, ctxId)) {
     813            0 :             return make_response_ret(msg, make_ret(Err::InvalidRpcData));
     814              :         }
     815              : 
     816            3 :         auto audioContext = this->audioContext(ctxId);
     817            3 :         IF_ASSERT_FAILED(audioContext) {
     818            0 :             return make_response_ret(msg, make_ret(Err::InvalidContext));
     819              :         }
     820              : 
     821            3 :         audioContext->trackAdded().disconnect(this);
     822            3 :         audioContext->trackRemoved().disconnect(this);
     823            3 :         audioContext->sourceParamsChanged().disconnect(this);
     824            3 :         audioContext->fxChainParamsChanged().disconnect(this);
     825              : 
     826            3 :         channel()->cancelAllRequests(ctxId);
     827            3 :         m_trackStreams.erase(ctxId);
     828              : 
     829            3 :         audioEngine()->destroyContext(ctxId);
     830              : 
     831            3 :         return make_response_ret(msg, make_ok());
     832            3 :     });
     833            3 : }
     834              : 
     835            5 : void EngineRpcController::addTrackStream(rpc::CtxId ctxId, TrackId trackId, rpc::StreamId streamId)
     836              : {
     837            5 :     m_trackStreams[ctxId][trackId].push_back(streamId);
     838            5 : }
     839              : 
     840            2 : void EngineRpcController::removeTrackStreams(rpc::CtxId ctxId, TrackId trackId)
     841              : {
     842            2 :     auto ctxIt = m_trackStreams.find(ctxId);
     843            2 :     if (ctxIt == m_trackStreams.end()) {
     844            1 :         return;
     845              :     }
     846              : 
     847            1 :     auto trackIt = ctxIt->second.find(trackId);
     848            1 :     if (trackIt == ctxIt->second.end()) {
     849            0 :         return;
     850              :     }
     851              : 
     852            4 :     for (StreamId streamId : trackIt->second) {
     853            3 :         channel()->removeStream(streamId);
     854              :     }
     855              : 
     856            1 :     ctxIt->second.erase(trackIt);
     857              : }
     858              : 
     859           74 : void EngineRpcController::onLongRequest(rpc::CtxId ctxId, rpc::MsgCode code, const RequestHandler& h)
     860              : {
     861           74 :     onRequest(OperationType::LongOperation, ctxId, code, h);
     862           74 : }
     863              : 
     864           95 : void EngineRpcController::onQuickRequest(rpc::CtxId ctxId, rpc::MsgCode code, const RequestHandler& h)
     865              : {
     866           95 :     onRequest(OperationType::QuickOperation, ctxId, code, h);
     867           95 : }
     868              : 
     869          169 : void EngineRpcController::onRequest(OperationType type, rpc::CtxId ctxId, rpc::MsgCode code, const RequestHandler& handler)
     870              : {
     871          169 :     m_usedRequests.push_back({ ctxId, code });
     872              : 
     873          169 :     channel()->onRequest(ctxId, code, [this, type, code, handler](const Msg& msg) -> Msg {
     874           14 :         Msg resp;
     875           28 :         Operation func = [this, code, handler, msg, &resp]() {
     876           14 :             if (m_terminated) {
     877            0 :                 return;
     878              :             }
     879              : 
     880              :             UNUSED(code);
     881              :             BEGIN_METHOD_DURATION;
     882           14 :             resp = handler(msg);
     883              :             END_METHOD_DURATION(code);
     884           14 :         };
     885              : 
     886           14 :         IF_ASSERT_FAILED(m_execOperation) {
     887            0 :             func();
     888              :         } else {
     889           14 :             m_execOperation->execOperation(type, func);
     890              :         }
     891           14 :         return resp;
     892           14 :     });
     893          169 : }
     894              : 
     895            3 : void EngineRpcController::deinit()
     896              : {
     897              :     ONLY_AUDIO_RPC_THREAD;
     898              : 
     899            3 :     m_terminated = true;
     900              : 
     901          172 :     for (const rpc::MsgKey& key : m_usedRequests) {
     902          169 :         channel()->onRequest(key.ctxId, key.code, nullptr);
     903              :     }
     904            3 :     m_usedRequests.clear();
     905            3 : }
        

Generated by: LCOV version 2.0-1