feat: audio chooser

This commit is contained in:
Ahmet Kaan GÜMÜŞ 2024-05-13 18:40:03 +03:00
parent ada65ea7c1
commit c53e2bca7d
5 changed files with 185 additions and 47 deletions

View file

@ -1,9 +1,9 @@
use std::{fs::File, sync::Arc};
use std::{fs::File, path::Path, sync::Arc};
use iced::{
alignment,
widget::{column, container, row, slider, text::LineHeight, Container, Rule},
window, Color, Command, Subscription,
widget::{column, container, row, scrollable, slider, text::LineHeight, Container, Rule},
window, Color, Command, Length, Subscription,
};
use tokio::sync::{
broadcast::{channel, Receiver, Sender},
@ -17,6 +17,8 @@ use crate::{
Config, BUFFER_LENGTH,
};
const AUDIOS_PATH: &str = "audios";
#[derive(Debug, Clone)]
pub enum Player {
Play,
@ -32,8 +34,11 @@ struct Features {
}
#[derive(Debug)]
struct AudioFile {
struct AudioMiscellaneous {
file: Option<File>,
selected_file_name: String,
playing_file_name: String,
files: Option<Vec<String>>,
decoded_to_playing_sender: Option<Sender<f32>>,
}
@ -48,9 +53,11 @@ pub enum Event {
StopAudio,
PauseAudio,
ContinueAudio,
ChooseAudio(String),
ChangeMicrophoneVolume(f32),
ChangeAudioVolume(f32),
LoadConfig(Config),
ListFiles(Option<Vec<String>>),
IcedEvent(iced::Event),
CloseWindow(window::Id),
}
@ -115,7 +122,7 @@ pub struct Streamer {
config: Option<Config>,
data_channel: DataChannel,
communication_channel: CommunicationChannel,
audio_file: AudioFile,
audio_miscellaneous: AudioMiscellaneous,
gui_status: GUIStatus,
}
impl Default for Streamer {
@ -140,8 +147,11 @@ impl Streamer {
base_to_playing_sender: channel(1).0,
playing_to_base_sender: channel(1).0,
},
audio_file: AudioFile {
audio_miscellaneous: AudioMiscellaneous {
file: None,
selected_file_name: String::new(),
playing_file_name: String::new(),
files: None,
decoded_to_playing_sender: None,
},
gui_status: GUIStatus {
@ -165,7 +175,6 @@ impl Streamer {
Event::Connect => {
println!("Connect");
self.gui_status.are_we_connect = Condition::Loading;
let microphone_stream_receiver =
self.data_channel.microphone_stream_sender.subscribe();
let audio_stream_receiver = self.data_channel.audio_stream_sender.subscribe();
@ -265,12 +274,9 @@ impl Streamer {
println!("Play Audio");
self.gui_status.are_we_play_audio = Condition::Loading;
let file = File::open("music.mp3").unwrap();
self.audio_file.file = Some(file);
self.audio_file.decoded_to_playing_sender = Some(
self.audio_miscellaneous.decoded_to_playing_sender = Some(
channel(
self.audio_file
self.audio_miscellaneous
.file
.as_ref()
.unwrap()
@ -303,12 +309,24 @@ impl Streamer {
let base_to_playing_sender =
self.communication_channel.base_to_playing_sender.clone();
let file = self.audio_file.file.as_ref().unwrap().try_clone().unwrap();
let decoded_to_playing_sender_for_playing =
self.audio_file.decoded_to_playing_sender.clone().unwrap();
let file = self
.audio_miscellaneous
.file
.as_ref()
.unwrap()
.try_clone()
.unwrap();
let decoded_to_playing_sender_for_playing = self
.audio_miscellaneous
.decoded_to_playing_sender
.clone()
.unwrap();
let decoded_to_playing_sender_for_is_finished =
self.audio_file.decoded_to_playing_sender.clone().unwrap();
let decoded_to_playing_sender_for_is_finished = self
.audio_miscellaneous
.decoded_to_playing_sender
.clone()
.unwrap();
let audio_volume = self.gui_status.audio_volume.value.clone();
@ -407,6 +425,12 @@ impl Streamer {
Message::State,
)
}
Event::ChooseAudio(chosen_audio) => {
let path = format!("{}/{}", AUDIOS_PATH, chosen_audio);
self.audio_miscellaneous.file = Some(File::open(path).unwrap());
self.audio_miscellaneous.selected_file_name = chosen_audio;
Command::none()
}
Event::ChangeMicrophoneVolume(value) => {
*self.gui_status.microphone_volume.value.blocking_lock() = value;
let microphone_volume = self.gui_status.microphone_volume.value.clone();
@ -427,18 +451,52 @@ impl Streamer {
self.config = Some(config);
Command::none()
}
Event::ListFiles(files) => {
self.audio_miscellaneous.files = files;
Command::none()
}
Event::IcedEvent(iced_event) => match iced_event {
iced::Event::Keyboard(_) => Command::none(),
iced::Event::Mouse(_) => Command::none(),
iced::Event::Keyboard(_) => Command::perform(
async move {
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
Event::ListFiles(files)
},
Message::Event,
),
iced::Event::Mouse(_) => Command::perform(
async move {
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
Event::ListFiles(files)
},
Message::Event,
),
iced::Event::Window(id, window_event) => {
if let window::Event::CloseRequested = window_event {
self.exit(id)
} else {
Command::none()
Command::perform(
async move {
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
Event::ListFiles(files)
},
Message::Event,
)
}
}
iced::Event::Touch(_) => Command::none(),
iced::Event::PlatformSpecific(_) => Command::none(),
iced::Event::Touch(_) => Command::perform(
async move {
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
Event::ListFiles(files)
},
Message::Event,
),
iced::Event::PlatformSpecific(_) => Command::perform(
async move {
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
Event::ListFiles(files)
},
Message::Event,
),
},
Event::CloseWindow(id) => window::close(id),
},
@ -461,11 +519,14 @@ impl Streamer {
Command::none()
}
State::PlayingAudio => {
self.audio_miscellaneous.playing_file_name =
self.audio_miscellaneous.selected_file_name.clone();
self.gui_status.are_we_play_audio = Condition::Active;
self.gui_status.are_we_paused_audio = Condition::Passive;
Command::none()
}
State::StopAudio => {
self.audio_miscellaneous.playing_file_name = String::new();
self.gui_status.are_we_play_audio = Condition::Passive;
Command::none()
}
@ -545,10 +606,17 @@ impl Streamer {
play_audio_status_text = text_centered("Loading").color(color_yellow);
button_with_centered_text("Processing")
}
Condition::Passive => {
play_audio_status_text = text_centered("Passive").color(color_pink);
button_with_centered_text("Play Audio").on_press(Message::Event(Event::PlayAudio))
}
Condition::Passive => match self.audio_miscellaneous.file {
Some(_) => {
play_audio_status_text = text_centered("Passive").color(color_pink);
button_with_centered_text("Play Audio")
.on_press(Message::Event(Event::PlayAudio))
}
None => {
play_audio_status_text = text_centered("No Audio").color(color_pink);
button_with_centered_text("No Audio")
}
},
};
let pause_audio_button = if let Condition::Active = self.gui_status.are_we_play_audio {
@ -587,6 +655,29 @@ impl Streamer {
)
.step(0.01);
let mut audio_scrollable_content = column![]
.spacing(1)
.height(Length::Fill)
.width(Length::Fill);
let audio_selected = text_centered(format!(
"Selected: {}",
self.audio_miscellaneous.selected_file_name.clone()
));
let audio_playing = text_centered(format!(
"Playing: {}",
self.audio_miscellaneous.playing_file_name.clone()
));
if self.audio_miscellaneous.files.is_some() {
for file in self.audio_miscellaneous.files.as_ref().clone().unwrap() {
let button = button_with_centered_text(file)
.on_press(Message::Event(Event::ChooseAudio(file.to_string())));
audio_scrollable_content = audio_scrollable_content.push(button.height(35));
}
}
let audios_scrollable = scrollable(audio_scrollable_content)
.height(Length::Fill)
.width(Length::Fill);
let audio_info_content = column![audio_selected, audio_playing,].height(60);
let header_content = row![header].width(350).height(50);
let text_content = row![
connection_text,
@ -634,12 +725,14 @@ impl Streamer {
status_content,
Rule::horizontal(1),
volume_content,
audios_scrollable,
audio_info_content,
]
.spacing(20)
.width(350)
.height(300);
.width(Length::Fill)
.height(Length::Fill);
container(content)
.height(300)
.height(Length::Fill)
.center_x()
.align_y(alignment::Vertical::Top)
}
@ -657,6 +750,15 @@ impl Streamer {
Message::Event,
)
}
pub fn list_files() -> Command<Message> {
Command::perform(
async move {
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
Event::ListFiles(files)
},
Message::Event,
)
}
fn call_closer(
streaming_to_base_receiver: Receiver<bool>,
base_to_streaming_sender: Sender<bool>,

View file

@ -6,9 +6,9 @@ use iced::{
use crate::gui::Message;
pub fn button_with_centered_text(txt: &'static str) -> Button<'static, Message> {
pub fn button_with_centered_text<T: Into<String>>(txt: T) -> Button<'static, Message> {
button(
text(txt)
text(txt.into())
.width(Length::Fill)
.horizontal_alignment(alignment::Horizontal::Center),
)
@ -16,8 +16,8 @@ pub fn button_with_centered_text(txt: &'static str) -> Button<'static, Message>
.width(Length::Fill)
}
pub fn text_centered(txt: &'static str) -> Text {
text(txt)
pub fn text_centered<T: Into<String>>(txt: T) -> Text<'static> {
text(txt.into())
.width(Length::Fill)
.height(Length::Fill)
.horizontal_alignment(alignment::Horizontal::Center)

View file

@ -1,4 +1,4 @@
use std::{fs::File, sync::Arc, time::Duration};
use std::{fs::File, path::Path, sync::Arc, time::Duration};
use tokio::sync::{
broadcast::{Receiver, Sender},
@ -316,3 +316,29 @@ pub async fn change_audio_volume(
*audio_stream_volume.lock().await = desired_value;
State::AudioVolumeChanged
}
pub async fn list_files(folder_path: &Path) -> Option<Vec<String>> {
let mut file_names: Vec<String> = vec![];
match std::fs::read_dir(folder_path) {
Ok(entities) => {
for entity in entities {
match entity {
Ok(entity) => {
let path = entity.path();
if path.is_file() {
file_names.push(path.file_name().unwrap().to_str().unwrap().to_string())
}
}
Err(err_val) => {
eprintln!("Error: Entity | {}", err_val);
}
}
}
Some(file_names)
}
Err(err_val) => {
eprintln!("Error: Read Directory | {}", err_val);
None
}
}
}

View file

@ -6,8 +6,9 @@ async fn main() {
tokio::task::block_in_place(|| {
iced::program("Streamer GUI", Streamer::update, Streamer::view)
.centered()
.window_size((350.0, 450.0))
.window_size((350.0, 650.0))
.load(Streamer::load_config)
.load(Streamer::list_files)
.antialiasing(true)
.subscription(Streamer::subscription)
.exit_on_close_request(false)

View file

@ -38,7 +38,13 @@ pub async fn play(
let output_device_sample_rate = output_device_config.sample_rate.0;
let (mut audio_resampled_left, mut audio_resampled_right) =
decode_audio(output_device_sample_rate, file);
match decode_audio(output_device_sample_rate, file) {
Some((left, right)) => (left, right),
None => {
let_the_base_know(playing_to_base_sender, Player::Stop).await;
return;
}
};
let mut decoded_to_playing_receiver = decoded_to_playing_sender.subscribe();
for _ in 0..audio_resampled_left.clone().len() {
@ -110,7 +116,7 @@ fn err_fn(err: cpal::StreamError) {
async fn let_the_base_know(playing_to_base_sender: Sender<Player>, action: Player) {
let _ = playing_to_base_sender.send(action);
}
fn decode_audio(output_device_sample_rate: u32, file: File) -> (Vec<f64>, Vec<f64>) {
fn decode_audio(output_device_sample_rate: u32, file: File) -> Option<(Vec<f64>, Vec<f64>)> {
let mut audio_decoded_left = vec![];
let mut audio_decoded_right = vec![];
let media_source_stream = MediaSourceStream::new(Box::new(file), Default::default());
@ -120,16 +126,19 @@ fn decode_audio(output_device_sample_rate: u32, file: File) -> (Vec<f64>, Vec<f6
let metadata_options = MetadataOptions::default();
let format_options = FormatOptions::default();
let probed = symphonia::default::get_probe()
.format(
&hint,
media_source_stream,
&format_options,
&metadata_options,
)
.unwrap();
let mut probed = symphonia::default::get_probe().format(
&hint,
media_source_stream,
&format_options,
&metadata_options,
);
let mut format = probed.format;
match probed {
Ok(probed_safe) => probed = Ok(probed_safe),
Err(_) => return None,
}
let mut format = probed.unwrap().format;
let track = format
.tracks()
@ -216,5 +225,5 @@ fn decode_audio(output_device_sample_rate: u32, file: File) -> (Vec<f64>, Vec<f6
audio_resampled_left.reverse();
audio_resampled_right.reverse();
(audio_resampled_left, audio_resampled_right)
Some((audio_resampled_left, audio_resampled_right))
}