1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "audio_haptic_sound_low_latency_impl.h"
17
18 #include <fcntl.h>
19
20 #include "isoundpool.h"
21 #include "audio_haptic_log.h"
22 #include "media_errors.h"
23
24 namespace {
25 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_AUDIO_NAPI, "AudioHapticSoundLowLatencyImpl"};
26 }
27
28 namespace OHOS {
29 namespace Media {
30 const int32_t MAX_SOUND_POOL_STREAMS = 1; // ensure that only one stream for sound pool is playing.
31 const int32_t LOAD_WAIT_SECONDS = 2;
32
AudioHapticSoundLowLatencyImpl(const std::string & audioUri,const bool & muteAudio,const AudioStandard::StreamUsage & streamUsage)33 AudioHapticSoundLowLatencyImpl::AudioHapticSoundLowLatencyImpl(const std::string &audioUri, const bool &muteAudio,
34 const AudioStandard::StreamUsage &streamUsage)
35 : audioUri_(audioUri),
36 muteAudio_(muteAudio),
37 streamUsage_(streamUsage)
38 {
39 }
40
~AudioHapticSoundLowLatencyImpl()41 AudioHapticSoundLowLatencyImpl::~AudioHapticSoundLowLatencyImpl()
42 {
43 if (soundPoolPlayer_ != nullptr) {
44 ReleaseSoundPoolPlayer();
45 }
46 }
47
LoadSoundPoolPlayer()48 int32_t AudioHapticSoundLowLatencyImpl::LoadSoundPoolPlayer()
49 {
50 MEDIA_LOGI("Enter LoadSoundPoolPlayer()");
51
52 AudioStandard::AudioRendererInfo audioRendererInfo;
53 audioRendererInfo.contentType = AudioStandard::ContentType::CONTENT_TYPE_UNKNOWN;
54 audioRendererInfo.streamUsage = streamUsage_;
55 audioRendererInfo.rendererFlags = 1;
56
57 soundPoolPlayer_ = SoundPoolFactory::CreateSoundPool(MAX_SOUND_POOL_STREAMS, audioRendererInfo);
58 CHECK_AND_RETURN_RET_LOG(soundPoolPlayer_ != nullptr, MSERR_INVALID_VAL,
59 "Failed to create sound pool player instance");
60
61 soundPoolCallback_ = std::make_shared<AHSoundLowLatencyCallback>(shared_from_this());
62 CHECK_AND_RETURN_RET_LOG(soundPoolCallback_ != nullptr, MSERR_INVALID_VAL, "Failed to create callback object");
63 soundPoolPlayer_->SetSoundPoolCallback(soundPoolCallback_);
64
65 firstFrameCallback_ = std::make_shared<AHSoundFirstFrameCallback>(shared_from_this());
66 CHECK_AND_RETURN_RET_LOG(firstFrameCallback_ != nullptr, MSERR_INVALID_VAL, "Failed to create callback object");
67 soundPoolPlayer_->SetSoundPoolFrameWriteCallback(firstFrameCallback_);
68
69 configuredAudioUri_ = "";
70 playerState_ = AudioHapticPlayerState::STATE_NEW;
71 return MSERR_OK;
72 }
73
PrepareSound()74 int32_t AudioHapticSoundLowLatencyImpl::PrepareSound()
75 {
76 MEDIA_LOGI("Enter PrepareSound with sound pool");
77 std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
78 int32_t result = LoadSoundPoolPlayer();
79 CHECK_AND_RETURN_RET_LOG(result == MSERR_OK && soundPoolPlayer_ != nullptr, MSERR_INVALID_STATE,
80 "Audio haptic player(soundpool) instance is null");
81
82 if (!configuredAudioUri_.empty() && configuredAudioUri_ == audioUri_) {
83 MEDIA_LOGI("Prepare: The audioUri_ uri has been loaded. Return directly.");
84 return MSERR_OK;
85 }
86
87 MEDIA_LOGI("Set audio source to soundpool. audioUri [%{public}s]", audioUri_.c_str());
88 char realPathRes[PATH_MAX + 1] = {'\0'};
89 CHECK_AND_RETURN_RET_LOG((strlen(audioUri_.c_str()) < PATH_MAX) &&
90 (realpath(audioUri_.c_str(), realPathRes) != nullptr), MSERR_UNSUPPORT_FILE, "Invalid file path length");
91 std::string realPathStr(realPathRes);
92
93 if (fileDes_ != -1) {
94 (void)close(fileDes_);
95 fileDes_ = -1;
96 }
97 fileDes_ = open(realPathStr.c_str(), O_RDONLY);
98 if (fileDes_ == -1) {
99 MEDIA_LOGE("Prepare: Failed to open the audio uri for sound pool.");
100 return MSERR_OPEN_FILE_FAILED;
101 }
102 std::string uri = "fd://" + std::to_string(fileDes_);
103
104 int32_t soundID = soundPoolPlayer_->Load(uri);
105 if (soundID < 0) {
106 MEDIA_LOGE("Prepare: Failed to load soundPool uri.");
107 return MSERR_OPEN_FILE_FAILED;
108 }
109 std::unique_lock<std::mutex> lockPrepare(prepareMutex_);
110 prepareCond_.wait_for(lockPrepare, std::chrono::seconds(LOAD_WAIT_SECONDS),
111 [this]() { return isPrepared_ || isReleased_ || isUnsupportedFile_; });
112 CHECK_AND_RETURN_RET_LOG(!isReleased_, MSERR_INVALID_OPERATION, "The sound pool is released when it is preparing.");
113 CHECK_AND_RETURN_RET_LOG(!isUnsupportedFile_, MSERR_UNSUPPORT_FILE,
114 "Failed to load audio uri: report unsupported file err when loading source for sound pool.");
115 CHECK_AND_RETURN_RET_LOG(isPrepared_, MSERR_OPEN_FILE_FAILED, "Failed to load audio uri: time out.");
116
117 // The audio source has been loaded for sound pool
118 soundID_ = soundID;
119 configuredAudioUri_ = audioUri_;
120 playerState_ = AudioHapticPlayerState::STATE_PREPARED;
121
122 return MSERR_OK;
123 }
124
StartSound()125 int32_t AudioHapticSoundLowLatencyImpl::StartSound()
126 {
127 MEDIA_LOGI("Enter StartSound with sound pool");
128 std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
129 if (playerState_ != AudioHapticPlayerState::STATE_PREPARED &&
130 playerState_ != AudioHapticPlayerState::STATE_RUNNING &&
131 playerState_ != AudioHapticPlayerState::STATE_STOPPED) {
132 MEDIA_LOGE("SoundPoolPlayer not Prepared");
133 return MSERR_START_FAILED;
134 }
135 CHECK_AND_RETURN_RET_LOG(soundPoolPlayer_ != nullptr, MSERR_INVALID_STATE, "Sound pool player instance is null");
136
137 PlayParams playParams {
138 .loop = (loop_ ? -1 : 0),
139 .rate = 0, // default AudioRendererRate::RENDER_RATE_NORMAL
140 .leftVolume = volume_ * (muteAudio_ ? 0 : 1),
141 .rightVolume = volume_ * (muteAudio_ ? 0 : 1),
142 .priority = 0,
143 .parallelPlayFlag = false,
144 };
145 streamID_ = soundPoolPlayer_->Play(soundID_, playParams);
146 playerState_ = AudioHapticPlayerState::STATE_RUNNING;
147
148 return MSERR_OK;
149 }
150
StopSound()151 int32_t AudioHapticSoundLowLatencyImpl::StopSound()
152 {
153 MEDIA_LOGI("Enter StopSound with sound pool");
154 std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
155 CHECK_AND_RETURN_RET_LOG(soundPoolPlayer_ != nullptr, MSERR_INVALID_STATE, "Sound pool player instance is null");
156
157 (void)soundPoolPlayer_->Stop(streamID_);
158 playerState_ = AudioHapticPlayerState::STATE_STOPPED;
159
160 return MSERR_OK;
161 }
162
ReleaseSound()163 int32_t AudioHapticSoundLowLatencyImpl::ReleaseSound()
164 {
165 MEDIA_LOGI("Enter ReleaseSound with sound pool");
166 {
167 std::lock_guard<std::mutex> lockPrepare(prepareMutex_);
168 isReleased_ = true;
169 prepareCond_.notify_one();
170 }
171 std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
172 CHECK_AND_RETURN_RET_LOG(playerState_ != AudioHapticPlayerState::STATE_RELEASED, MSERR_OK,
173 "The audio haptic player has been released.");
174 ReleaseSoundPoolPlayer();
175 playerState_ = AudioHapticPlayerState::STATE_RELEASED;
176
177 return MSERR_OK;
178 }
179
ReleaseSoundPoolPlayer()180 void AudioHapticSoundLowLatencyImpl::ReleaseSoundPoolPlayer()
181 {
182 if (soundPoolPlayer_ != nullptr) {
183 (void)soundPoolPlayer_->Release();
184 soundPoolPlayer_ = nullptr;
185 }
186 soundPoolCallback_ = nullptr;
187 if (fileDes_ != -1) {
188 (void)close(fileDes_);
189 fileDes_ = -1;
190 }
191 }
192
SetVolume(float volume)193 int32_t AudioHapticSoundLowLatencyImpl::SetVolume(float volume)
194 {
195 MEDIA_LOGI("AudioHapticSoundLowLatencyImpl::SetVolume %{public}f", volume);
196 if (volume < 0.0f || volume > 1.0f) {
197 MEDIA_LOGE("SetVolume: the volume value is invalid.");
198 return MSERR_INVALID_VAL;
199 }
200
201 std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
202 int32_t result = MSERR_OK;
203 volume_ = volume;
204
205 if (playerState_ != AudioHapticPlayerState::STATE_PREPARED &&
206 playerState_ != AudioHapticPlayerState::STATE_RUNNING) {
207 MEDIA_LOGI("Audio haptic player is not prepared or running. No need to modify player");
208 return result;
209 }
210 if (streamID_ != -1) {
211 float actualVolume = volume_ * (muteAudio_ ? 0 : 1);
212 result = soundPoolPlayer_->SetVolume(streamID_, actualVolume, actualVolume);
213 }
214 return result;
215 }
216
SetLoop(bool loop)217 int32_t AudioHapticSoundLowLatencyImpl::SetLoop(bool loop)
218 {
219 MEDIA_LOGI("AudioHapticSoundLowLatencyImpl::SetLoop %{public}d", loop);
220 std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
221 int32_t result = MSERR_OK;
222 loop_ = loop;
223
224 if (playerState_ != AudioHapticPlayerState::STATE_PREPARED &&
225 playerState_ != AudioHapticPlayerState::STATE_RUNNING) {
226 MEDIA_LOGI("Audio haptic player is not prepared or running. No need to modify player");
227 return result;
228 }
229 if (streamID_ != -1) {
230 int32_t loopCount = loop_ ? -1 : 0;
231 result = soundPoolPlayer_->SetLoop(streamID_, loopCount);
232 }
233 return result;
234 }
235
GetAudioCurrentTime()236 int32_t AudioHapticSoundLowLatencyImpl::GetAudioCurrentTime()
237 {
238 MEDIA_LOGE("GetAudioCurrentTime is unsupported for sound pool.");
239 return -1;
240 }
241
SetAudioHapticSoundCallback(const std::shared_ptr<AudioHapticSoundCallback> & callback)242 int32_t AudioHapticSoundLowLatencyImpl::SetAudioHapticSoundCallback(
243 const std::shared_ptr<AudioHapticSoundCallback> &callback)
244 {
245 if (callback == nullptr) {
246 MEDIA_LOGE("The audio haptic player callback is nullptr.");
247 return MSERR_INVALID_VAL;
248 }
249
250 std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
251 audioHapticPlayerCallback_ = callback;
252 return MSERR_OK;
253 }
254
NotifyPreparedEvent()255 void AudioHapticSoundLowLatencyImpl::NotifyPreparedEvent()
256 {
257 std::lock_guard<std::mutex> lockPrepare(prepareMutex_);
258 isPrepared_ = true;
259 prepareCond_.notify_one();
260 }
261
NotifyErrorEvent(int32_t errorCode)262 void AudioHapticSoundLowLatencyImpl::NotifyErrorEvent(int32_t errorCode)
263 {
264 MediaServiceErrCode mediaErr = static_cast<MediaServiceErrCode>(errorCode);
265 if (mediaErr == MSERR_UNSUPPORT_FILE) {
266 std::lock_guard<std::mutex> lockPrepare(prepareMutex_);
267 isUnsupportedFile_ = true;
268 prepareCond_.notify_one();
269 }
270
271 std::shared_ptr<AudioHapticSoundCallback> cb = audioHapticPlayerCallback_.lock();
272 if (cb != nullptr) {
273 MEDIA_LOGI("NotifyFirstFrameEvent for audio haptic player");
274 cb->OnError(errorCode);
275 } else {
276 MEDIA_LOGE("NotifyFirstFrameEvent: audioHapticPlayerCallback_ is nullptr");
277 }
278 }
279
NotifyFirstFrameEvent(uint64_t latency)280 void AudioHapticSoundLowLatencyImpl::NotifyFirstFrameEvent(uint64_t latency)
281 {
282 std::shared_ptr<AudioHapticSoundCallback> cb = audioHapticPlayerCallback_.lock();
283 if (cb != nullptr) {
284 MEDIA_LOGI("NotifyFirstFrameEvent for audio haptic player");
285 cb->OnFirstFrameWriting(latency);
286 } else {
287 MEDIA_LOGE("NotifyFirstFrameEvent: audioHapticPlayerCallback_ is nullptr");
288 }
289 }
290
NotifyEndOfStreamEvent()291 void AudioHapticSoundLowLatencyImpl::NotifyEndOfStreamEvent()
292 {
293 MEDIA_LOGI("NotifyEndOfStreamEvent");
294 playerState_ = AudioHapticPlayerState::STATE_STOPPED;
295 std::shared_ptr<AudioHapticSoundCallback> cb = audioHapticPlayerCallback_.lock();
296 if (cb != nullptr) {
297 MEDIA_LOGI("NotifyEndOfStreamEvent for audio haptic player");
298 cb->OnEndOfStream();
299 } else {
300 MEDIA_LOGE("NotifyEndOfStreamEvent: audioHapticPlayerCallback_ is nullptr");
301 }
302 }
303
304 // Callback class symbols
AHSoundLowLatencyCallback(std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl)305 AHSoundLowLatencyCallback::AHSoundLowLatencyCallback(
306 std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl)
307 : soundLowLatencyImpl_(soundLowLatencyImpl) {}
308
OnLoadCompleted(int32_t soundId)309 void AHSoundLowLatencyCallback::OnLoadCompleted(int32_t soundId)
310 {
311 MEDIA_LOGI("OnLoadCompleted reported from sound pool.");
312 std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl = soundLowLatencyImpl_.lock();
313 if (soundLowLatencyImpl == nullptr) {
314 MEDIA_LOGE("The audio haptic player for low latency mode has been released.");
315 return;
316 }
317 soundLowLatencyImpl->NotifyPreparedEvent();
318 }
319
OnPlayFinished()320 void AHSoundLowLatencyCallback::OnPlayFinished()
321 {
322 MEDIA_LOGI("OnPlayFinished reported from sound pool.");
323 std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl = soundLowLatencyImpl_.lock();
324 if (soundLowLatencyImpl == nullptr) {
325 MEDIA_LOGE("The audio haptic player for low latency mode has been released.");
326 return;
327 }
328 soundLowLatencyImpl->NotifyEndOfStreamEvent();
329 }
330
OnError(int32_t errorCode)331 void AHSoundLowLatencyCallback::OnError(int32_t errorCode)
332 {
333 MEDIA_LOGE("OnError reported from sound pool: %{public}d", errorCode);
334 std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl = soundLowLatencyImpl_.lock();
335 if (soundLowLatencyImpl == nullptr) {
336 MEDIA_LOGE("The audio haptic player for low latency mode has been released.");
337 return;
338 }
339 soundLowLatencyImpl->NotifyErrorEvent(errorCode);
340 }
341
AHSoundFirstFrameCallback(std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl)342 AHSoundFirstFrameCallback::AHSoundFirstFrameCallback(
343 std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl)
344 : soundLowLatencyImpl_(soundLowLatencyImpl) {}
345
OnFirstAudioFrameWritingCallback(uint64_t & latency)346 void AHSoundFirstFrameCallback::OnFirstAudioFrameWritingCallback(uint64_t &latency)
347 {
348 MEDIA_LOGI("OnFirstAudioFrameWritingCallback from Soundpool. Latency %{public}" PRIu64 "", latency);
349 std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl = soundLowLatencyImpl_.lock();
350 if (soundLowLatencyImpl == nullptr) {
351 MEDIA_LOGE("The audio haptic player for low latency mode has been released.");
352 return;
353 }
354 soundLowLatencyImpl->NotifyFirstFrameEvent(latency);
355 }
356 } // namesapce AudioStandard
357 } // namespace OHOS
358