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) 2024 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 "player.h"
23 :
24 : #include "audio/common/rpc/rpcpacker.h"
25 : #include "audio/common/audiosanitizer.h"
26 :
27 : #include "log.h"
28 :
29 : using namespace muse;
30 : using namespace muse::async;
31 : using namespace muse::audio;
32 : using namespace muse::audio::rpc;
33 :
34 14 : rpc::CtxId Player::ctxId() const
35 : {
36 14 : return rpc::ctxId(iocContext());
37 : }
38 :
39 5 : void Player::init()
40 : {
41 : ONLY_AUDIO_MAIN_THREAD;
42 :
43 : //! NOTE Subscribe and request initial state
44 :
45 : {
46 5 : m_playbackStatusChanged.onReceive(this, [this](PlaybackStatus st) {
47 2 : m_playbackStatus = st;
48 2 : });
49 :
50 5 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetPlaybackStatus);
51 5 : channel()->send(msg, [this](const Msg& res) {
52 : ONLY_AUDIO_MAIN_THREAD;
53 2 : Ret ret;
54 2 : PlaybackStatus status = PlaybackStatus::Stopped;
55 2 : StreamId streamId = 0;
56 2 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret, status, streamId)) {
57 0 : return;
58 : }
59 :
60 2 : if (ret) {
61 2 : channel()->addReceiveStream(ctxId(), StreamName::PlaybackStatusStream, streamId, m_playbackStatusChanged);
62 : //! NOTE Send initial state
63 2 : m_playbackStatusChanged.send(status);
64 : } else {
65 0 : LOGE() << "GetPlaybackStatus failed: " << ret.toString();
66 : }
67 2 : });
68 5 : }
69 :
70 : {
71 5 : m_playbackPositionChanged.onReceive(this, [this](const secs_t newPos) {
72 2 : m_playbackPosition = newPos;
73 2 : });
74 :
75 5 : Msg msg = rpc::make_request(ctxId(), MsgCode::GetPlaybackPosition);
76 5 : channel()->send(msg, [this](const Msg& res) {
77 : ONLY_AUDIO_MAIN_THREAD;
78 2 : Ret ret;
79 2 : secs_t pos = 0.0;
80 2 : StreamId streamId = 0;
81 2 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret, pos, streamId)) {
82 0 : return;
83 : }
84 :
85 2 : if (ret) {
86 2 : channel()->addReceiveStream(ctxId(), StreamName::PlaybackPositionStream, streamId, m_playbackPositionChanged);
87 : //! NOTE Send initial state
88 2 : m_playbackPositionChanged.send(pos);
89 : } else {
90 0 : LOGE() << "GetPlaybackPosition failed: " << ret.toString();
91 : }
92 2 : });
93 5 : }
94 5 : }
95 :
96 2 : void Player::deinit()
97 : {
98 : ONLY_AUDIO_MAIN_THREAD;
99 :
100 2 : m_playbackStatusChanged.disconnect(this);
101 2 : m_playbackPositionChanged.disconnect(this);
102 2 : }
103 :
104 0 : async::Promise<Ret> Player::prepareToPlay()
105 : {
106 : ONLY_AUDIO_MAIN_THREAD;
107 0 : return async::make_promise<Ret>([this](auto resolve, auto) {
108 : ONLY_AUDIO_MAIN_THREAD;
109 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::PrepareToPlay);
110 0 : channel()->send(msg, [resolve](const Msg& res) {
111 : ONLY_AUDIO_MAIN_THREAD;
112 0 : Ret ret;
113 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
114 0 : (void)resolve(make_ret(Ret::Code::UnknownError));
115 0 : return;
116 : }
117 :
118 0 : (void)resolve(ret);
119 0 : });
120 0 : return Promise<Ret>::dummy_result();
121 0 : }, PromiseType::AsyncByBody);
122 : }
123 :
124 0 : void Player::play(const secs_t delay)
125 : {
126 : ONLY_AUDIO_MAIN_THREAD;
127 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::Play, RpcPacker::pack(delay));
128 0 : channel()->send(msg);
129 0 : }
130 :
131 0 : void Player::seek(const secs_t newPosition, const bool flushSound)
132 : {
133 : ONLY_AUDIO_MAIN_THREAD;
134 :
135 0 : if (m_playbackPosition == newPosition) {
136 0 : return;
137 : }
138 :
139 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::Seek, RpcPacker::pack(newPosition, flushSound));
140 0 : channel()->send(msg);
141 0 : }
142 :
143 0 : void Player::stop()
144 : {
145 : ONLY_AUDIO_MAIN_THREAD;
146 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::Stop);
147 0 : channel()->send(msg);
148 0 : }
149 :
150 0 : void Player::pause()
151 : {
152 : ONLY_AUDIO_MAIN_THREAD;
153 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::Pause);
154 0 : channel()->send(msg);
155 0 : }
156 :
157 0 : void Player::resume(const secs_t delay)
158 : {
159 : ONLY_AUDIO_MAIN_THREAD;
160 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::Resume, RpcPacker::pack(delay));
161 0 : channel()->send(msg);
162 0 : }
163 :
164 0 : void Player::setDuration(const secs_t duration)
165 : {
166 : ONLY_AUDIO_MAIN_THREAD;
167 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::SetDuration, RpcPacker::pack(duration));
168 0 : channel()->send(msg);
169 0 : }
170 :
171 0 : async::Promise<bool> Player::setLoop(const secs_t from, const secs_t to)
172 : {
173 : ONLY_AUDIO_MAIN_THREAD;
174 0 : return async::make_promise<bool>([this, from, to](auto resolve, auto reject) {
175 : ONLY_AUDIO_MAIN_THREAD;
176 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::SetLoop, RpcPacker::pack(from, to));
177 0 : channel()->send(msg, [resolve, reject](const Msg& res) {
178 : ONLY_AUDIO_MAIN_THREAD;
179 0 : Ret ret;
180 0 : IF_ASSERT_FAILED(RpcPacker::unpack(res.data, ret)) {
181 0 : return;
182 : }
183 :
184 0 : if (ret) {
185 0 : (void)resolve(true);
186 : } else {
187 0 : (void)reject(ret.code(), ret.text());
188 : }
189 0 : });
190 0 : return Promise<bool>::dummy_result();
191 0 : }, PromiseType::AsyncByBody);
192 : }
193 :
194 0 : void Player::resetLoop()
195 : {
196 : ONLY_AUDIO_MAIN_THREAD;
197 0 : Msg msg = rpc::make_request(ctxId(), MsgCode::ResetLoop);
198 0 : channel()->send(msg);
199 0 : }
200 :
201 1 : secs_t Player::playbackPosition() const
202 : {
203 : ONLY_AUDIO_MAIN_THREAD;
204 1 : return m_playbackPosition;
205 : }
206 :
207 0 : async::Channel<secs_t> Player::playbackPositionChanged() const
208 : {
209 : ONLY_AUDIO_MAIN_THREAD;
210 0 : return m_playbackPositionChanged;
211 : }
212 :
213 1 : PlaybackStatus Player::playbackStatus() const
214 : {
215 : ONLY_AUDIO_MAIN_THREAD;
216 1 : return m_playbackStatus;
217 : }
218 :
219 0 : async::Channel<PlaybackStatus> Player::playbackStatusChanged() const
220 : {
221 : ONLY_AUDIO_MAIN_THREAD;
222 0 : return m_playbackStatusChanged;
223 : }
|