1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use crate::call_async;
5use crate::event::Publisher;
6use crate::service_context::{ExternalServiceProxy, ServiceContext};
7use anyhow::{anyhow, Context as _, Error};
8use fidl::endpoints::Proxy as _;
9use fidl_fuchsia_media::AudioRenderUsage2;
10use fidl_fuchsia_media_sounds::{PlayerMarker, PlayerProxy};
11use futures::lock::Mutex;
12use std::collections::HashSet;
13use std::rc::Rc;
14use {fidl_fuchsia_io as fio, fuchsia_async as fasync};
1516/// Creates a file-based sound from a resource file.
17fn resource_file(name: &str) -> Result<fidl::endpoints::ClientEnd<fio::FileMarker>, Error> {
18let path = format!("/config/data/{name}");
19 fuchsia_fs::file::open_in_namespace(&path, fio::PERM_READABLE)
20 .with_context(|| format!("opening resource file: {path}"))?
21.into_client_end()
22 .map_err(|_: fio::FileProxy| {
23anyhow!("failed to convert new Proxy to ClientEnd for resource file {path}")
24 })
25}
2627/// Establish a connection to the sound player and return the proxy representing the service.
28/// Will not do anything if the sound player connection is already established.
29pub(super) async fn connect_to_sound_player(
30 publisher: Publisher,
31 service_context_handle: Rc<ServiceContext>,
32 sound_player_connection: Rc<Mutex<Option<ExternalServiceProxy<PlayerProxy>>>>,
33) {
34let mut sound_player_connection_lock = sound_player_connection.lock().await;
35if sound_player_connection_lock.is_none() {
36*sound_player_connection_lock = service_context_handle
37 .connect_with_publisher::<PlayerMarker>(publisher)
38 .await
39.context("Connecting to fuchsia.media.sounds.Player")
40 .map_err(|e| log::error!("Failed to connect to fuchsia.media.sounds.Player: {}", e))
41 .ok()
42 }
43}
4445/// Plays a sound with the given `id` and `file_name` via the `sound_player_proxy`.
46///
47/// The `id` and `file_name` are expected to be unique and mapped 1:1 to each other. This allows
48/// the sound file to be reused without having to load it again.
49pub(super) async fn play_sound<'a>(
50 sound_player_proxy: &ExternalServiceProxy<PlayerProxy>,
51 file_name: &'a str,
52 id: u32,
53 added_files: Rc<Mutex<HashSet<&'a str>>>,
54) -> Result<(), Error> {
55// New sound, add it to the sound player set.
56if added_files.lock().await.insert(file_name) {
57let sound_file_channel = match resource_file(file_name) {
58Ok(file) => Some(file),
59Err(e) => return Err(anyhow!("[earcons] Failed to convert sound file: {}", e)),
60 };
61if let Some(file_channel) = sound_file_channel {
62match call_async!(sound_player_proxy => add_sound_from_file(id, file_channel)).await {
63Ok(_) => log::debug!("[earcons] Added sound to Player: {}", file_name),
64Err(e) => {
65return Err(anyhow!("[earcons] Unable to add sound to Player: {}", e));
66 }
67 };
68 }
69 }
7071let sound_player_proxy = sound_player_proxy.clone();
72// This fasync thread is needed so that the earcons sounds can play rapidly and not wait
73 // for the previous sound to finish to send another request.
74fasync::Task::local(async move {
75if let Err(e) =
76call_async!(sound_player_proxy => play_sound2(id, AudioRenderUsage2::Background)).await
77{
78log::error!("[earcons] Unable to Play sound from Player: {}", e);
79 };
80 })
81 .detach();
82Ok(())
83}