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