feat: ✨ decode on the go
This commit is contained in:
parent
378e94376c
commit
8247bcee21
10 changed files with 312 additions and 233 deletions
|
@ -1,5 +1,5 @@
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use front::components::{coin_status_renderer, listen_renderer, server_status_renderer};
|
use front::components::listen_renderer;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
|
@ -8,7 +8,6 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
let server_address = "https://tahinli.com.tr:2323".to_string();
|
|
||||||
rsx! {
|
rsx! {
|
||||||
page_base {}
|
page_base {}
|
||||||
listen_renderer {}
|
listen_renderer {}
|
||||||
|
|
|
@ -7,7 +7,6 @@ use futures_util::StreamExt;
|
||||||
use ringbuf::{HeapRb, Producer, SharedRb};
|
use ringbuf::{HeapRb, Producer, SharedRb};
|
||||||
use std::{io::Write, mem::MaybeUninit, sync::Arc};
|
use std::{io::Write, mem::MaybeUninit, sync::Arc};
|
||||||
|
|
||||||
|
|
||||||
use crate::{listening::listen_podcast, BUFFER_LENGTH};
|
use crate::{listening::listen_podcast, BUFFER_LENGTH};
|
||||||
|
|
||||||
pub async fn start_listening(
|
pub async fn start_listening(
|
||||||
|
@ -17,13 +16,9 @@ pub async fn start_listening(
|
||||||
if is_listening() {
|
if is_listening() {
|
||||||
log::info!("Trying Sir");
|
log::info!("Trying Sir");
|
||||||
let connect_addr = "ws://192.168.1.2:2424";
|
let connect_addr = "ws://192.168.1.2:2424";
|
||||||
|
|
||||||
let ws_stream: tokio_tungstenite_wasm::WebSocketStream;
|
let ws_stream: tokio_tungstenite_wasm::WebSocketStream;
|
||||||
match tokio_tungstenite_wasm::connect(
|
match tokio_tungstenite_wasm::connect(connect_addr).await {
|
||||||
connect_addr,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(ws_stream_connected) => ws_stream = ws_stream_connected,
|
Ok(ws_stream_connected) => ws_stream = ws_stream_connected,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
is_listening.set(false);
|
is_listening.set(false);
|
||||||
|
@ -67,10 +62,10 @@ pub async fn sound_stream(
|
||||||
log::error!("Error: Decompression | {}", err_val);
|
log::error!("Error: Decompression | {}", err_val);
|
||||||
}
|
}
|
||||||
let uncompressed_data = match decompression_writer.into_inner() {
|
let uncompressed_data = match decompression_writer.into_inner() {
|
||||||
Ok(healty_packet) => healty_packet,
|
Ok(healthy_packet) => healthy_packet,
|
||||||
Err(unhealty_packet) => {
|
Err(unhealthy_packet) => {
|
||||||
log::warn!("Warning: Unhealty Packet | {}", unhealty_packet.len());
|
log::warn!("Warning: Unhealthy Packet | {}", unhealthy_packet.len());
|
||||||
unhealty_packet
|
unhealthy_packet
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
log::info!("{}", uncompressed_data.len());
|
log::info!("{}", uncompressed_data.len());
|
||||||
|
|
|
@ -3,13 +3,24 @@ name = "streamer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = "symbols"
|
||||||
|
opt-level = 3
|
||||||
|
overflow-checks = true
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
unsafe_code = "forbid"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
brotli = "5.0.0"
|
brotli = "5.0.0"
|
||||||
cpal = "0.15.3"
|
cpal = "0.15.3"
|
||||||
futures-util = { version = "0.3.30", features = ["futures-sink", "sink"] }
|
futures-util = { version = "0.3.30", features = ["futures-sink", "sink"] }
|
||||||
iced = { git = "https://github.com/iced-rs/iced", features = ["tokio"] }
|
iced = { git = "https://github.com/iced-rs/iced", features = ["tokio"], rev = "dcdf1307006883f50083c186ca7b8656bfa60873"}
|
||||||
ringbuf = "0.3.3"
|
ringbuf = "0.3.3"
|
||||||
rubato = "0.15.0"
|
rubato = "0.15.0"
|
||||||
rustls-pemfile = "2.1.2"
|
rustls-pemfile = "2.1.2"
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
use std::{cmp::max, fs::File, path::Path, sync::Arc};
|
use std::{
|
||||||
|
cmp::max,
|
||||||
|
fs::File,
|
||||||
|
path::Path,
|
||||||
|
process::exit,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use iced::{
|
use iced::{
|
||||||
alignment,
|
alignment,
|
||||||
widget::{column, container, row, scrollable, slider, text::LineHeight, Container, Rule},
|
widget::{column, container, row, scrollable, slider, text::LineHeight, Container, Rule},
|
||||||
window, Color, Command, Length, Subscription,
|
window::{self},
|
||||||
};
|
Color, Length, Subscription, Task,
|
||||||
use tokio::sync::{
|
|
||||||
broadcast::{channel, Receiver, Sender},
|
|
||||||
Mutex,
|
|
||||||
};
|
};
|
||||||
|
use tokio::sync::broadcast::{channel, Receiver, Sender};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
gui_components::{button_with_centered_text, text_centered},
|
gui_components::{button_with_centered_text, text_centered},
|
||||||
|
@ -135,6 +139,52 @@ impl Default for Streamer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Streamer {
|
impl Streamer {
|
||||||
|
pub fn new_with_load() -> (Self, Task<Message>) {
|
||||||
|
(
|
||||||
|
Self {
|
||||||
|
config: None,
|
||||||
|
data_channel: DataChannel {
|
||||||
|
microphone_stream_sender: channel(BUFFER_LENGTH).0,
|
||||||
|
audio_stream_sender: channel(BUFFER_LENGTH).0,
|
||||||
|
},
|
||||||
|
communication_channel: CommunicationChannel {
|
||||||
|
base_to_streaming_sender: channel(1).0,
|
||||||
|
streaming_to_base_sender: channel(1).0,
|
||||||
|
streaming_to_base_is_finished: channel(1).0,
|
||||||
|
base_to_recording_sender: channel(1).0,
|
||||||
|
recording_to_base_sender: channel(1).0,
|
||||||
|
base_to_playing_sender: channel(1).0,
|
||||||
|
playing_to_base_sender: channel(1).0,
|
||||||
|
},
|
||||||
|
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 {
|
||||||
|
are_we_connect: Condition::Passive,
|
||||||
|
are_we_record: Condition::Passive,
|
||||||
|
are_we_play_audio: Condition::Passive,
|
||||||
|
are_we_paused_audio: Condition::Passive,
|
||||||
|
microphone_volume: ChangeableValue {
|
||||||
|
value: Arc::new(1.0.into()),
|
||||||
|
},
|
||||||
|
audio_volume: ChangeableValue {
|
||||||
|
value: Arc::new(1.0.into()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Task::perform(
|
||||||
|
async move {
|
||||||
|
let config = get_config();
|
||||||
|
Event::LoadConfig(config)
|
||||||
|
},
|
||||||
|
Message::Event,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
config: None,
|
config: None,
|
||||||
|
@ -172,10 +222,10 @@ impl Streamer {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, message: Message) -> Command<Message> {
|
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::Event(event) => match event {
|
Message::Event(event) => match event {
|
||||||
Event::None => Command::none(),
|
Event::None => Task::none(),
|
||||||
Event::Connect => {
|
Event::Connect => {
|
||||||
println!("Connect");
|
println!("Connect");
|
||||||
self.gui_status.are_we_connect = Condition::Loading;
|
self.gui_status.are_we_connect = Condition::Loading;
|
||||||
|
@ -206,7 +256,7 @@ impl Streamer {
|
||||||
.streaming_to_base_sender
|
.streaming_to_base_sender
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|
||||||
let connect_command = Command::perform(
|
let connect_command = Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::connect(
|
gui_utils::connect(
|
||||||
microphone_stream_receiver,
|
microphone_stream_receiver,
|
||||||
|
@ -223,7 +273,7 @@ impl Streamer {
|
||||||
Message::State,
|
Message::State,
|
||||||
);
|
);
|
||||||
|
|
||||||
let is_streaming_finished_command = Command::perform(
|
let is_streaming_finished_command = Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::is_streaming_finished(
|
gui_utils::is_streaming_finished(
|
||||||
streaming_to_base_receiver_is_streaming_finished,
|
streaming_to_base_receiver_is_streaming_finished,
|
||||||
|
@ -235,7 +285,7 @@ impl Streamer {
|
||||||
);
|
);
|
||||||
|
|
||||||
let commands = vec![connect_command, is_streaming_finished_command];
|
let commands = vec![connect_command, is_streaming_finished_command];
|
||||||
Command::batch(commands)
|
Task::batch(commands)
|
||||||
}
|
}
|
||||||
Event::Disconnect => {
|
Event::Disconnect => {
|
||||||
println!("Disconnect");
|
println!("Disconnect");
|
||||||
|
@ -248,7 +298,7 @@ impl Streamer {
|
||||||
let base_to_streaming_sender =
|
let base_to_streaming_sender =
|
||||||
self.communication_channel.base_to_streaming_sender.clone();
|
self.communication_channel.base_to_streaming_sender.clone();
|
||||||
|
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::disconnect(
|
gui_utils::disconnect(
|
||||||
streaming_to_base_receiver,
|
streaming_to_base_receiver,
|
||||||
|
@ -272,7 +322,7 @@ impl Streamer {
|
||||||
.base_to_recording_sender
|
.base_to_recording_sender
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::start_recording(
|
gui_utils::start_recording(
|
||||||
microphone_stream_sender,
|
microphone_stream_sender,
|
||||||
|
@ -293,7 +343,7 @@ impl Streamer {
|
||||||
.subscribe();
|
.subscribe();
|
||||||
let base_to_recording_sender =
|
let base_to_recording_sender =
|
||||||
self.communication_channel.base_to_recording_sender.clone();
|
self.communication_channel.base_to_recording_sender.clone();
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::stop_recording(
|
gui_utils::stop_recording(
|
||||||
recording_to_base_receiver,
|
recording_to_base_receiver,
|
||||||
|
@ -319,7 +369,7 @@ impl Streamer {
|
||||||
eprintln!("Error: Open File | {}", err_val);
|
eprintln!("Error: Open File | {}", err_val);
|
||||||
self.audio_miscellaneous.file = None;
|
self.audio_miscellaneous.file = None;
|
||||||
self.gui_status.are_we_play_audio = Condition::Passive;
|
self.gui_status.are_we_play_audio = Condition::Passive;
|
||||||
return Command::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.audio_miscellaneous.decoded_to_playing_sender = Some(
|
self.audio_miscellaneous.decoded_to_playing_sender = Some(
|
||||||
|
@ -379,7 +429,7 @@ impl Streamer {
|
||||||
|
|
||||||
let audio_volume = self.gui_status.audio_volume.value.clone();
|
let audio_volume = self.gui_status.audio_volume.value.clone();
|
||||||
|
|
||||||
let playing_command = Command::perform(
|
let playing_command = Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::start_playing(
|
gui_utils::start_playing(
|
||||||
audio_stream_sender,
|
audio_stream_sender,
|
||||||
|
@ -393,7 +443,7 @@ impl Streamer {
|
||||||
},
|
},
|
||||||
Message::State,
|
Message::State,
|
||||||
);
|
);
|
||||||
let is_finished_command = Command::perform(
|
let is_finished_command = Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::is_playing_finished(
|
gui_utils::is_playing_finished(
|
||||||
playing_to_base_receiver_is_audio_finished,
|
playing_to_base_receiver_is_audio_finished,
|
||||||
|
@ -406,7 +456,7 @@ impl Streamer {
|
||||||
Message::State,
|
Message::State,
|
||||||
);
|
);
|
||||||
let commands = vec![playing_command, is_finished_command];
|
let commands = vec![playing_command, is_finished_command];
|
||||||
Command::batch(commands)
|
Task::batch(commands)
|
||||||
}
|
}
|
||||||
Event::StopAudio => {
|
Event::StopAudio => {
|
||||||
println!("Stop Audio");
|
println!("Stop Audio");
|
||||||
|
@ -419,7 +469,7 @@ impl Streamer {
|
||||||
let base_to_playing_sender =
|
let base_to_playing_sender =
|
||||||
self.communication_channel.base_to_playing_sender.clone();
|
self.communication_channel.base_to_playing_sender.clone();
|
||||||
|
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::stop_playing(
|
gui_utils::stop_playing(
|
||||||
playing_to_base_receiver,
|
playing_to_base_receiver,
|
||||||
|
@ -441,7 +491,7 @@ impl Streamer {
|
||||||
let base_to_playing_sender =
|
let base_to_playing_sender =
|
||||||
self.communication_channel.base_to_playing_sender.clone();
|
self.communication_channel.base_to_playing_sender.clone();
|
||||||
|
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::pause_playing(
|
gui_utils::pause_playing(
|
||||||
playing_to_base_receiver,
|
playing_to_base_receiver,
|
||||||
|
@ -463,7 +513,7 @@ impl Streamer {
|
||||||
let base_to_playing_sender =
|
let base_to_playing_sender =
|
||||||
self.communication_channel.base_to_playing_sender.clone();
|
self.communication_channel.base_to_playing_sender.clone();
|
||||||
|
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
gui_utils::continue_playing(
|
gui_utils::continue_playing(
|
||||||
playing_to_base_receiver,
|
playing_to_base_receiver,
|
||||||
|
@ -486,117 +536,110 @@ impl Streamer {
|
||||||
self.audio_miscellaneous.file = None;
|
self.audio_miscellaneous.file = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Event::ChangeMicrophoneVolume(value) => {
|
Event::ChangeMicrophoneVolume(value) => {
|
||||||
*self.gui_status.microphone_volume.value.blocking_lock() = value;
|
// let microphone_volume = self.gui_status.microphone_volume.value.clone();
|
||||||
|
//*self.gui_status.microphone_volume.value.blocking_lock() = value;
|
||||||
let microphone_volume = self.gui_status.microphone_volume.value.clone();
|
let microphone_volume = self.gui_status.microphone_volume.value.clone();
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move { change_microphone_volume(value, microphone_volume).await },
|
async move { change_microphone_volume(value, microphone_volume).await },
|
||||||
Message::State,
|
Message::State,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Event::ChangeAudioVolume(value) => {
|
Event::ChangeAudioVolume(value) => {
|
||||||
*self.gui_status.audio_volume.value.blocking_lock() = value;
|
// *self.gui_status.audio_volume.value.blocking_lock() = value;
|
||||||
let audio_volume = self.gui_status.audio_volume.value.clone();
|
let audio_volume = self.gui_status.audio_volume.value.clone();
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move { change_audio_volume(value, audio_volume).await },
|
async move { change_audio_volume(value, audio_volume).await },
|
||||||
Message::State,
|
Message::State,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Event::LoadConfig(config) => {
|
Event::LoadConfig(config) => {
|
||||||
self.config = Some(config);
|
self.config = Some(config);
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Event::ListFiles(files) => {
|
Event::ListFiles(files) => {
|
||||||
self.audio_miscellaneous.files = files;
|
self.audio_miscellaneous.files = files;
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Event::IcedEvent(iced_event) => match iced_event {
|
Event::IcedEvent(iced_event) => match iced_event {
|
||||||
iced::Event::Keyboard(_) => Command::perform(
|
iced::Event::Keyboard(_) => Task::perform(
|
||||||
async move {
|
async move {
|
||||||
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
||||||
Event::ListFiles(files)
|
Event::ListFiles(files)
|
||||||
},
|
},
|
||||||
Message::Event,
|
Message::Event,
|
||||||
),
|
),
|
||||||
iced::Event::Mouse(_) => Command::perform(
|
iced::Event::Mouse(_) => Task::perform(
|
||||||
async move {
|
async move {
|
||||||
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
||||||
Event::ListFiles(files)
|
Event::ListFiles(files)
|
||||||
},
|
},
|
||||||
Message::Event,
|
Message::Event,
|
||||||
),
|
),
|
||||||
iced::Event::Window(id, window_event) => {
|
iced::Event::Touch(_) => Task::perform(
|
||||||
if let window::Event::CloseRequested = window_event {
|
|
||||||
self.exit(id)
|
|
||||||
} else {
|
|
||||||
Command::perform(
|
|
||||||
async move {
|
|
||||||
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
|
||||||
Event::ListFiles(files)
|
|
||||||
},
|
|
||||||
Message::Event,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iced::Event::Touch(_) => Command::perform(
|
|
||||||
async move {
|
async move {
|
||||||
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
||||||
Event::ListFiles(files)
|
Event::ListFiles(files)
|
||||||
},
|
},
|
||||||
Message::Event,
|
Message::Event,
|
||||||
),
|
),
|
||||||
iced::Event::PlatformSpecific(_) => Command::perform(
|
iced::Event::Window(windows_event) => Task::perform(
|
||||||
async move {
|
{
|
||||||
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
if let window::Event::CloseRequested = windows_event {
|
||||||
Event::ListFiles(files)
|
self.exit();
|
||||||
|
}
|
||||||
|
async move {
|
||||||
|
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
||||||
|
Event::ListFiles(files)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Message::Event,
|
Message::Event,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Event::CloseWindow(id) => window::close(id),
|
Event::CloseWindow(_) => self.exit(),
|
||||||
},
|
},
|
||||||
Message::State(state) => match state {
|
Message::State(state) => match state {
|
||||||
State::None => Command::none(),
|
State::None => Task::none(),
|
||||||
State::Connected => {
|
State::Connected => {
|
||||||
self.gui_status.are_we_connect = Condition::Active;
|
self.gui_status.are_we_connect = Condition::Active;
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
State::Disconnected => {
|
State::Disconnected => {
|
||||||
self.gui_status.are_we_connect = Condition::Passive;
|
self.gui_status.are_we_connect = Condition::Passive;
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
State::Recording => {
|
State::Recording => {
|
||||||
self.gui_status.are_we_record = Condition::Active;
|
self.gui_status.are_we_record = Condition::Active;
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
State::StopRecording => {
|
State::StopRecording => {
|
||||||
self.gui_status.are_we_record = Condition::Passive;
|
self.gui_status.are_we_record = Condition::Passive;
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
State::PlayingAudio => {
|
State::PlayingAudio => {
|
||||||
self.audio_miscellaneous.playing_file_name =
|
self.audio_miscellaneous.playing_file_name =
|
||||||
self.audio_miscellaneous.selected_file_name.clone();
|
self.audio_miscellaneous.selected_file_name.clone();
|
||||||
self.gui_status.are_we_play_audio = Condition::Active;
|
self.gui_status.are_we_play_audio = Condition::Active;
|
||||||
self.gui_status.are_we_paused_audio = Condition::Passive;
|
self.gui_status.are_we_paused_audio = Condition::Passive;
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
State::StopAudio => {
|
State::StopAudio => {
|
||||||
self.audio_miscellaneous.playing_file_name = String::new();
|
self.audio_miscellaneous.playing_file_name = String::new();
|
||||||
self.gui_status.are_we_play_audio = Condition::Passive;
|
self.gui_status.are_we_play_audio = Condition::Passive;
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
State::PausedAudio => {
|
State::PausedAudio => {
|
||||||
self.gui_status.are_we_paused_audio = Condition::Active;
|
self.gui_status.are_we_paused_audio = Condition::Active;
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
State::ContinuedAudio => {
|
State::ContinuedAudio => {
|
||||||
self.gui_status.are_we_paused_audio = Condition::Passive;
|
self.gui_status.are_we_paused_audio = Condition::Passive;
|
||||||
Command::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
State::MicrophoneVolumeChanged => Command::none(),
|
State::MicrophoneVolumeChanged => Task::none(),
|
||||||
State::AudioVolumeChanged => Command::none(),
|
State::AudioVolumeChanged => Task::none(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,14 +743,14 @@ impl Streamer {
|
||||||
|
|
||||||
let microphone_volume_slider = slider(
|
let microphone_volume_slider = slider(
|
||||||
0.0..=1.0,
|
0.0..=1.0,
|
||||||
*self.gui_status.microphone_volume.value.blocking_lock(),
|
*self.gui_status.microphone_volume.value.lock().unwrap(),
|
||||||
|value| Message::Event(Event::ChangeMicrophoneVolume(value)),
|
|value| Message::Event(Event::ChangeMicrophoneVolume(value)),
|
||||||
)
|
)
|
||||||
.step(0.01);
|
.step(0.01);
|
||||||
|
|
||||||
let audio_volume_slider = slider(
|
let audio_volume_slider = slider(
|
||||||
0.0..=1.0,
|
0.0..=1.0,
|
||||||
*self.gui_status.audio_volume.value.blocking_lock(),
|
*self.gui_status.audio_volume.value.lock().unwrap(),
|
||||||
|value| Message::Event(Event::ChangeAudioVolume(value)),
|
|value| Message::Event(Event::ChangeAudioVolume(value)),
|
||||||
)
|
)
|
||||||
.step(0.01);
|
.step(0.01);
|
||||||
|
@ -717,7 +760,7 @@ impl Streamer {
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let longest_name_for_scrollable = match self.audio_miscellaneous.files.as_ref() {
|
let longest_audio_name = match self.audio_miscellaneous.files.as_ref() {
|
||||||
Some(audio_files) => {
|
Some(audio_files) => {
|
||||||
let mut longest = 0;
|
let mut longest = 0;
|
||||||
for audio_file in audio_files {
|
for audio_file in audio_files {
|
||||||
|
@ -733,7 +776,7 @@ impl Streamer {
|
||||||
let mut audio_scrollable_content = column![]
|
let mut audio_scrollable_content = column![]
|
||||||
.spacing(1)
|
.spacing(1)
|
||||||
.height(audio_file_size_for_scrollable)
|
.height(audio_file_size_for_scrollable)
|
||||||
.width(longest_name_for_scrollable);
|
.width(longest_audio_name);
|
||||||
let audio_selected = text_centered(format!(
|
let audio_selected = text_centered(format!(
|
||||||
"Selected: {}",
|
"Selected: {}",
|
||||||
self.audio_miscellaneous.selected_file_name.clone()
|
self.audio_miscellaneous.selected_file_name.clone()
|
||||||
|
@ -750,10 +793,10 @@ impl Streamer {
|
||||||
audio_scrollable_content.push(button.height(AUDIO_SCROLLABLE_BUTTON_SIZE));
|
audio_scrollable_content.push(button.height(AUDIO_SCROLLABLE_BUTTON_SIZE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let audios_scrollable = scrollable(audio_scrollable_content)
|
let audios_scrollable = scrollable(audio_scrollable_content).height(200).width(350);
|
||||||
.height(200)
|
let audio_info_content = column![audio_selected, audio_playing,]
|
||||||
.width(longest_name_for_scrollable);
|
.height(100)
|
||||||
let audio_info_content = column![audio_selected, audio_playing,].height(60);
|
.width(longest_audio_name);
|
||||||
let header_content = row![header].width(350).height(50);
|
let header_content = row![header].width(350).height(50);
|
||||||
let text_content = row![
|
let text_content = row![
|
||||||
connection_text,
|
connection_text,
|
||||||
|
@ -817,17 +860,17 @@ impl Streamer {
|
||||||
.map(Event::IcedEvent)
|
.map(Event::IcedEvent)
|
||||||
.map(Message::Event)
|
.map(Message::Event)
|
||||||
}
|
}
|
||||||
pub fn load_config() -> Command<Message> {
|
pub fn load_config() -> Task<Message> {
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
let config = get_config().await;
|
let config = get_config();
|
||||||
Event::LoadConfig(config)
|
Event::LoadConfig(config)
|
||||||
},
|
},
|
||||||
Message::Event,
|
Message::Event,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub fn list_files() -> Command<Message> {
|
pub fn list_files() -> Task<Message> {
|
||||||
Command::perform(
|
Task::perform(
|
||||||
async move {
|
async move {
|
||||||
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
let files = gui_utils::list_files(Path::new(AUDIOS_PATH)).await;
|
||||||
Event::ListFiles(files)
|
Event::ListFiles(files)
|
||||||
|
@ -843,27 +886,22 @@ impl Streamer {
|
||||||
playing_to_base_receiver: Receiver<Player>,
|
playing_to_base_receiver: Receiver<Player>,
|
||||||
base_to_playing_sender: Sender<Player>,
|
base_to_playing_sender: Sender<Player>,
|
||||||
features_in_need: Features,
|
features_in_need: Features,
|
||||||
window_id: window::Id,
|
) -> Task<Message> {
|
||||||
) -> Command<Message> {
|
tokio::spawn(async move {
|
||||||
Command::perform(
|
if features_in_need.stream {
|
||||||
async move {
|
gui_utils::disconnect(streaming_to_base_receiver, base_to_streaming_sender).await;
|
||||||
if features_in_need.stream {
|
}
|
||||||
gui_utils::disconnect(streaming_to_base_receiver, base_to_streaming_sender)
|
if features_in_need.record {
|
||||||
.await;
|
gui_utils::stop_recording(recording_to_base_receiver, base_to_recording_sender)
|
||||||
}
|
.await;
|
||||||
if features_in_need.record {
|
}
|
||||||
gui_utils::stop_recording(recording_to_base_receiver, base_to_recording_sender)
|
if features_in_need.play_audio {
|
||||||
.await;
|
gui_utils::stop_playing(playing_to_base_receiver, base_to_playing_sender).await;
|
||||||
}
|
}
|
||||||
if features_in_need.play_audio {
|
});
|
||||||
gui_utils::stop_playing(playing_to_base_receiver, base_to_playing_sender).await;
|
exit(1);
|
||||||
}
|
|
||||||
Event::CloseWindow(window_id)
|
|
||||||
},
|
|
||||||
Message::Event,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
fn exit(&self, window_id: window::Id) -> Command<Message> {
|
fn exit(&self) -> Task<Message> {
|
||||||
let mut features_in_need = Features {
|
let mut features_in_need = Features {
|
||||||
stream: false,
|
stream: false,
|
||||||
record: false,
|
record: false,
|
||||||
|
@ -905,7 +943,6 @@ impl Streamer {
|
||||||
playing_to_base_receiver,
|
playing_to_base_receiver,
|
||||||
base_to_playing_sender,
|
base_to_playing_sender,
|
||||||
features_in_need,
|
features_in_need,
|
||||||
window_id,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use iced::{
|
use iced::{
|
||||||
alignment,
|
|
||||||
widget::{button, text, Button, Text},
|
widget::{button, text, Button, Text},
|
||||||
Length,
|
Length,
|
||||||
};
|
};
|
||||||
|
@ -7,18 +6,14 @@ use iced::{
|
||||||
use crate::gui::Message;
|
use crate::gui::Message;
|
||||||
|
|
||||||
pub fn button_with_centered_text<T: Into<String>>(txt: T) -> Button<'static, Message> {
|
pub fn button_with_centered_text<T: Into<String>>(txt: T) -> Button<'static, Message> {
|
||||||
button(
|
button(text(txt.into()).width(Length::Fill).center())
|
||||||
text(txt.into())
|
.height(Length::Fill)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.horizontal_alignment(alignment::Horizontal::Center),
|
|
||||||
)
|
|
||||||
.height(Length::Fill)
|
|
||||||
.width(Length::Fill)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_centered<T: Into<String>>(txt: T) -> Text<'static> {
|
pub fn text_centered<T: Into<String>>(txt: T) -> Text<'static> {
|
||||||
text(txt.into())
|
text(txt.into())
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.horizontal_alignment(alignment::Horizontal::Center)
|
.center()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use std::{fs::File, path::Path, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
fs::File,
|
||||||
use tokio::sync::{
|
path::Path,
|
||||||
broadcast::{Receiver, Sender},
|
sync::{Arc, Mutex},
|
||||||
Mutex,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use tokio::sync::broadcast::{Receiver, Sender};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
gui::{Player, State},
|
gui::{Player, State},
|
||||||
playing, recording, streaming, Config,
|
playing, recording, streaming, Config,
|
||||||
|
@ -348,7 +350,7 @@ pub async fn change_microphone_volume(
|
||||||
desired_value: f32,
|
desired_value: f32,
|
||||||
microphone_stream_volume: Arc<Mutex<f32>>,
|
microphone_stream_volume: Arc<Mutex<f32>>,
|
||||||
) -> State {
|
) -> State {
|
||||||
*microphone_stream_volume.lock().await = desired_value;
|
*microphone_stream_volume.lock().unwrap() = desired_value;
|
||||||
State::MicrophoneVolumeChanged
|
State::MicrophoneVolumeChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +358,7 @@ pub async fn change_audio_volume(
|
||||||
desired_value: f32,
|
desired_value: f32,
|
||||||
audio_stream_volume: Arc<Mutex<f32>>,
|
audio_stream_volume: Arc<Mutex<f32>>,
|
||||||
) -> State {
|
) -> State {
|
||||||
*audio_stream_volume.lock().await = desired_value;
|
*audio_stream_volume.lock().unwrap() = desired_value;
|
||||||
State::AudioVolumeChanged
|
State::AudioVolumeChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,34 @@ use streamer::gui::Streamer;
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
tokio::task::block_in_place(|| {
|
|
||||||
iced::program("Streamer GUI", Streamer::update, Streamer::view)
|
iced::application("Streamer GUI", Streamer::update, Streamer::view)
|
||||||
.centered()
|
.centered()
|
||||||
.window_size((350.0, 650.0))
|
.window_size((350.0, 650.0))
|
||||||
.load(Streamer::load_config)
|
.antialiasing(true)
|
||||||
.load(Streamer::list_files)
|
.subscription(Streamer::subscription)
|
||||||
.antialiasing(true)
|
.exit_on_close_request(false)
|
||||||
.subscription(Streamer::subscription)
|
.run_with(|| Streamer::new_with_load())
|
||||||
.exit_on_close_request(false)
|
.unwrap()
|
||||||
.run()
|
|
||||||
.unwrap()
|
// tokio::task::spawn_blocking(|| {
|
||||||
});
|
// iced::application("Streamer GUI", Streamer::update, Streamer::view)
|
||||||
|
// .centered()
|
||||||
|
// .window_size((350.0, 650.0))
|
||||||
|
// .antialiasing(true)
|
||||||
|
// .subscription(Streamer::subscription)
|
||||||
|
// .exit_on_close_request(false)
|
||||||
|
// .run_with(|| Streamer::new_with_load())
|
||||||
|
// .unwrap()
|
||||||
|
// });
|
||||||
|
// tokio::task::block_in_place(|| {
|
||||||
|
// iced::application("Streamer GUI", Streamer::update, Streamer::view)
|
||||||
|
// .centered()
|
||||||
|
// .window_size((350.0, 650.0))
|
||||||
|
// .antialiasing(true)
|
||||||
|
// .subscription(Streamer::subscription)
|
||||||
|
// .exit_on_close_request(false)
|
||||||
|
// .run_with(|| Streamer::new_with_load())
|
||||||
|
// .unwrap()
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use std::{fs::File, sync::Arc};
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
use rubato::{
|
use rubato::{
|
||||||
|
@ -6,17 +10,14 @@ use rubato::{
|
||||||
};
|
};
|
||||||
use symphonia::core::{
|
use symphonia::core::{
|
||||||
audio::{AudioBufferRef, Signal},
|
audio::{AudioBufferRef, Signal},
|
||||||
codecs::{DecoderOptions, CODEC_TYPE_NULL},
|
codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL},
|
||||||
formats::FormatOptions,
|
formats::{FormatOptions, FormatReader},
|
||||||
io::MediaSourceStream,
|
io::MediaSourceStream,
|
||||||
meta::MetadataOptions,
|
meta::MetadataOptions,
|
||||||
probe::Hint,
|
probe::Hint,
|
||||||
};
|
};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{
|
sync::broadcast::{Receiver, Sender},
|
||||||
broadcast::{Receiver, Sender},
|
|
||||||
Mutex,
|
|
||||||
},
|
|
||||||
task,
|
task,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,30 +38,20 @@ pub async fn play(
|
||||||
|
|
||||||
let output_device_sample_rate = output_device_config.sample_rate.0;
|
let output_device_sample_rate = output_device_config.sample_rate.0;
|
||||||
|
|
||||||
let (mut audio_resampled_left, mut audio_resampled_right) =
|
|
||||||
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();
|
let mut decoded_to_playing_receiver = decoded_to_playing_sender.subscribe();
|
||||||
for _ in 0..audio_resampled_left.clone().len() {
|
let audio_process_task = tokio::spawn(process_audio(
|
||||||
decoded_to_playing_sender
|
output_device_sample_rate,
|
||||||
.send(audio_resampled_left.pop().unwrap() as f32)
|
file,
|
||||||
.unwrap();
|
decoded_to_playing_sender,
|
||||||
decoded_to_playing_sender
|
));
|
||||||
.send(audio_resampled_right.pop().unwrap() as f32)
|
while decoded_to_playing_receiver.is_empty() {
|
||||||
.unwrap();
|
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_data_fn = move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
let output_data_fn = move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
||||||
for sample in data {
|
for sample in data {
|
||||||
if decoded_to_playing_receiver.len() > 0 {
|
if decoded_to_playing_receiver.len() > 0 {
|
||||||
let single = match decoded_to_playing_receiver.blocking_recv() {
|
let single = match decoded_to_playing_receiver.blocking_recv() {
|
||||||
Ok(single) => single * *audio_volume.blocking_lock(),
|
Ok(single) => single * *audio_volume.lock().unwrap(),
|
||||||
Err(_) => 0.0,
|
Err(_) => 0.0,
|
||||||
};
|
};
|
||||||
if audio_stream_sender.receiver_count() > 0 {
|
if audio_stream_sender.receiver_count() > 0 {
|
||||||
|
@ -101,9 +92,19 @@ pub async fn play(
|
||||||
//todo when pause error, do software level stop
|
//todo when pause error, do software level stop
|
||||||
Err(_) => todo!(),
|
Err(_) => todo!(),
|
||||||
},
|
},
|
||||||
Player::Stop => break,
|
Player::Stop => {
|
||||||
|
if !audio_process_task.is_finished() {
|
||||||
|
audio_process_task.abort();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(_) => break,
|
Err(_) => {
|
||||||
|
if !audio_process_task.is_finished() {
|
||||||
|
audio_process_task.abort();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
drop(output_stream);
|
drop(output_stream);
|
||||||
|
@ -116,9 +117,56 @@ fn err_fn(err: cpal::StreamError) {
|
||||||
async fn let_the_base_know(playing_to_base_sender: Sender<Player>, action: Player) {
|
async fn let_the_base_know(playing_to_base_sender: Sender<Player>, action: Player) {
|
||||||
let _ = playing_to_base_sender.send(action);
|
let _ = playing_to_base_sender.send(action);
|
||||||
}
|
}
|
||||||
fn decode_audio(output_device_sample_rate: u32, file: File) -> Option<(Vec<f64>, Vec<f64>)> {
|
|
||||||
|
fn decode_audio(
|
||||||
|
format: &mut Box<dyn FormatReader>,
|
||||||
|
track_id: u32,
|
||||||
|
decoder: &mut Box<dyn Decoder>,
|
||||||
|
) -> Option<(Vec<f64>, Vec<f64>)> {
|
||||||
let mut audio_decoded_left = vec![];
|
let mut audio_decoded_left = vec![];
|
||||||
let mut audio_decoded_right = vec![];
|
let mut audio_decoded_right = vec![];
|
||||||
|
let packet = match format.next_packet() {
|
||||||
|
Ok(packet) => packet,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
while !format.metadata().is_latest() {
|
||||||
|
format.metadata().pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.track_id() != track_id {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(decoded) = decoder.decode(&packet) {
|
||||||
|
if let AudioBufferRef::F32(buf) = decoded {
|
||||||
|
for (left, right) in buf.chan(0).iter().zip(buf.chan(1).iter()) {
|
||||||
|
audio_decoded_left.push(*left as f64);
|
||||||
|
audio_decoded_right.push(*right as f64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some((audio_decoded_left, audio_decoded_right))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resample_audio(
|
||||||
|
audio_decoded_left: Vec<f64>,
|
||||||
|
audio_decoded_right: Vec<f64>,
|
||||||
|
resampler: &mut SincFixedIn<f64>,
|
||||||
|
) -> (Vec<f64>, Vec<f64>) {
|
||||||
|
let audio_decoded_channels_combined = vec![audio_decoded_left, audio_decoded_right];
|
||||||
|
let audio_resampled = resampler
|
||||||
|
.process(&audio_decoded_channels_combined, None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(audio_resampled[0].clone(), audio_resampled[1].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn process_audio(
|
||||||
|
output_device_sample_rate: u32,
|
||||||
|
file: File,
|
||||||
|
decoded_to_playing_sender: tokio::sync::broadcast::Sender<f32>,
|
||||||
|
) {
|
||||||
let media_source_stream = MediaSourceStream::new(Box::new(file), Default::default());
|
let media_source_stream = MediaSourceStream::new(Box::new(file), Default::default());
|
||||||
|
|
||||||
let hint = Hint::new();
|
let hint = Hint::new();
|
||||||
|
@ -135,7 +183,7 @@ fn decode_audio(output_device_sample_rate: u32, file: File) -> Option<(Vec<f64>,
|
||||||
|
|
||||||
match probed {
|
match probed {
|
||||||
Ok(probed_safe) => probed = Ok(probed_safe),
|
Ok(probed_safe) => probed = Ok(probed_safe),
|
||||||
Err(_) => return None,
|
Err(_) => return,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut format = probed.unwrap().format;
|
let mut format = probed.unwrap().format;
|
||||||
|
@ -147,7 +195,6 @@ fn decode_audio(output_device_sample_rate: u32, file: File) -> Option<(Vec<f64>,
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let audio_sample_rate = track.codec_params.sample_rate.unwrap();
|
let audio_sample_rate = track.codec_params.sample_rate.unwrap();
|
||||||
|
|
||||||
DecoderOptions::default();
|
DecoderOptions::default();
|
||||||
let decoder_options = DecoderOptions::default();
|
let decoder_options = DecoderOptions::default();
|
||||||
let mut decoder = symphonia::default::get_codecs()
|
let mut decoder = symphonia::default::get_codecs()
|
||||||
|
@ -156,74 +203,50 @@ fn decode_audio(output_device_sample_rate: u32, file: File) -> Option<(Vec<f64>,
|
||||||
|
|
||||||
let track_id = track.id;
|
let track_id = track.id;
|
||||||
|
|
||||||
loop {
|
|
||||||
let packet = match format.next_packet() {
|
|
||||||
Ok(packet) => packet,
|
|
||||||
Err(_) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
while !format.metadata().is_latest() {
|
|
||||||
format.metadata().pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if packet.track_id() != track_id {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match decoder.decode(&packet) {
|
|
||||||
Ok(decoded) => match decoded {
|
|
||||||
AudioBufferRef::F32(buf) => {
|
|
||||||
for (left, right) in buf.chan(0).iter().zip(buf.chan(1).iter()) {
|
|
||||||
audio_decoded_left.push(*left as f64);
|
|
||||||
audio_decoded_right.push(*right as f64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
Err(_) => {
|
|
||||||
//eprintln!("Error: Sample Decode | {}", err_val);
|
|
||||||
println!("End ?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = SincInterpolationParameters {
|
let params = SincInterpolationParameters {
|
||||||
sinc_len: 256,
|
sinc_len: 256,
|
||||||
f_cutoff: 0.95,
|
f_cutoff: 0.95,
|
||||||
interpolation: SincInterpolationType::Linear,
|
interpolation: SincInterpolationType::Linear,
|
||||||
oversampling_factor: 256,
|
oversampling_factor: 128,
|
||||||
window: WindowFunction::BlackmanHarris2,
|
window: WindowFunction::BlackmanHarris2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let chunk_size = match decode_audio(&mut format, track_id, &mut decoder) {
|
||||||
|
Some((audio_decoded_left_channel, _)) => audio_decoded_left_channel.len(),
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
let mut resampler = SincFixedIn::<f64>::new(
|
let mut resampler = SincFixedIn::<f64>::new(
|
||||||
output_device_sample_rate as f64 / audio_sample_rate as f64,
|
output_device_sample_rate as f64 / audio_sample_rate as f64,
|
||||||
2.0,
|
2.0,
|
||||||
params,
|
params,
|
||||||
audio_decoded_left.len(),
|
chunk_size,
|
||||||
2,
|
2,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let audio_decoded_channes_combined =
|
loop {
|
||||||
vec![audio_decoded_left.clone(), audio_decoded_right.clone()];
|
let (mut audio_decoded_left, mut audio_decoded_right) = (vec![], vec![]);
|
||||||
let audio_resampled = resampler
|
|
||||||
.process(&audio_decoded_channes_combined, None)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut audio_resampled_left = vec![];
|
match decode_audio(&mut format, track_id, &mut decoder) {
|
||||||
let mut audio_resampled_right = vec![];
|
Some((audio_decoded_left_channel, audio_decoded_right_channel)) => {
|
||||||
|
for (single_left, single_right) in audio_decoded_left_channel
|
||||||
|
.iter()
|
||||||
|
.zip(&audio_decoded_right_channel)
|
||||||
|
{
|
||||||
|
audio_decoded_left.push(*single_left);
|
||||||
|
audio_decoded_right.push(*single_right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
|
||||||
for sample in &audio_resampled[0] {
|
let (audio_resampled_left, audio_resampled_right) =
|
||||||
audio_resampled_left.push(*sample);
|
resample_audio(audio_decoded_left, audio_decoded_right, &mut resampler);
|
||||||
|
|
||||||
|
for (single_left, single_right) in audio_resampled_left.iter().zip(&audio_resampled_right) {
|
||||||
|
let _ = decoded_to_playing_sender.send(*single_left as f32);
|
||||||
|
let _ = decoded_to_playing_sender.send(*single_right as f32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for sample in &audio_resampled[1] {
|
|
||||||
audio_resampled_right.push(*sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
audio_resampled_left.reverse();
|
|
||||||
audio_resampled_right.reverse();
|
|
||||||
|
|
||||||
Some((audio_resampled_left, audio_resampled_right))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
use std::{cmp::min, io::Write, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
cmp::min,
|
||||||
|
io::Write,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use brotli::CompressorWriter;
|
use brotli::CompressorWriter;
|
||||||
use futures_util::SinkExt;
|
use futures_util::SinkExt;
|
||||||
use ringbuf::HeapRb;
|
use ringbuf::HeapRb;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{
|
sync::broadcast::{channel, Receiver, Sender},
|
||||||
broadcast::{channel, Receiver, Sender},
|
|
||||||
Mutex,
|
|
||||||
},
|
|
||||||
task::JoinHandle,
|
task::JoinHandle,
|
||||||
};
|
};
|
||||||
use tokio_tungstenite::tungstenite::Message;
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
|
@ -157,8 +159,8 @@ async fn mixer(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut flow = vec![];
|
let mut flow = vec![];
|
||||||
let microphone_volume = *microphone_stream_volume.lock().await;
|
let microphone_volume = *microphone_stream_volume.lock().unwrap();
|
||||||
let audio_volume = *audio_stream_volume.lock().await;
|
let audio_volume = *audio_stream_volume.lock().unwrap();
|
||||||
|
|
||||||
for element in microphone_stream {
|
for element in microphone_stream {
|
||||||
if element < 0.01 || element > -0.01 {
|
if element < 0.01 || element > -0.01 {
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
use tokio::{fs::File, io::AsyncReadExt};
|
use std::{fs::File, io::Read};
|
||||||
|
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
|
|
||||||
pub async fn get_config() -> Config {
|
pub fn get_config() -> Config {
|
||||||
let mut config_file = File::open("configs/streamer_configs.txt").await.unwrap();
|
let mut config_file = File::open("configs/streamer_configs.txt").unwrap();
|
||||||
let mut configs_unparsed = String::new();
|
let mut configs_unparsed = String::new();
|
||||||
config_file
|
config_file.read_to_string(&mut configs_unparsed).unwrap();
|
||||||
.read_to_string(&mut configs_unparsed)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let configs_parsed: Vec<&str> = configs_unparsed.split_terminator("\n").collect();
|
let configs_parsed: Vec<&str> = configs_unparsed.split_terminator("\n").collect();
|
||||||
let mut configs_cleaned: Vec<&str> = vec![];
|
let mut configs_cleaned: Vec<&str> = vec![];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue