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) 2021 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 "playback.h"
23 :
24 : #include "audio/common/rpc/rpcpacker.h"
25 : #include "audio/common/audiosanitizer.h"
26 : #include "audio/common/audioerrors.h"
27 : #include "player.h"
28 :
29 : #include "muse_framework_config.h"
30 :
31 : #include "log.h"
32 :
33 : using namespace muse;
34 : using namespace muse::audio;
35 : using namespace muse::audio::rpc;
36 : using namespace muse::async;
37 :
38 5 : Playback::~Playback()
39 : {
40 5 : channel()->cancelAllRequests(ctxId());
41 5 : }
42 :
43 75 : rpc::CtxId Playback::ctxId() const
44 : {
45 75 : return rpc::ctxId(iocContext());
46 : }
47 :
48 : // Init
49 6 : async::Promise<Ret> Playback::init()
50 : {
51 : ONLY_AUDIO_MAIN_THREAD;
52 :
53 6 : channel()->onNotification(ctxId(), MsgCode::TrackAdded, [this](const Msg& msg) {
54 : ONLY_AUDIO_MAIN_THREAD;
55 0 : TrackId trackId = 0;
56 0 : IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
57 0 : return;
58 : }
59 0 : m_trackAdded.send(trackId);
60 : });
61 :
62 6 : channel()->onNotification(ctxId(), MsgCode::TrackRemoved, [this](const Msg& msg) {
63 : ONLY_AUDIO_MAIN_THREAD;
64 1 : TrackId trackId = 0;
65 1 : IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId)) {
66 0 : return;
67 : }
68 1 : removeTrackStreams(trackId);
69 1 : m_trackRemoved.send(trackId);
70 : });
71 :
72 6 : channel()->onNotification(ctxId(), MsgCode::SourceParamsChanged, [this](const Msg& msg) {
73 : ONLY_AUDIO_MAIN_THREAD;
74 0 : TrackId trackId = 0;
75 0 : AudioSourceParams params;
76 0 : IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId, params)) {
77 0 : return;
78 : }
79 0 : m_sourceParamsChanged.send(trackId, params);
80 0 : });
81 :
82 6 : channel()->onNotification(ctxId(), MsgCode::FxChainParamsChanged, [this](const Msg& msg) {
83 : ONLY_AUDIO_MAIN_THREAD;
84 0 : TrackId trackId = 0;
85 0 : AudioFxChain params;
86 0 : IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId, params)) {
87 0 : return;
88 : }
89 0 : if (trackId == MASTER_TRACK_ID) {
90 0 : m_masterFxChainParamsChanged.send(params);
91 : } else {
92 0 : m_fxChainParamsChanged.send(trackId, params);
93 : }
94 0 : });
95 :
96 6 : channel()->onNotification(ctxId(), MsgCode::ControlParamsChanged, [this](const Msg& msg) {
97 : ONLY_AUDIO_MAIN_THREAD;
98 0 : TrackId trackId = 0;
99 0 : ControlParams params;
100 0 : IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId, params)) {
101 0 : return;
102 : }
103 0 : if (trackId == MASTER_TRACK_ID) {
104 0 : m_masterControlParamsChanged.send(params);
105 : } else {
106 0 : m_controlParamsChanged.send(trackId, params);
107 : }
108 0 : });
109 :
110 6 : channel()->onNotification(ctxId(), MsgCode::AuxSendsParamsChanged, [this](const Msg& msg) {
111 : ONLY_AUDIO_MAIN_THREAD;
112 0 : TrackId trackId = 0;
113 0 : AuxSendsParams params;
114 0 : IF_ASSERT_FAILED(RpcPacker::unpack(msg.data, trackId, params)) {
115 0 : return;
116 : }
117 0 : if (trackId == MASTER_TRACK_ID) {
118 0 : m_masterAuxSendsParamsChanged.send(params);
119 : } else {
120 0 : m_auxSendsParamsChanged.send(trackId, params);
121 : }
122 0 : });
123 :
124 12 : return async::make_promise<Ret>([this](auto resolve, auto /*reject*/) {
125 : ONLY_AUDIO_MAIN_THREAD;
126 :
127 12 : auto initContext = [this, resolve]() {
128 : //! NOTE The message context here is global, and the context ID is the data in the message
129 6 : Msg msg = rpc::make_request(rpc::GLOBAL_CTX_ID, MsgCode::ContextInit, RpcPacker::pack(ctxId()));
130 6 : channel()->send(msg, [this, resolve](const Msg& res) {
131 : ONLY_AUDIO_MAIN_THREAD;
132 6 : Ret ret;
133 6 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
134 0 : ret = audio::make_ret(Err::InvalidRpcData);
135 0 : (void)resolve(ret);
136 0 : return;
137 : }
138 6 : m_inited.set(ret.success());
139 6 : (void)resolve(ret);
140 6 : });
141 6 : };
142 :
143 6 : LOGD() << "isAudioStarted: " << startAudioController()->isAudioStarted();
144 6 : if (startAudioController()->isAudioStarted()) {
145 6 : initContext();
146 : } else {
147 0 : startAudioController()->isAudioStartedChanged().onReceive(this, [this, initContext](bool arg) {
148 0 : LOGD() << "isAudioStartedChanged: " << arg;
149 0 : if (arg) {
150 0 : initContext();
151 : } else {
152 0 : LOGE() << "audio not started";
153 : }
154 0 : startAudioController()->isAudioStartedChanged().disconnect(this);
155 : });
156 : }
157 :
158 12 : return Promise<Ret>::dummy_result();
159 18 : }, PromiseType::AsyncByPromise);
160 : }
161 :
162 3 : void Playback::deinit()
163 : {
164 : ONLY_AUDIO_MAIN_THREAD;
165 :
166 3 : channel()->onNotification(ctxId(), MsgCode::TrackAdded, nullptr);
167 3 : channel()->onNotification(ctxId(), MsgCode::TrackRemoved, nullptr);
168 3 : channel()->onNotification(ctxId(), MsgCode::SourceParamsChanged, nullptr);
169 3 : channel()->onNotification(ctxId(), MsgCode::FxChainParamsChanged, nullptr);
170 3 : channel()->onNotification(ctxId(), MsgCode::ControlParamsChanged, nullptr);
171 3 : channel()->onNotification(ctxId(), MsgCode::AuxSendsParamsChanged, nullptr);
172 :
173 3 : channel()->cancelAllRequests(ctxId());
174 :
175 3 : m_saveSoundTrackProgressStream = SaveSoundTrackProgress();
176 3 : m_saveSoundTrackProgressStreamInited = false;
177 3 : m_saveSoundTrackProgressStreamId = 0;
178 :
179 3 : if (m_player) {
180 2 : m_player->deinit();
181 2 : m_player.reset();
182 : }
183 :
184 3 : m_trackStreams.clear();
185 :
186 3 : channel()->send(rpc::make_request(rpc::GLOBAL_CTX_ID, MsgCode::ContextDeinit, RpcPacker::pack(ctxId())));
187 3 : m_inited.set(false);
188 6 : }
189 :
190 2 : void Playback::addTrackStream(TrackId trackId, rpc::StreamId streamId) const
191 : {
192 2 : m_trackStreams[trackId].push_back(streamId);
193 2 : }
194 :
195 1 : void Playback::removeTrackStreams(TrackId trackId) const
196 : {
197 1 : auto it = m_trackStreams.find(trackId);
198 1 : if (it == m_trackStreams.end()) {
199 0 : return;
200 : }
201 :
202 2 : for (StreamId streamId : it->second) {
203 1 : channel()->removeStream(streamId);
204 : }
205 :
206 1 : m_trackStreams.erase(it);
207 : }
208 :
209 13 : bool Playback::isInited() const
210 : {
211 13 : return m_inited.val;
212 : }
213 :
214 0 : async::Channel<bool> Playback::initedChanged() const
215 : {
216 0 : return m_inited.ch;
217 : }
218 :
219 6 : IPlayerPtr Playback::player() const
220 : {
221 : //! NOTE The player requests the engine context, so it must exist
222 6 : IF_ASSERT_FAILED(isInited()) {
223 0 : return nullptr;
224 : }
225 :
226 6 : if (!m_player) {
227 5 : m_player = std::make_shared<Player>(iocContext());
228 5 : m_player->init();
229 : }
230 :
231 6 : return m_player;
232 : }
233 :
234 : template<typename T>
235 0 : static void doReject(MsgCode code, T& reject, const Ret& ret)
236 : {
237 0 : LOGE() << "failed rpc request: " << rpc::to_string(code) << ", err: " << ret.toString();
238 0 : (void)reject(ret.code(), ret.text());
239 0 : }
240 :
241 : // Resources
242 0 : async::Promise<AudioResourceMetaList> Playback::availableInputResources() const
243 : {
244 : ONLY_AUDIO_MAIN_THREAD;
245 0 : return async::make_promise<AudioResourceMetaList>([this](auto resolve, auto reject) {
246 : ONLY_AUDIO_MAIN_THREAD;
247 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetAvailableInputResources);
248 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
249 : ONLY_AUDIO_MAIN_THREAD;
250 0 : RetVal<AudioResourceMetaList> ret;
251 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
252 0 : doReject(MsgCode::GetAvailableInputResources, reject, audio::make_ret(Err::InvalidRpcData));
253 0 : return;
254 : }
255 0 : if (ret.ret) {
256 0 : (void)resolve(ret.val);
257 : } else {
258 0 : doReject(MsgCode::GetAvailableInputResources, reject, ret.ret);
259 : }
260 0 : });
261 0 : return Promise<AudioResourceMetaList>::dummy_result();
262 0 : }, PromiseType::AsyncByBody);
263 : }
264 :
265 0 : async::Promise<SoundPresetList> Playback::availableSoundPresets(const AudioResourceMeta& resourceMeta) const
266 : {
267 : ONLY_AUDIO_MAIN_THREAD;
268 0 : return async::make_promise<SoundPresetList>([this, resourceMeta](auto resolve, auto reject) {
269 : ONLY_AUDIO_MAIN_THREAD;
270 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetAvailableSoundPresets, RpcPacker::pack(resourceMeta));
271 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
272 : ONLY_AUDIO_MAIN_THREAD;
273 0 : RetVal<SoundPresetList> ret;
274 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
275 0 : doReject(MsgCode::GetAvailableSoundPresets, reject, audio::make_ret(Err::InvalidRpcData));
276 0 : return;
277 : }
278 0 : if (ret.ret) {
279 0 : (void)resolve(ret.val);
280 : } else {
281 0 : doReject(MsgCode::GetAvailableSoundPresets, reject, ret.ret);
282 : }
283 0 : });
284 0 : return Promise<SoundPresetList>::dummy_result();
285 0 : }, PromiseType::AsyncByBody);
286 : }
287 :
288 0 : async::Promise<AudioResourceMetaList> Playback::availableOutputResources() const
289 : {
290 : ONLY_AUDIO_MAIN_THREAD;
291 0 : return async::make_promise<AudioResourceMetaList>([this](auto resolve, auto reject) {
292 : ONLY_AUDIO_MAIN_THREAD;
293 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetAvailableOutputResources);
294 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
295 : ONLY_AUDIO_MAIN_THREAD;
296 0 : RetVal<AudioResourceMetaList> ret;
297 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
298 0 : doReject(MsgCode::GetAvailableOutputResources, reject, audio::make_ret(Err::InvalidRpcData));
299 0 : return;
300 : }
301 0 : if (ret.ret) {
302 0 : (void)resolve(ret.val);
303 : } else {
304 0 : doReject(MsgCode::GetAvailableOutputResources, reject, ret.ret);
305 : }
306 0 : });
307 0 : return Promise<AudioResourceMetaList>::dummy_result();
308 0 : }, PromiseType::AsyncByBody);
309 : }
310 :
311 : // Setup tracks
312 0 : async::Promise<TrackIdList> Playback::trackIdList() const
313 : {
314 : ONLY_AUDIO_MAIN_THREAD;
315 0 : return async::make_promise<TrackIdList>([this](auto resolve, auto reject) {
316 : ONLY_AUDIO_MAIN_THREAD;
317 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetTrackIdList);
318 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
319 : ONLY_AUDIO_MAIN_THREAD;
320 0 : RetVal<TrackIdList> ret;
321 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
322 0 : doReject(MsgCode::GetTrackIdList, reject, audio::make_ret(Err::InvalidRpcData));
323 0 : return;
324 : }
325 0 : if (ret.ret) {
326 0 : (void)resolve(ret.val);
327 : } else {
328 0 : doReject(MsgCode::GetTrackIdList, reject, ret.ret);
329 : }
330 0 : });
331 0 : return Promise<TrackIdList>::dummy_result();
332 0 : }, PromiseType::AsyncByBody);
333 : }
334 :
335 0 : async::Promise<RetVal<TrackName> > Playback::trackName(const TrackId trackId) const
336 : {
337 : ONLY_AUDIO_MAIN_THREAD;
338 0 : return async::make_promise<RetVal<TrackName> >([this, trackId](auto resolve, auto reject) {
339 : ONLY_AUDIO_MAIN_THREAD;
340 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetTrackName, RpcPacker::pack(trackId));
341 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
342 : ONLY_AUDIO_MAIN_THREAD;
343 0 : RetVal<TrackName> ret;
344 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
345 0 : doReject(MsgCode::GetTrackName, reject, audio::make_ret(Err::InvalidRpcData));
346 0 : return;
347 : }
348 0 : (void)resolve(ret);
349 0 : });
350 0 : return Promise<RetVal<TrackName> >::dummy_result();
351 0 : }, PromiseType::AsyncByBody);
352 : }
353 :
354 0 : async::Promise<TrackId, TrackParams> Playback::addTrack(const TrackName& trackName,
355 : io::IODevice* playbackData,
356 : const TrackParams& params)
357 : {
358 : #ifdef MUSE_CONFIGURATION_IS_WEB
359 : NOT_SUPPORTED;
360 : return async::make_promise<TrackId, TrackParams>([](auto /*resolve*/, auto reject) {
361 : doReject(MsgCode::AddTrackWithIODevice, reject, muse::make_ret(Ret::Code::NotSupported));
362 : return Promise<TrackId, TrackParams>::dummy_result();
363 : });
364 : #else
365 : ONLY_AUDIO_MAIN_THREAD;
366 0 : return async::make_promise<TrackId, TrackParams>([this, trackName, playbackData, params](auto resolve, auto reject) {
367 : ONLY_AUDIO_MAIN_THREAD;
368 :
369 0 : ByteArray data = RpcPacker::pack(trackName, reinterpret_cast<uint64_t>(playbackData), params);
370 :
371 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::AddTrackWithIODevice, data);
372 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
373 : ONLY_AUDIO_MAIN_THREAD;
374 0 : RetVal2<TrackId, TrackParams> ret;
375 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
376 0 : doReject(MsgCode::AddTrackWithIODevice, reject, audio::make_ret(Err::InvalidRpcData));
377 0 : return;
378 : }
379 0 : if (ret.ret) {
380 0 : (void)resolve(ret.val1, ret.val2);
381 : } else {
382 0 : doReject(MsgCode::AddTrackWithIODevice, reject, ret.ret);
383 : }
384 0 : });
385 0 : return Promise<TrackId, TrackParams>::dummy_result();
386 0 : }, PromiseType::AsyncByBody);
387 :
388 : #endif // MUSE_CONFIGURATION_IS_WEB
389 : }
390 :
391 0 : async::Promise<TrackId, TrackParams> Playback::addTrack(const TrackName& trackName,
392 : const mpe::PlaybackData& playbackData,
393 : const TrackParams& params)
394 : {
395 : ONLY_AUDIO_MAIN_THREAD;
396 0 : return async::make_promise<TrackId, TrackParams>([this, trackName, playbackData, params](auto resolve, auto reject) {
397 : ONLY_AUDIO_MAIN_THREAD;
398 :
399 0 : rpc::StreamId mainStreamId = channel()->addSendStream(ctxId(), StreamName::PlaybackDataMainStream, playbackData.mainStream);
400 0 : rpc::StreamId offStreamId = channel()->addSendStream(ctxId(), StreamName::PlaybackDataOffStream, playbackData.offStream);
401 :
402 0 : ByteArray data = RpcPacker::pack(trackName, playbackData, params, mainStreamId, offStreamId);
403 :
404 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::AddTrackWithPlaybackData, data);
405 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
406 : ONLY_AUDIO_MAIN_THREAD;
407 0 : RetVal2<TrackId, TrackParams> ret;
408 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
409 0 : doReject(MsgCode::AddTrackWithPlaybackData, reject, audio::make_ret(Err::InvalidRpcData));
410 0 : return;
411 : }
412 0 : if (ret.ret) {
413 0 : (void)resolve(ret.val1, ret.val2);
414 : } else {
415 0 : doReject(MsgCode::AddTrackWithPlaybackData, reject, ret.ret);
416 : }
417 0 : });
418 0 : return Promise<TrackId, TrackParams>::dummy_result();
419 0 : }, PromiseType::AsyncByBody);
420 : }
421 :
422 0 : async::Promise<TrackId, TrackParams> Playback::addAuxTrack(const TrackName& trackName, const TrackParams& params)
423 : {
424 : ONLY_AUDIO_MAIN_THREAD;
425 0 : return async::make_promise<TrackId, TrackParams>([this, trackName, params](auto resolve, auto reject) {
426 : ONLY_AUDIO_MAIN_THREAD;
427 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::AddAuxTrack, RpcPacker::pack(trackName, params));
428 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
429 : ONLY_AUDIO_MAIN_THREAD;
430 0 : RetVal2<TrackId, TrackParams> ret;
431 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
432 0 : doReject(MsgCode::AddAuxTrack, reject, audio::make_ret(Err::InvalidRpcData));
433 0 : return;
434 : }
435 0 : if (ret.ret) {
436 0 : (void)resolve(ret.val1, ret.val2);
437 : } else {
438 0 : doReject(MsgCode::AddAuxTrack, reject, ret.ret);
439 : }
440 0 : });
441 0 : return Promise<TrackId, TrackParams>::dummy_result();
442 0 : }, PromiseType::AsyncByBody);
443 : }
444 :
445 0 : void Playback::removeTrack(const TrackId trackId)
446 : {
447 : ONLY_AUDIO_MAIN_THREAD;
448 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::RemoveTrack, RpcPacker::pack(trackId));
449 0 : channel()->send(msg);
450 0 : }
451 :
452 0 : void Playback::removeAllTracks()
453 : {
454 : ONLY_AUDIO_MAIN_THREAD;
455 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::RemoveAllTracks);
456 0 : channel()->send(msg);
457 0 : }
458 :
459 0 : async::Channel<TrackId> Playback::trackAdded() const
460 : {
461 0 : return m_trackAdded;
462 : }
463 :
464 0 : async::Channel<TrackId> Playback::trackRemoved() const
465 : {
466 0 : return m_trackRemoved;
467 : }
468 :
469 : // Params
470 0 : async::Promise<TrackParams> Playback::params(const TrackId trackId) const
471 : {
472 : ONLY_AUDIO_MAIN_THREAD;
473 0 : return async::make_promise<TrackParams>([this, trackId](auto resolve, auto reject) {
474 : ONLY_AUDIO_MAIN_THREAD;
475 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetTrackParams, RpcPacker::pack(trackId));
476 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
477 : ONLY_AUDIO_MAIN_THREAD;
478 0 : RetVal<TrackParams> ret;
479 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
480 0 : doReject(MsgCode::GetTrackParams, reject, audio::make_ret(Err::InvalidRpcData));
481 0 : return;
482 : }
483 :
484 0 : if (ret.ret) {
485 0 : (void)resolve(ret.val);
486 : } else {
487 0 : doReject(MsgCode::GetTrackParams, reject, ret.ret);
488 : }
489 0 : });
490 0 : return Promise<TrackParams>::dummy_result();
491 0 : }, PromiseType::AsyncByBody);
492 : }
493 :
494 0 : void Playback::setSourceParams(const TrackId trackId, const AudioSourceParams& params)
495 : {
496 : ONLY_AUDIO_MAIN_THREAD;
497 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::SetSourceParams, RpcPacker::pack(trackId, params));
498 0 : channel()->send(msg);
499 0 : }
500 :
501 0 : void Playback::setControlParams(const TrackId trackId, const ControlParams& params)
502 : {
503 : ONLY_AUDIO_MAIN_THREAD;
504 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::SetControlParams, RpcPacker::pack(trackId, params));
505 0 : channel()->send(msg);
506 0 : }
507 :
508 0 : void Playback::setFxChainParams(const TrackId trackId, const AudioFxChain& params)
509 : {
510 : ONLY_AUDIO_MAIN_THREAD;
511 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::SetFxChainParams, RpcPacker::pack(trackId, params));
512 0 : channel()->send(msg);
513 0 : }
514 :
515 0 : void Playback::setAuxSendsParams(const TrackId trackId, const AuxSendsParams& params)
516 : {
517 : ONLY_AUDIO_MAIN_THREAD;
518 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::SetAuxSendsParams, RpcPacker::pack(trackId, params));
519 0 : channel()->send(msg);
520 0 : }
521 :
522 0 : async::Channel<TrackId, AudioSourceParams> Playback::sourceParamsChanged() const
523 : {
524 0 : return m_sourceParamsChanged;
525 : }
526 :
527 0 : async::Channel<TrackId, ControlParams> Playback::controlParamsChanged() const
528 : {
529 0 : return m_controlParamsChanged;
530 : }
531 :
532 0 : async::Channel<TrackId, AudioFxChain> Playback::fxChainParamsChanged() const
533 : {
534 0 : return m_fxChainParamsChanged;
535 : }
536 :
537 0 : async::Channel<TrackId, AuxSendsParams> Playback::auxSendsParamsChanged() const
538 : {
539 0 : return m_auxSendsParamsChanged;
540 : }
541 :
542 : // Same for master
543 0 : async::Promise<TrackParams> Playback::masterParams() const
544 : {
545 0 : return params(MASTER_TRACK_ID);
546 : }
547 :
548 0 : void Playback::setMasterControlParams(const ControlParams& params)
549 : {
550 0 : setControlParams(MASTER_TRACK_ID, params);
551 0 : }
552 :
553 0 : void Playback::setMasterFxChainParams(const AudioFxChain& params)
554 : {
555 0 : setFxChainParams(MASTER_TRACK_ID, params);
556 0 : }
557 :
558 0 : void Playback::setMasterAuxSendsParams(const AuxSendsParams& params)
559 : {
560 0 : setAuxSendsParams(MASTER_TRACK_ID, params);
561 0 : }
562 :
563 0 : async::Channel<ControlParams> Playback::masterControlParamsChanged() const
564 : {
565 0 : return m_masterControlParamsChanged;
566 : }
567 :
568 0 : async::Channel<AudioFxChain> Playback::masterFxChainParamsChanged() const
569 : {
570 0 : return m_masterFxChainParamsChanged;
571 : }
572 :
573 0 : async::Channel<AuxSendsParams> Playback::masterAuxSendsParamsChanged() const
574 : {
575 0 : return m_masterAuxSendsParamsChanged;
576 : }
577 :
578 0 : void Playback::processInput(const TrackId trackId) const
579 : {
580 : ONLY_AUDIO_MAIN_THREAD;
581 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::ProcessInput, RpcPacker::pack(trackId));
582 0 : channel()->send(msg);
583 0 : }
584 :
585 0 : muse::async::Promise<InputProcessingProgress> Playback::inputProcessingProgress(const TrackId trackId) const
586 : {
587 : ONLY_AUDIO_MAIN_THREAD;
588 0 : return async::make_promise<InputProcessingProgress>([this, trackId](auto resolve, auto reject) {
589 : ONLY_AUDIO_MAIN_THREAD;
590 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetInputProcessingProgress, RpcPacker::pack(trackId));
591 0 : channel()->send(msg, [this, trackId, resolve, reject](const Msg& res) {
592 : ONLY_AUDIO_MAIN_THREAD;
593 0 : Ret ret;
594 0 : bool isStarted = false;
595 0 : StreamId streamId = 0;
596 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret, isStarted, streamId)) {
597 0 : doReject(MsgCode::GetInputProcessingProgress, reject, audio::make_ret(Err::InvalidRpcData));
598 0 : return;
599 : }
600 :
601 0 : if (ret) {
602 0 : InputProcessingProgress prog;
603 0 : prog.isStarted = isStarted;
604 0 : channel()->addReceiveStream(ctxId(), StreamName::InputProcessingProgressStream, streamId, prog.processedChannel);
605 0 : addTrackStream(trackId, streamId);
606 0 : (void)resolve(prog);
607 0 : } else {
608 0 : doReject(MsgCode::GetInputProcessingProgress, reject, ret);
609 : }
610 0 : });
611 0 : return Promise<InputProcessingProgress>::dummy_result();
612 0 : }, PromiseType::AsyncByBody);
613 : }
614 :
615 0 : void Playback::clearCache(const TrackId trackId) const
616 : {
617 : ONLY_AUDIO_MAIN_THREAD;
618 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::ClearCache, RpcPacker::pack(trackId));
619 0 : channel()->send(msg);
620 0 : }
621 :
622 0 : void Playback::clearSources()
623 : {
624 : ONLY_AUDIO_MAIN_THREAD;
625 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::ClearSources);
626 0 : channel()->send(msg);
627 0 : }
628 :
629 0 : void Playback::clearMasterOutputParams()
630 : {
631 : ONLY_AUDIO_MAIN_THREAD;
632 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::ClearMasterOutputParams);
633 0 : channel()->send(msg);
634 0 : }
635 :
636 0 : void Playback::clearAllFx()
637 : {
638 : ONLY_AUDIO_MAIN_THREAD;
639 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::ClearAllFx);
640 0 : channel()->send(msg);
641 0 : }
642 :
643 : // Signals
644 2 : async::Promise<AudioSignalChanges> Playback::signalChanges(const TrackId trackId) const
645 : {
646 : ONLY_AUDIO_MAIN_THREAD;
647 4 : return async::make_promise<AudioSignalChanges>([this, trackId](auto resolve, auto reject) {
648 : ONLY_AUDIO_MAIN_THREAD;
649 2 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetSignalChanges, RpcPacker::pack(trackId));
650 4 : channel()->send(msg, [this, trackId, resolve, reject](const Msg& res) {
651 : ONLY_AUDIO_MAIN_THREAD;
652 2 : RetVal<StreamId> ret;
653 2 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
654 0 : doReject(MsgCode::GetSignalChanges, reject, audio::make_ret(Err::InvalidRpcData));
655 0 : return;
656 : }
657 :
658 2 : if (ret.ret) {
659 2 : AudioSignalChanges ch;
660 2 : channel()->addReceiveStream(ctxId(), StreamName::AudioSignalStream, ret.val, ch);
661 2 : addTrackStream(trackId, ret.val);
662 2 : (void)resolve(ch);
663 2 : } else {
664 0 : doReject(MsgCode::GetSignalChanges, reject, ret.ret);
665 : }
666 2 : });
667 4 : return Promise<AudioSignalChanges>::dummy_result();
668 6 : }, PromiseType::AsyncByBody);
669 : }
670 :
671 0 : async::Promise<AudioSignalChanges> Playback::masterSignalChanges() const
672 : {
673 0 : return signalChanges(MASTER_TRACK_ID);
674 : }
675 :
676 0 : async::Promise<AutomatedControlParamsChanges> Playback::automatedControlParamsChanges(const TrackId trackId) const
677 : {
678 : ONLY_AUDIO_MAIN_THREAD;
679 0 : return async::make_promise<AutomatedControlParamsChanges>([this, trackId](auto resolve, auto reject) {
680 : ONLY_AUDIO_MAIN_THREAD;
681 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetAutomatedControlParamsChanges, RpcPacker::pack(trackId));
682 0 : channel()->send(msg, [this, trackId, resolve, reject](const Msg& res) {
683 : ONLY_AUDIO_MAIN_THREAD;
684 0 : RetVal<StreamId> ret;
685 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
686 0 : doReject(MsgCode::GetAutomatedControlParamsChanges, reject, audio::make_ret(Err::InvalidRpcData));
687 0 : return;
688 : }
689 :
690 0 : if (ret.ret) {
691 0 : AutomatedControlParamsChanges ch;
692 0 : channel()->addReceiveStream(ctxId(), StreamName::AutomatedControlParamsStream, ret.val, ch);
693 0 : addTrackStream(trackId, ret.val);
694 0 : (void)resolve(ch);
695 0 : } else {
696 0 : doReject(MsgCode::GetAutomatedControlParamsChanges, reject, ret.ret);
697 : }
698 0 : });
699 0 : return Promise<AutomatedControlParamsChanges>::dummy_result();
700 0 : }, PromiseType::AsyncByBody);
701 : }
702 :
703 0 : async::Promise<bool> Playback::saveSoundTrack(const SoundTrackFormat& format, io::IODevice& dstDevice)
704 : {
705 : ONLY_AUDIO_MAIN_THREAD;
706 0 : return async::make_promise<bool>([this, format, &dstDevice](auto resolve, auto reject) {
707 : ONLY_AUDIO_MAIN_THREAD;
708 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::SaveSoundTrack, RpcPacker::pack(format, reinterpret_cast<uintptr_t>(&dstDevice)));
709 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
710 : ONLY_AUDIO_MAIN_THREAD;
711 0 : Ret ret;
712 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
713 0 : doReject(MsgCode::SaveSoundTrack, reject, audio::make_ret(Err::InvalidRpcData));
714 0 : return;
715 : }
716 :
717 0 : if (ret) {
718 0 : (void)resolve(true);
719 : } else {
720 0 : doReject(MsgCode::SaveSoundTrack, reject, ret);
721 : }
722 0 : });
723 0 : return Promise<bool>::dummy_result();
724 0 : }, PromiseType::AsyncByBody);
725 : }
726 :
727 0 : void Playback::abortSavingAllSoundTracks()
728 : {
729 : ONLY_AUDIO_MAIN_THREAD;
730 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::AbortSavingAllSoundTracks);
731 0 : channel()->send(msg);
732 0 : }
733 :
734 0 : SaveSoundTrackProgress Playback::saveSoundTrackProgressChanged() const
735 : {
736 0 : if (!m_saveSoundTrackProgressStreamInited) {
737 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetSaveSoundTrackProgress);
738 0 : channel()->send(msg, [this](const Msg& res) {
739 : ONLY_AUDIO_MAIN_THREAD;
740 0 : RetVal<StreamId> ret;
741 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
742 0 : return;
743 : }
744 :
745 0 : if (!ret.ret) {
746 0 : LOGE() << "GetSaveSoundTrackProgress failed: " << ret.ret.toString();
747 0 : return;
748 : }
749 :
750 0 : if (m_saveSoundTrackProgressStreamId == 0) {
751 0 : m_saveSoundTrackProgressStreamId = ret.val;
752 0 : channel()->addReceiveStream(ctxId(), StreamName::SaveSoundTrackProgressStream,
753 : m_saveSoundTrackProgressStreamId,
754 0 : m_saveSoundTrackProgressStream);
755 : }
756 :
757 0 : assert(m_saveSoundTrackProgressStreamId == ret.val);
758 0 : });
759 :
760 0 : m_saveSoundTrackProgressStreamInited = true;
761 0 : }
762 :
763 0 : return m_saveSoundTrackProgressStream;
764 : }
|