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