From 0af881ca5fa29a8dfa55a2034d3e7bfd9b41ac40 Mon Sep 17 00:00:00 2001 From: Paul Sajna Date: Tue, 30 Aug 2022 19:55:00 -0700 Subject: [PATCH] more ui stuff --- crates/app/storyteller-vc/Cargo.lock | 76 ++++++++++- crates/app/storyteller-vc/Cargo.toml | 3 +- crates/app/storyteller-vc/src/main.rs | 179 ++++++++++++++++++++------ 3 files changed, 219 insertions(+), 39 deletions(-) diff --git a/crates/app/storyteller-vc/Cargo.lock b/crates/app/storyteller-vc/Cargo.lock index ced1aea4c9..1e1cec34d4 100644 --- a/crates/app/storyteller-vc/Cargo.lock +++ b/crates/app/storyteller-vc/Cargo.lock @@ -566,6 +566,27 @@ dependencies = [ "syn", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -1254,6 +1275,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "native-dialog" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab637f328b31bd0855c43bd38a4a4455e74324d9e74e0aac6a803422f43abc6" +dependencies = [ + "block", + "cocoa", + "dirs-next", + "objc", + "objc-foundation", + "objc_id", + "once_cell", + "raw-window-handle 0.4.3", + "thiserror", + "wfd", + "which", + "winapi", +] + [[package]] name = "ndk" version = "0.5.0" @@ -1712,6 +1753,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + [[package]] name = "regex" version = "1.6.0" @@ -1875,12 +1927,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" [[package]] -name = "storyteller-vc" +name = "storyteller-recast" version = "0.1.0" dependencies = [ "console_error_panic_hook", "cpal", "iced", + "native-dialog", "ringbuf", "wasm-bindgen", "web-sys", @@ -2175,6 +2228,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wfd" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e713040b67aae5bf1a0ae3e1ebba8cc29ab2b90da9aa1bff6e09031a8a41d7a8" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "wgpu" version = "0.12.0" @@ -2278,6 +2341,17 @@ dependencies = [ "wgpu", ] +[[package]] +name = "which" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +dependencies = [ + "either", + "lazy_static", + "libc", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/crates/app/storyteller-vc/Cargo.toml b/crates/app/storyteller-vc/Cargo.toml index bb723a4107..737ee23942 100644 --- a/crates/app/storyteller-vc/Cargo.toml +++ b/crates/app/storyteller-vc/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "storyteller-vc" +name = "storyteller-recast" version = "0.1.0" edition = "2021" @@ -8,6 +8,7 @@ edition = "2021" [dependencies] iced = "0.4.2" ringbuf = "0.2.8" +native-dialog = "0.6.3" [target.'cfg(target_arch = "wasm32")'.dependencies] cpal = { version = "0.13.5", features=["wasm-bindgen"] } diff --git a/crates/app/storyteller-vc/src/main.rs b/crates/app/storyteller-vc/src/main.rs index f5a54bcabe..9ba062bc3a 100644 --- a/crates/app/storyteller-vc/src/main.rs +++ b/crates/app/storyteller-vc/src/main.rs @@ -3,35 +3,52 @@ use iced::widget::{Slider, slider, Button, button, Text, pick_list, PickList}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use cpal::{SampleRate, BufferSize, Device}; use ringbuf::RingBuffer; +use native_dialog::FileDialog; pub fn main() -> iced::Result { App::run(Settings::default()) } struct App { + model_browse_state: button::State, slider_1_state: slider::State, slider_1_value: f32, slider_2_state: slider::State, slider_2_value: f32, record_state: button::State, recording: bool, - record_sender: Option>, + record_sender: Option>, + play_target_state: button::State, + play_source_state: button::State, + target_playing: bool, + source_playing: bool, + realtime_record_state: button::State, + realtime_recording: bool, + realtime_record_sender: Option>, input_device_list_state: pick_list::State, input_device_list_options: Vec, input_device_list_selected: Option, + input_browse_state: button::State, output_device_list_state: pick_list::State, output_device_list_options: Vec, output_device_list_selected: Option, + output_browse_state: button::State, } #[derive(Clone, Debug)] enum Message { + ModelBrowsePressed, Slider1Changed(f32), Slider2Changed(f32), RecordPressed, + PlayTargetPressed, + PlaySourcePressed, + RealtimeRecordPressed, InputDeviceChanged(String), OutputDeviceChanged(String), + InputBrowsePressed, + OutputBrowsePressed, } impl Application for App { @@ -40,6 +57,7 @@ impl Application for App { type Flags = (); fn new(_flags: ()) -> (App, Command) { + let model_browse_state = button::State::new(); let slider_1_state = slider::State::new(); let slider_1_value = 85.0; let slider_2_state = slider::State::new(); @@ -47,15 +65,27 @@ impl Application for App { let record_state = button::State::new(); let recording = false; let record_sender = None; + let play_target_state = button::State::new(); + let play_source_state = button::State::new(); + let target_playing = false; + let source_playing = false; + let realtime_record_state = button::State::new(); + let realtime_recording = false; + let realtime_record_sender = None; let input_device_list_state = pick_list::State::new(); - let input_device_list_options = cpal::default_host().input_devices().unwrap().map(|d|d.name().unwrap()).collect(); + let mut input_device_list_options: Vec = cpal::default_host().input_devices().unwrap().map(|d|d.name().unwrap()).collect(); + input_device_list_options.insert(0, String::from("From file")); + let input_browse_state = button::State::new(); let input_device_list_selected = None; let output_device_list_state = pick_list::State::new(); - let output_device_list_options = cpal::default_host().output_devices().unwrap().map(|d| d.name().unwrap()).collect(); + let mut output_device_list_options: Vec = cpal::default_host().output_devices().unwrap().map(|d| d.name().unwrap()).collect(); + output_device_list_options.insert(0, String::from("To file")); let output_device_list_selected = None; + let output_browse_state = button::State::new(); ( App { + model_browse_state, slider_1_state, slider_1_value, slider_2_state, @@ -63,77 +93,106 @@ impl Application for App { record_state, recording, record_sender, + play_target_state, + play_source_state, + target_playing, + source_playing, + realtime_record_state, + realtime_recording, + realtime_record_sender, input_device_list_state, input_device_list_options, input_device_list_selected, + input_browse_state, output_device_list_state, output_device_list_options, output_device_list_selected, - + output_browse_state, }, Command::none() ) } fn title(&self) -> String { - String::from("Storyteller VC") + String::from("Storyteller Recast") } fn update(&mut self, msg: Self::Message) -> Command { match msg { Message::Slider1Changed(new_value) => { self.slider_1_value = new_value; - if self.record_sender.is_some() { - self.record_sender.as_ref().unwrap().send((self.recording.clone(), self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); + if self.realtime_record_sender.is_some() { + self.realtime_record_sender.as_ref().unwrap().send((self.realtime_recording.clone(), self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); } } Message::Slider2Changed(new_value) => { self.slider_2_value = new_value; - if self.record_sender.is_some() { - self.record_sender.as_ref().unwrap().send((self.recording.clone(), self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); + if self.realtime_record_sender.is_some() { + self.realtime_record_sender.as_ref().unwrap().send((self.realtime_recording.clone(), self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); } } - Message::RecordPressed => { - if self.recording { - if self.record_sender.is_some() + // Realtime record button => record and playback at the same time + Message::RealtimeRecordPressed => { + if self.realtime_recording { + if self.realtime_record_sender.is_some() { - self.record_sender.as_ref().unwrap().send((false, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); + self.realtime_record_sender.as_ref().unwrap().send((false, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); } } else { - let (record_sender, record_receiver) = std::sync::mpsc::channel(); - self.record_sender = Some(record_sender); + let (realtime_record_sender, realtime_record_receiver) = std::sync::mpsc::channel(); + self.realtime_record_sender = Some(realtime_record_sender); let input_device_name = self.input_device_list_selected.clone(); - let output_device_name = self.output_device_list_selected.clone(); - std::thread::spawn(move || { start_recording(record_receiver, input_device_name, output_device_name) }); - self.record_sender.as_ref().unwrap().send((true, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); + let output_device_name = self.output_device_list_selected.clone(); std::thread::spawn(move || { start_realtime_recording(realtime_record_receiver, input_device_name, output_device_name) }); + self.realtime_record_sender.as_ref().unwrap().send((true, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); } - self.recording = !self.recording; + self.realtime_recording = !self.realtime_recording; } + Message::ModelBrowsePressed => { + let path = FileDialog::new() + .add_filter("Storyteller Recast Model", &["recast"]) + .show_open_single_file() + .unwrap(); + } + Message::InputBrowsePressed => { + let path = FileDialog::new() + .add_filter("WAV audio file", &["wav"]) + .show_open_single_file() + .unwrap(); + } + Message::OutputBrowsePressed => { + let path = FileDialog::new() + .add_filter("WAV audio file", &["wav"]) + .show_save_single_file() + .unwrap(); + } + Message::RecordPressed => { } + Message::PlayTargetPressed => { } + Message::PlaySourcePressed => { } Message::InputDeviceChanged(new_device) => { self.input_device_list_selected = Some(new_device); // Restart recording if currently recording and the device changes - if self.recording { - self.record_sender.as_ref().unwrap().send((false, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); - let (record_sender, record_receiver) = std::sync::mpsc::channel(); - self.record_sender = Some(record_sender); + if self.realtime_recording { + self.realtime_record_sender.as_ref().unwrap().send((false, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); + let (realtime_record_sender, realtime_record_receiver) = std::sync::mpsc::channel(); + self.realtime_record_sender = Some(realtime_record_sender); let input_device_name = self.input_device_list_selected.clone(); let output_device_name = self.output_device_list_selected.clone(); - std::thread::spawn(move || { start_recording(record_receiver, input_device_name, output_device_name) }); - self.record_sender.as_ref().unwrap().send((true, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); + std::thread::spawn(move || { start_realtime_recording(realtime_record_receiver, input_device_name, output_device_name) }); + self.realtime_record_sender.as_ref().unwrap().send((true, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); } } Message::OutputDeviceChanged(new_device) => { self.output_device_list_selected = Some(new_device); // Restart recording if currently recording and the device changes - if self.recording { - self.record_sender.as_ref().unwrap().send((false, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); - let (record_sender, record_receiver) = std::sync::mpsc::channel(); - self.record_sender = Some(record_sender); + if self.realtime_recording { + self.realtime_record_sender.as_ref().unwrap().send((false, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); + let (realtime_record_sender, realtime_record_receiver) = std::sync::mpsc::channel(); + self.realtime_record_sender = Some(realtime_record_sender); let input_device_name = self.input_device_list_selected.clone(); let output_device_name = self.output_device_list_selected.clone(); - std::thread::spawn(move || { start_recording(record_receiver, input_device_name, output_device_name) }); - self.record_sender.as_ref().unwrap().send((true, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); + std::thread::spawn(move || { start_realtime_recording(realtime_record_receiver, input_device_name, output_device_name) }); + self.realtime_record_sender.as_ref().unwrap().send((true, self.slider_1_value.clone(), self.slider_2_value.clone())).unwrap(); } } @@ -142,7 +201,33 @@ impl Application for App { } fn view(&mut self) -> Element { - Column::new() + let mut file_selection_row_elems: Vec> = vec![]; + if self.input_device_list_selected == Some(String::from("From file")) + { + file_selection_row_elems.push( + Text::new("Input File: ").into() + ); + file_selection_row_elems.push( + Button::new(&mut self.input_browse_state, Text::new("Browse")).on_press(Message::InputBrowsePressed).into() + ); + } + if self.output_device_list_selected == Some(String::from("To file")) + { + file_selection_row_elems.push( + Text::new("Output File: ").into() + ); + file_selection_row_elems.push( + Button::new(&mut self.output_browse_state, Text::new("Browse")).on_press(Message::OutputBrowsePressed).into() + ); + } + let mut file_selection_row = Row::with_children(file_selection_row_elems); + + let elements = Column::new() + .push( + Row::new() + .push(Text::new("Select Voice File: ")) + .push(Button::new(&mut self.model_browse_state, Text::new("Browse")).on_press(Message::ModelBrowsePressed)) + ) .push( Row::new() .push(Text::new("Input Device: ")) @@ -164,6 +249,9 @@ impl Application for App { ) ) ) + .push( + file_selection_row + ) .push( Row::new() .push(Text::new("Input Volume: ")) @@ -176,12 +264,28 @@ impl Application for App { ) ) .push( - Button::new(&mut self.record_state, if self.recording { Text::new("Stop") } else {Text::new("Record")} ).on_press(Message::RecordPressed) - ).into() + Row::new() + .push( + Button::new(&mut self.record_state, if self.recording { Text::new("Stop")} else {Text::new("Record")}).on_press(Message::RecordPressed) + ) + .push( + Button::new(&mut self.play_target_state, Text::new("Play Target")).on_press(Message::PlayTargetPressed) + ) + .push( + Button::new(&mut self.play_source_state, Text::new("Play Source")).on_press(Message::PlaySourcePressed) + ) + .push( + Button::new(&mut self.realtime_record_state, if self.realtime_recording { Text::new("Stop") } else {Text::new("Realtime\n(experimental)")}).on_press(Message::RealtimeRecordPressed) + ) + + ); + + elements.into() } } -fn start_recording(record_receiver: std::sync::mpsc::Receiver<(bool, f32, f32)>, input_device_name: Option, output_device_name: Option) { +// Realtime record => record and playback at the same time +fn start_realtime_recording(realtime_record_receiver: std::sync::mpsc::Receiver<(bool, f32, f32)>, input_device_name: Option, output_device_name: Option) { let host = cpal::default_host(); //FIXME better unique identifier than the name of the device @@ -199,6 +303,7 @@ fn start_recording(record_receiver: std::sync::mpsc::Receiver<(bool, f32, f32)>, let config = cpal::StreamConfig { channels: 1, sample_rate: SampleRate(48000), buffer_size: BufferSize::Default }; let ring = RingBuffer::new(100_0000); let (mut producer, mut consumer) = ring.split(); + //FIXME unsafe static mut input_volume: f32 = 85.0; static mut output_volume: f32 = 85.0; @@ -237,8 +342,8 @@ fn start_recording(record_receiver: std::sync::mpsc::Receiver<(bool, f32, f32)>, input_stream.play().unwrap(); output_stream.play().unwrap(); loop { - let (recording, ivol, ovol) = record_receiver.recv().unwrap(); - if recording { + let (realtime_recording, ivol, ovol) = realtime_record_receiver.recv().unwrap(); + if realtime_recording { unsafe { input_volume = ivol }; unsafe { output_volume = ovol }; std::thread::sleep(std::time::Duration::from_millis(50));