use iced::widget::{button, column, container, pick_list, row, text, text_input, checkbox, Space}; use iced::{Alignment, Background, Color, Element, Length, Theme}; use super::{Editor, Message, ModalState}; use crate::export::{self, ExportConfig, ExportFormat}; use crate::gui::theme as ui_theme; impl Editor { pub(crate) fn handle_export(&mut self, message: Message) { match message { Message::ShowExportDialog => { self.modal_state = Some(ModalState::ExportDialog(ExportConfig { sample_rate: self.project_config.sample_rate, ..ExportConfig::default() })); } Message::ExportFormatSelected(fmt) => { if let Some(ModalState::ExportDialog(ref mut cfg)) = self.modal_state { cfg.format = fmt; } } Message::ExportBitDepthSelected(bd) => { if let Some(ModalState::ExportDialog(ref mut cfg)) = self.modal_state { cfg.bit_depth = bd; } } Message::ExportNormalizeToggled => { if let Some(ModalState::ExportDialog(ref mut cfg)) = self.modal_state { cfg.normalize = !cfg.normalize; } } Message::ExportFilenameChanged(name) => { if let Some(ModalState::ExportDialog(ref mut cfg)) = self.modal_state { cfg.filename = name; } } Message::ExportConfirm => { if let Some(ModalState::ExportDialog(ref cfg)) = self.modal_state { self.perform_export(cfg.clone()); } self.modal_state = None; } _ => {} } } pub(crate) fn perform_export(&self, config: ExportConfig) { let bounced = export::bounce_offline( &self.tracks, &self.project_path, self.tempo, self.project_config.sample_rate, self.time_signature_numerator as u32, ); let (mut mix_l, mut mix_r) = match bounced { Some(data) => data, None => { debug_log!("nothing to export"); return; } }; if config.normalize { export::normalize(&mut mix_l, &mut mix_r); } let export_dir = self.project_path.join("exports"); let _ = std::fs::create_dir_all(&export_dir); let filename = format!("{}.{}", config.filename, config.format.extension()); let path = export_dir.join(&filename); let result = match config.format { ExportFormat::Wav => export::export_wav( &path, &mix_l, &mix_r, config.sample_rate, config.bit_depth, ), ExportFormat::Flac => export::export_flac( &path, &mix_l, &mix_r, config.sample_rate, config.bit_depth, ), ExportFormat::Xtc => export::export_xtc( &path, &mix_l, &mix_r, config.sample_rate, config.bit_depth, self.analysis_fft_size as u32, ), }; match result { Ok(()) => debug_log!("[export] wrote {}", path.display()), Err(_e) => debug_log!("[export] failed: {}", _e), } } pub(crate) fn export_dialog_view(&self, config: &ExportConfig) -> Element<'_, Message> { let format_picker = pick_list( ExportFormat::ALL.as_slice(), Some(config.format), Message::ExportFormatSelected, ); let bit_depths: Vec = vec![16, 24, 32]; let bit_depth_picker = pick_list( bit_depths, Some(config.bit_depth), Message::ExportBitDepthSelected, ); let normalize_check = checkbox("Normalize", config.normalize) .on_toggle(|_| Message::ExportNormalizeToggled); let filename_input = text_input("filename", &config.filename) .on_input(Message::ExportFilenameChanged) .width(200); let export_btn = button(text("Export").size(ui_theme::TS_MD)) .on_press(Message::ExportConfirm); let cancel_btn = button(text("Cancel").size(ui_theme::TS_MD)) .on_press(Message::CloseModal); container( column![ text("Export / Bounce").size(ui_theme::TS_XL), Space::new(0, ui_theme::SP_LG), row![text("Format:").size(ui_theme::TS_MD).width(80), format_picker].spacing(ui_theme::SP_MD).align_y(Alignment::Center), row![text("Bit Depth:").size(ui_theme::TS_MD).width(80), bit_depth_picker].spacing(ui_theme::SP_MD).align_y(Alignment::Center), row![text("Filename:").size(ui_theme::TS_MD).width(80), filename_input].spacing(ui_theme::SP_MD).align_y(Alignment::Center), normalize_check, Space::new(0, ui_theme::SP_LG), row![cancel_btn, Space::new(Length::Fill, 0), export_btn].spacing(ui_theme::SP_MD), ] .spacing(ui_theme::SP_MD) .padding(ui_theme::SP_XXL) .width(400), ) .style(|_theme: &Theme| container::Style { background: Some(Background::Color(Color::from_rgb8(0x38, 0x31, 0x2A))), border: iced::Border { color: Color::from_rgb8(0x58, 0x4E, 0x44), width: 1.0, radius: 10.0.into(), }, ..container::Style::default() }) .into() } }