LCOV - code coverage report
Current view: top level - muse/framework/audio/common/rpc - irpcchannel.h Coverage Total Hit
Test: filtered_coverage.info Lines: 35.9 % 184 66
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              : #pragma once
      23              : 
      24              : #include <functional>
      25              : #include <tuple>
      26              : 
      27              : #include "global/modularity/imoduleinterface.h"
      28              : #include "global/modularity/ioc.h"
      29              : 
      30              : #include "global/types/bytearray.h"
      31              : #include "global/async/channel.h"
      32              : #include "global/async/asyncable.h"
      33              : 
      34              : #include "rpcpacker.h"
      35              : 
      36              : namespace muse::audio::rpc {
      37              : using CallId = uint64_t;
      38              : using CtxId = uint16_t;
      39              : constexpr CtxId GLOBAL_CTX_ID = 0;
      40              : 
      41           93 : inline CtxId ctxId(const muse::modularity::ContextPtr& ctx)
      42              : {
      43           93 :     IF_ASSERT_FAILED(ctx) {
      44            0 :         return GLOBAL_CTX_ID;
      45              :     }
      46           93 :     IF_ASSERT_FAILED(ctx->id > 0) {
      47            0 :         return GLOBAL_CTX_ID;
      48              :     }
      49           93 :     return static_cast<CtxId>(ctx->id);
      50              : }
      51              : 
      52              : enum class MsgCode {
      53              :     Undefined = 0,
      54              : 
      55              :     // Init / Deinit
      56              :     EngineInit,
      57              :     EngineRunning, // notification
      58              :     EngineDeinit,
      59              : 
      60              :     ContextInit,
      61              :     ContextDeinit,
      62              : 
      63              :     // Config
      64              :     EngineConfigChanged,
      65              : 
      66              :     // AudioEngine
      67              :     SetOutputSpec,
      68              : 
      69              :     // Resources
      70              :     GetAvailableInputResources,
      71              :     GetAvailableSoundPresets,
      72              :     GetAvailableOutputResources,
      73              : 
      74              :     // Tracks
      75              :     RemoveTrack,
      76              :     RemoveAllTracks,
      77              :     GetTrackIdList,
      78              :     GetTrackName,
      79              :     AddTrackWithPlaybackData,
      80              :     AddTrackWithIODevice,
      81              :     AddAuxTrack,
      82              :     // notification
      83              :     TrackAdded,
      84              :     TrackRemoved,
      85              : 
      86              :     // Params
      87              :     GetTrackParams,
      88              :     SetSourceParams,
      89              :     SetControlParams,
      90              :     SetFxChainParams,
      91              :     SetAuxSendsParams,
      92              :     // notification
      93              :     SourceParamsChanged,
      94              :     FxChainParamsChanged,
      95              :     ControlParamsChanged,
      96              :     AuxSendsParamsChanged,
      97              : 
      98              :     // Input processing
      99              :     GetInputProcessingProgress,
     100              :     ProcessInput,
     101              : 
     102              :     // Clear
     103              :     ClearCache,
     104              :     ClearSources,
     105              :     ClearMasterOutputParams,
     106              :     ClearAllFx,
     107              : 
     108              :     // Play
     109              :     PrepareToPlay,
     110              :     Play,
     111              :     Seek,
     112              :     Stop,
     113              :     Pause,
     114              :     Resume,
     115              :     SetDuration,
     116              :     SetLoop,
     117              :     ResetLoop,
     118              :     GetPlaybackStatus,
     119              :     GetPlaybackPosition,
     120              : 
     121              :     // Signals
     122              :     GetSignalChanges,
     123              :     GetAutomatedControlParamsChanges,
     124              : 
     125              :     // Export
     126              :     SaveSoundTrack,
     127              :     AbortSavingAllSoundTracks,
     128              :     GetSaveSoundTrackProgress,
     129              : 
     130              :     // SoundFont
     131              :     LoadSoundFonts,
     132              :     AddSoundFont,
     133              :     AddSoundFontData,
     134              : 
     135              :     // Transport
     136              :     TransportEventReceived,
     137              : 
     138              :     // Streams
     139              :     FirstStream = 100,
     140              :     PlaybackDataMainStream,
     141              :     PlaybackDataOffStream,
     142              :     AudioSignalStream,
     143              :     AudioMasterSignalStream,
     144              :     AutomatedControlParamsStream,
     145              :     PlaybackStatusStream,
     146              :     PlaybackPositionStream,
     147              :     InputProcessingProgressStream,
     148              :     SaveSoundTrackProgressStream
     149              : };
     150              : 
     151            0 : inline std::string to_string(MsgCode m)
     152              : {
     153            0 :     switch (m) {
     154            0 :     case MsgCode::Undefined: return "Undefined";
     155              : 
     156              :     // Init / Deinit
     157            0 :     case MsgCode::EngineRunning: return "EngineRunning";
     158            0 :     case MsgCode::EngineInit: return "EngineInit";
     159            0 :     case MsgCode::EngineDeinit: return "EngineDeinit";
     160              : 
     161            0 :     case MsgCode::ContextInit: return "ContextInit";
     162            0 :     case MsgCode::ContextDeinit: return "ContextDeinit";
     163              : 
     164              :     // Config
     165            0 :     case MsgCode::EngineConfigChanged: return "EngineConfigChanged";
     166              : 
     167              :     // AudioEngine
     168            0 :     case MsgCode::SetOutputSpec: return "SetOutputSpec";
     169              : 
     170              :     // Resources
     171            0 :     case MsgCode::GetAvailableInputResources: return "GetAvailableInputResources";
     172            0 :     case MsgCode::GetAvailableSoundPresets: return "GetAvailableSoundPresets";
     173            0 :     case MsgCode::GetAvailableOutputResources: return "GetAvailableOutputResources";
     174              : 
     175              :     // Tracks
     176            0 :     case MsgCode::RemoveTrack: return "RemoveTrack";
     177            0 :     case MsgCode::RemoveAllTracks: return "RemoveAllTracks";
     178            0 :     case MsgCode::GetTrackIdList: return "GetTrackIdList";
     179            0 :     case MsgCode::GetTrackName: return "GetTrackName";
     180            0 :     case MsgCode::AddTrackWithPlaybackData: return "AddTrackWithPlaybackData";
     181            0 :     case MsgCode::AddTrackWithIODevice: return "AddTrackWithIODevice";
     182            0 :     case MsgCode::AddAuxTrack: return "AddAuxTrack";
     183            0 :     case MsgCode::TrackAdded: return "TrackAdded";
     184            0 :     case MsgCode::TrackRemoved: return "TrackRemoved";
     185              : 
     186              :     // Params
     187            0 :     case MsgCode::GetTrackParams: return "GetTrackParams";
     188            0 :     case MsgCode::SetSourceParams: return "SetSourceParams";
     189            0 :     case MsgCode::SetControlParams: return "SetControlParams";
     190            0 :     case MsgCode::SetFxChainParams: return "SetFxChainParams";
     191            0 :     case MsgCode::SetAuxSendsParams: return "SetAuxSendsParams";
     192              :     // notification
     193            0 :     case MsgCode::SourceParamsChanged: return "SourceParamsChanged";
     194            0 :     case MsgCode::FxChainParamsChanged: return "FxChainParamsChanged";
     195            0 :     case MsgCode::ControlParamsChanged: return "ControlParamsChanged";
     196            0 :     case MsgCode::AuxSendsParamsChanged: return "AuxSendsParamsChanged";
     197              : 
     198              :     // Input processing
     199            0 :     case MsgCode::GetInputProcessingProgress: return "GetInputProcessingProgress";
     200            0 :     case MsgCode::ProcessInput: return "ProcessInput";
     201              : 
     202              :     // Clear
     203            0 :     case MsgCode::ClearCache: return "ClearCache";
     204            0 :     case MsgCode::ClearSources: return "ClearSources";
     205            0 :     case MsgCode::ClearMasterOutputParams: return "ClearMasterOutputParams";
     206            0 :     case MsgCode::ClearAllFx: return "ClearAllFx";
     207              : 
     208              :     // Play
     209            0 :     case MsgCode::PrepareToPlay: return "PrepareToPlay";
     210            0 :     case MsgCode::Play: return "Play";
     211            0 :     case MsgCode::Seek: return "Seek";
     212            0 :     case MsgCode::Stop: return "Stop";
     213            0 :     case MsgCode::Pause: return "Pause";
     214            0 :     case MsgCode::Resume: return "Resume";
     215            0 :     case MsgCode::SetDuration: return "SetDuration";
     216            0 :     case MsgCode::SetLoop: return "SetLoop";
     217            0 :     case MsgCode::ResetLoop: return "ResetLoop";
     218            0 :     case MsgCode::GetPlaybackStatus: return "GetPlaybackStatus";
     219            0 :     case MsgCode::GetPlaybackPosition: return "GetPlaybackPosition";
     220              : 
     221              :     // Signals
     222            0 :     case MsgCode::GetSignalChanges: return "GetSignalChanges";
     223            0 :     case MsgCode::GetAutomatedControlParamsChanges: return "GetAutomatedControlParamsChanges";
     224              : 
     225            0 :     case MsgCode::SaveSoundTrack: return "SaveSoundTrack";
     226            0 :     case MsgCode::AbortSavingAllSoundTracks: return "AbortSavingAllSoundTracks";
     227            0 :     case MsgCode::GetSaveSoundTrackProgress: return "GetSaveSoundTrackProgress";
     228              : 
     229              :     // SoundFont
     230            0 :     case MsgCode::LoadSoundFonts: return "LoadSoundFonts";
     231            0 :     case MsgCode::AddSoundFont: return "AddSoundFont";
     232            0 :     case MsgCode::AddSoundFontData: return "AddSoundFontData";
     233              : 
     234              :     // Transport
     235            0 :     case MsgCode::TransportEventReceived: return "TransportEventReceived";
     236              : 
     237              :     // Streams
     238            0 :     case MsgCode::FirstStream: return "FirstStream";
     239            0 :     case MsgCode::PlaybackDataMainStream: return "PlaybackDataMainStream";
     240            0 :     case MsgCode::PlaybackDataOffStream: return "PlaybackDataOffStream";
     241            0 :     case MsgCode::AudioSignalStream: return "AudioSignalStream";
     242            0 :     case MsgCode::AudioMasterSignalStream: return "AudioMasterSignalStream";
     243            0 :     case MsgCode::AutomatedControlParamsStream: return "AutomatedControlParamsStream";
     244            0 :     case MsgCode::PlaybackStatusStream: return "PlaybackStatusStream";
     245            0 :     case MsgCode::PlaybackPositionStream: return "PlaybackPositionStream";
     246            0 :     case MsgCode::InputProcessingProgressStream: return "InputProcessingProgressStream";
     247            0 :     case MsgCode::SaveSoundTrackProgressStream: return "SaveSoundTrackProgressStream";
     248              :     }
     249              : 
     250            0 :     assert(false && "unknown enum value");
     251              :     return std::to_string(static_cast<int>(m));
     252              : }
     253              : 
     254              : enum class MsgType {
     255              :     Undefined = 0,
     256              :     Notification,
     257              :     Request,
     258              :     Response,
     259              :     ResponseDelayed,
     260              :     Stream
     261              : };
     262              : 
     263              : inline std::string to_string(MsgType t)
     264              : {
     265              :     switch (t) {
     266              :     case MsgType::Undefined: return "Undefined";
     267              :     case MsgType::Notification: return "Notification";
     268              :     case MsgType::Request: return "Request";
     269              :     case MsgType::Response: return "Response";
     270              :     case MsgType::ResponseDelayed: return "ResponseDelayed";
     271              :     case MsgType::Stream: return "Stream";
     272              :     }
     273              : 
     274              :     assert(false && "unknown enum value");
     275              :     return std::to_string(static_cast<int>(t));
     276              : }
     277              : 
     278              : struct Msg {
     279              :     CallId callId = 0;
     280              :     CtxId ctxId = 0;   // 0 - global, >0 - contextual
     281              :     MsgCode code = MsgCode::Undefined;
     282              :     MsgType type = MsgType::Undefined;
     283              :     ByteArray data;
     284              : };
     285              : 
     286              : struct MsgKey {
     287              :     CtxId ctxId = 0;
     288              :     MsgCode code = MsgCode::Undefined;
     289              : 
     290              :     bool operator==(const MsgKey& other) const
     291              :     {
     292              :         return ctxId == other.ctxId && code == other.code;
     293              :     }
     294              : 
     295            0 :     bool operator<(const MsgKey& other) const
     296              :     {
     297            0 :         return std::tie(ctxId, code) < std::tie(other.ctxId, other.code);
     298              :     }
     299              : };
     300              : 
     301              : using StreamId = CallId;
     302              : using StreamName = MsgCode;
     303              : using StreamMsg = Msg;
     304              : 
     305           14 : inline StreamId& last_stream_id()
     306              : {
     307              :     static StreamId lastStreamId = 0;
     308           14 :     return lastStreamId;
     309              : }
     310              : 
     311              : inline void set_last_stream_id(StreamId id)
     312              : {
     313              :     last_stream_id() = id;
     314              : }
     315              : 
     316            7 : inline StreamId new_stream_id()
     317              : {
     318            7 :     ++last_stream_id();
     319            7 :     return last_stream_id();
     320              : }
     321              : 
     322              : using StreamHandler = std::function<void (const Msg& msg)>;
     323              : 
     324              : enum class StreamType {
     325              :     Undefined = 0,
     326              :     Send,
     327              :     Receive
     328              : };
     329              : 
     330              : struct IRpcStream {
     331           13 :     virtual ~IRpcStream() = default;
     332              : 
     333              :     virtual void setCtxId(CtxId ctxId) = 0;
     334              :     virtual CtxId ctxId() const = 0;
     335              :     virtual StreamId streamId() const = 0;
     336              :     virtual StreamType type() const = 0;
     337              :     virtual void init() = 0;
     338              :     virtual bool inited() const = 0;
     339              : };
     340              : 
     341              : using RpcStreamExec = std::function<void (const std::function<void ()>&)>;
     342              : 
     343              : class IStreamRpcChannel;
     344              : template<typename ... Types>
     345              : class RpcStream : public IRpcStream, public async::Asyncable
     346              : {
     347              : public:
     348              : 
     349           13 :     RpcStream(IStreamRpcChannel* rpc, StreamName name, StreamId id, StreamType type,
     350              :               const async::Channel<Types...>& ch,
     351              :               const RpcStreamExec& exec)
     352           13 :         : m_rpc(rpc), m_name(name), m_streamId(id), m_type(type), m_ch(ch), m_exec(exec) {}
     353              : 
     354           26 :     ~RpcStream()
     355              :     {
     356           13 :         deinit();
     357           26 :     }
     358              : 
     359           13 :     void setCtxId(CtxId ctxId) override { m_ctxId = ctxId; }
     360            7 :     CtxId ctxId() const override { return m_ctxId; }
     361              :     StreamName name() const { return m_name; }
     362           10 :     StreamId streamId() const override { return m_streamId; }
     363            0 :     StreamType type() const override { return m_type; }
     364              :     void init() override;
     365            0 :     bool inited() const override { return m_inited; }
     366              :     void deinit();
     367              : 
     368              : private:
     369              :     IStreamRpcChannel* m_rpc = nullptr;
     370              :     CtxId m_ctxId = 0;
     371              :     StreamName m_name = StreamName::Undefined;
     372              :     StreamId m_streamId = 0;
     373              :     StreamType m_type = StreamType::Undefined;
     374              :     async::Channel<Types...> m_ch;
     375              :     RpcStreamExec m_exec = nullptr;
     376              :     bool m_inited = false;
     377              : };
     378              : 
     379              : class IStreamRpcChannel
     380              : {
     381              : public:
     382           32 :     virtual ~IStreamRpcChannel() = default;
     383              : 
     384              :     virtual void addStream(std::shared_ptr<IRpcStream> s) = 0;
     385              :     virtual void removeStream(StreamId id) = 0;
     386              :     virtual void sendStream(const StreamMsg& msg) = 0;
     387              :     virtual void onStream(StreamId id, StreamHandler h) = 0;
     388              : };
     389              : 
     390              : using RequestHandler = std::function<Msg (const Msg& msg)>;
     391              : using ResponseHandler = std::function<void (const Msg& msg)>;
     392              : using NotificationHandler = std::function<void (const Msg& msg)>;
     393              : 
     394              : class IRpcChannel : MODULE_GLOBAL_INTERFACE, public IStreamRpcChannel
     395              : {
     396           55 :     INTERFACE_ID(IRpcChannel)
     397              : public:
     398           32 :     virtual ~IRpcChannel() = default;
     399              : 
     400              :     virtual void setupOnMain() = 0;
     401              :     virtual void setupOnEngine() = 0;
     402              : 
     403              :     virtual void process() = 0;
     404              : 
     405              :     virtual void send(const Msg& msg, const ResponseHandler& onResponse = nullptr) = 0;
     406              :     virtual void cancelAllRequests(CtxId ctxId) = 0;
     407              :     virtual void onRequest(CtxId ctxId, MsgCode code, RequestHandler h) = 0;
     408              :     virtual void onNotification(CtxId ctxId, MsgCode code, NotificationHandler h) = 0;
     409              : 
     410              :     // stream (async/channel)
     411              :     template<typename ... Types>
     412            7 :     StreamId addSendStream(CtxId ctxId, StreamName name, const async::Channel<Types...>& ch)
     413              :     {
     414            7 :         StreamId id = new_stream_id();
     415            7 :         auto s = new RpcStream<Types...>(this, name, id, StreamType::Send, ch, nullptr);
     416            7 :         s->setCtxId(ctxId);
     417            7 :         addStream(std::shared_ptr<IRpcStream>(s));
     418            7 :         return id;
     419              :     }
     420              : 
     421              :     template<typename ... Types>
     422            6 :     void addReceiveStream(CtxId ctxId, StreamName name, rpc::StreamId id, const async::Channel<Types...>& ch,
     423              :                           const RpcStreamExec& exec = nullptr)
     424              :     {
     425            6 :         auto s = new RpcStream<Types...>(this, name, id, StreamType::Receive, ch, exec);
     426            6 :         s->setCtxId(ctxId);
     427            6 :         addStream(std::shared_ptr<IRpcStream>(s));
     428            6 :     }
     429              : };
     430              : 
     431            0 : inline StreamMsg make_stream_msg(CtxId ctxId, StreamId id, StreamName name, const ByteArray& data = ByteArray())
     432              : {
     433            0 :     return StreamMsg{ id, ctxId, name, MsgType::Stream, data };
     434              : }
     435              : 
     436              : template<typename ... Types>
     437            0 : void RpcStream<Types...>::init()
     438              : {
     439            0 :     if (m_inited) {
     440            0 :         return;
     441              :     }
     442              : 
     443            0 :     switch (m_type) {
     444            0 :     case StreamType::Send: {
     445            0 :         m_ch.onReceive(this, [this](const Types... args) {
     446            0 :                 ByteArray data = RpcPacker::pack(args ...);
     447            0 :                 m_rpc->sendStream(make_stream_msg(m_ctxId, m_streamId, m_name, data));
     448            0 :             });
     449            0 :     } break;
     450            0 :     case StreamType::Receive: {
     451            0 :         m_rpc->onStream(m_streamId, [this](const StreamMsg& msg) {
     452            0 :                 std::function<void()> func = [this, msg]() {
     453            0 :                     std::tuple<Types...> values;
     454            0 :                     bool success = std::apply([msg](auto&... args) {
     455            0 :                         return RpcPacker::unpack(msg.data, args ...);
     456              :                     }, values);
     457              : 
     458            0 :                     if (success) {
     459            0 :                         std::apply([this](const auto&... args) {
     460            0 :                             m_ch.send(args ...);
     461              :                         }, values);
     462              :                     }
     463            0 :                 };
     464            0 :                 if (m_exec) {
     465            0 :                     m_exec(func);
     466              :                 } else {
     467            0 :                     func();
     468              :                 }
     469            0 :             });
     470            0 :     } break;
     471            0 :     case StreamType::Undefined: {
     472            0 :     } break;
     473              :     }
     474              : 
     475            0 :     m_inited = true;
     476              : }
     477              : 
     478              : template<typename ... Types>
     479           13 : void RpcStream<Types...>::deinit()
     480              : {
     481           13 :     if (!m_inited) {
     482           13 :         return;
     483              :     }
     484              : 
     485            0 :     switch (m_type) {
     486            0 :     case StreamType::Send: {
     487            0 :         m_ch.disconnect(this);
     488            0 :     } break;
     489            0 :     case StreamType::Receive: {
     490            0 :         m_rpc->onStream(m_streamId, nullptr);
     491            0 :     } break;
     492            0 :     case StreamType::Undefined: {
     493            0 :     } break;
     494              :     }
     495              : 
     496            0 :     m_inited = false;
     497              : }
     498              : 
     499              : // msgs
     500           74 : inline CallId new_call_id()
     501              : {
     502              :     static CallId lastId = 0;
     503           74 :     ++lastId;
     504           74 :     return lastId;
     505              : }
     506              : 
     507           71 : inline Msg make_request(CtxId ctxId, MsgCode code, const ByteArray& data = ByteArray())
     508              : {
     509           71 :     Msg msg;
     510           71 :     msg.callId = new_call_id();
     511           71 :     msg.ctxId = ctxId;
     512           71 :     msg.code = code;
     513           71 :     msg.type = MsgType::Request;
     514           71 :     msg.data = data;
     515           71 :     return msg;
     516              : }
     517              : 
     518           26 : inline Msg make_response(const Msg& req, const ByteArray& data = ByteArray())
     519              : {
     520           26 :     Msg msg;
     521           26 :     msg.ctxId = req.ctxId;
     522           26 :     msg.callId = req.callId;
     523           26 :     msg.code = req.code;
     524           26 :     msg.type = MsgType::Response;
     525           26 :     msg.data = data;
     526           26 :     return msg;
     527              : }
     528              : 
     529              : //! NOTE Service message means that the response will be sent later
     530            0 : inline Msg make_response_delayed(const Msg& req)
     531              : {
     532            0 :     Msg r = make_response(req);
     533            0 :     r.type = MsgType::ResponseDelayed;
     534            0 :     return r;
     535              : }
     536              : 
     537            7 : inline Msg make_response_ret(const Msg& req, const Ret& ret)
     538              : {
     539            7 :     return make_response(req, RpcPacker::pack(ret));
     540              : }
     541              : 
     542              : template<typename T>
     543            4 : inline Msg make_response_ret(const Msg& req, const RetVal<T>& ret)
     544              : {
     545            4 :     return make_response(req, RpcPacker::pack(ret));
     546              : }
     547              : 
     548            3 : inline Msg make_notification(CtxId ctxId, MsgCode code, const ByteArray& data = ByteArray())
     549              : {
     550            3 :     Msg msg;
     551            3 :     msg.callId = new_call_id();
     552            3 :     msg.ctxId = ctxId;
     553            3 :     msg.code = code;
     554            3 :     msg.type = MsgType::Notification;
     555            3 :     msg.data = data;
     556            3 :     return msg;
     557              : }
     558              : }
        

Generated by: LCOV version 2.0-1