• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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