Added errors to ui
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
a07a522cf3
commit
96a89f3091
|
@ -73,6 +73,7 @@ dependencies = [
|
||||||
"hound",
|
"hound",
|
||||||
"image",
|
"image",
|
||||||
"rfd",
|
"rfd",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -8,3 +8,4 @@ hound = "*"
|
||||||
image = "0.24.0"
|
image = "0.24.0"
|
||||||
eframe = "0.16.0"
|
eframe = "0.16.0"
|
||||||
rfd = "0.7.0"
|
rfd = "0.7.0"
|
||||||
|
thiserror = "1.0.30"
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use amdemod::SquaringAMDemodulator;
|
use amdemod::SquaringAMDemodulator;
|
||||||
use aptsyncer::{APTSyncer, SyncedSample};
|
use aptsyncer::{APTSyncer, SyncedSample};
|
||||||
|
use errors::DecoderError;
|
||||||
use firfilter::FIRFilter;
|
use firfilter::FIRFilter;
|
||||||
use resamplers::{Downsampler, Upsampler};
|
use resamplers::{Downsampler, Upsampler};
|
||||||
use utils::float_sample_iterator;
|
use utils::float_sample_iterator;
|
||||||
|
@ -75,12 +76,16 @@ const LOWPASS_COEFFS: [f32; 63] = [
|
||||||
-7.383784e-03,
|
-7.383784e-03,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn decode<T>(input_file: &str, output_file: &str, progress_update: T) -> Result<(), String>
|
pub fn decode<T>(
|
||||||
|
input_file: &str,
|
||||||
|
output_file: &str,
|
||||||
|
progress_update: T,
|
||||||
|
) -> Result<(), DecoderError>
|
||||||
where
|
where
|
||||||
T: Fn(f32, image::RgbaImage) -> (bool, u32),
|
T: Fn(f32, image::RgbaImage) -> (bool, u32),
|
||||||
{
|
{
|
||||||
let mut reader = hound::WavReader::open(input_file)
|
let mut reader =
|
||||||
.map_err(|err| format!("Could not open inputfile: {}", err))?;
|
hound::WavReader::open(input_file).map_err(|err| DecoderError::InputFileError(err))?;
|
||||||
|
|
||||||
if reader.spec().channels != 1 {
|
if reader.spec().channels != 1 {
|
||||||
panic!("Expected a mono file");
|
panic!("Expected a mono file");
|
||||||
|
@ -88,7 +93,7 @@ where
|
||||||
|
|
||||||
let sample_rate = reader.spec().sample_rate;
|
let sample_rate = reader.spec().sample_rate;
|
||||||
if sample_rate != 48000 {
|
if sample_rate != 48000 {
|
||||||
return Err("Expected a 48kHz sample rate".to_owned());
|
return Err(DecoderError::UnexpectedSamplingRate(sample_rate));
|
||||||
}
|
}
|
||||||
|
|
||||||
let sample_count = reader.len();
|
let sample_count = reader.len();
|
||||||
|
@ -186,7 +191,7 @@ where
|
||||||
progress_update(1.0, img.to_rgba8());
|
progress_update(1.0, img.to_rgba8());
|
||||||
|
|
||||||
img.save_with_format(&Path::new(output_file), image::ImageFormat::Png)
|
img.save_with_format(&Path::new(output_file), image::ImageFormat::Png)
|
||||||
.map_err(|err| format!("Could not save outputfile: {}", err))?;
|
.map_err(|err| DecoderError::OutputFileError(err))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
use hound;
|
||||||
|
use image;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum DecoderError {
|
||||||
|
#[error("Unable to read input file: {0}")]
|
||||||
|
InputFileError(#[from] hound::Error),
|
||||||
|
|
||||||
|
#[error("Expected a sampling rate of 48000Hz not {0}Hz")]
|
||||||
|
UnexpectedSamplingRate(u32),
|
||||||
|
|
||||||
|
#[error("Unable to write output file: {0}")]
|
||||||
|
OutputFileError(#[from] image::ImageError),
|
||||||
|
|
||||||
|
#[error("FIXME: Unknown decoder error")]
|
||||||
|
Unknown,
|
||||||
|
}
|
|
@ -4,10 +4,12 @@ extern crate eframe;
|
||||||
extern crate hound;
|
extern crate hound;
|
||||||
extern crate image;
|
extern crate image;
|
||||||
extern crate rfd;
|
extern crate rfd;
|
||||||
|
extern crate thiserror;
|
||||||
|
|
||||||
mod amdemod;
|
mod amdemod;
|
||||||
mod aptsyncer;
|
mod aptsyncer;
|
||||||
mod decoder;
|
mod decoder;
|
||||||
|
mod errors;
|
||||||
mod firfilter;
|
mod firfilter;
|
||||||
mod resamplers;
|
mod resamplers;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
51
src/ui.rs
51
src/ui.rs
|
@ -6,6 +6,7 @@ use eframe::egui::{Color32, RichText};
|
||||||
use eframe::{egui, epi};
|
use eframe::{egui, epi};
|
||||||
|
|
||||||
use decoder;
|
use decoder;
|
||||||
|
use errors::DecoderError;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum DecoderRunState {
|
enum DecoderRunState {
|
||||||
|
@ -19,6 +20,7 @@ struct DecoderJobState {
|
||||||
progress: f32,
|
progress: f32,
|
||||||
texture: Option<egui::TextureId>,
|
texture: Option<egui::TextureId>,
|
||||||
run_state: DecoderRunState,
|
run_state: DecoderRunState,
|
||||||
|
error: Option<DecoderError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DecoderJobState {
|
impl DecoderJobState {
|
||||||
|
@ -34,6 +36,7 @@ impl Default for DecoderJobState {
|
||||||
progress: 0.0,
|
progress: 0.0,
|
||||||
texture: None,
|
texture: None,
|
||||||
run_state: DecoderRunState::DONE,
|
run_state: DecoderRunState::DONE,
|
||||||
|
error: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,32 +135,43 @@ impl epi::App for DecoderApp {
|
||||||
let decoding_state = decoding_state.clone();
|
let decoding_state = decoding_state.clone();
|
||||||
let input_path = input_path.clone();
|
let input_path = input_path.clone();
|
||||||
let output_path = output_path.clone();
|
let output_path = output_path.clone();
|
||||||
|
|
||||||
|
state.error = None;
|
||||||
state.run_state = DecoderRunState::RUNNING;
|
state.run_state = DecoderRunState::RUNNING;
|
||||||
|
if let Some(old_texture) = state.texture {
|
||||||
|
frame.free_texture(old_texture);
|
||||||
|
}
|
||||||
|
state.texture = None;
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
decoder::decode(&input_path, &output_path, |progress, image| {
|
let decoder_res =
|
||||||
let mut state = decoding_state.lock().unwrap();
|
decoder::decode(&input_path, &output_path, |progress, image| {
|
||||||
|
let mut state = decoding_state.lock().unwrap();
|
||||||
|
|
||||||
state.progress = progress;
|
state.progress = progress;
|
||||||
|
|
||||||
let size = [image.width() as _, image.height() as _];
|
let size = [image.width() as _, image.height() as _];
|
||||||
let epi_img = epi::Image::from_rgba_unmultiplied(
|
let epi_img = epi::Image::from_rgba_unmultiplied(
|
||||||
size,
|
size,
|
||||||
image.as_flat_samples().as_slice(),
|
image.as_flat_samples().as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(old_texture) = state.texture {
|
if let Some(old_texture) = state.texture {
|
||||||
frame.free_texture(old_texture);
|
frame.free_texture(old_texture);
|
||||||
}
|
}
|
||||||
state.texture = Some(frame.alloc_texture(epi_img));
|
state.texture = Some(frame.alloc_texture(epi_img));
|
||||||
|
|
||||||
frame.request_repaint();
|
frame.request_repaint();
|
||||||
|
|
||||||
return (state.is_running(), state.update_steps);
|
return (state.is_running(), state.update_steps);
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut state = decoding_state.lock().unwrap();
|
let mut state = decoding_state.lock().unwrap();
|
||||||
state.run_state = DecoderRunState::DONE;
|
state.run_state = DecoderRunState::DONE;
|
||||||
|
state.error = match decoder_res {
|
||||||
|
Err(err) => Some(err),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
frame.request_repaint();
|
frame.request_repaint();
|
||||||
});
|
});
|
||||||
|
@ -175,6 +189,11 @@ impl epi::App for DecoderApp {
|
||||||
|
|
||||||
let progressbar = ProgressBar::new(state.progress).show_percentage();
|
let progressbar = ProgressBar::new(state.progress).show_percentage();
|
||||||
ui.add(progressbar);
|
ui.add(progressbar);
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
if let Some(err) = &state.error {
|
||||||
|
ui.label(RichText::new(err.to_string()).color(Color32::RED));
|
||||||
|
};
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
|
27
src/utils.rs
27
src/utils.rs
|
@ -4,15 +4,28 @@ extern crate hound;
|
||||||
|
|
||||||
type FileReader = std::io::BufReader<std::fs::File>;
|
type FileReader = std::io::BufReader<std::fs::File>;
|
||||||
|
|
||||||
pub fn float_sample_iterator<'a>(reader: &'a mut hound::WavReader<FileReader>)
|
pub fn float_sample_iterator<'a>(
|
||||||
-> Box<Iterator<Item=f32> + 'a> {
|
reader: &'a mut hound::WavReader<FileReader>,
|
||||||
|
) -> Box<Iterator<Item = f32> + 'a> {
|
||||||
match reader.spec().sample_format {
|
match reader.spec().sample_format {
|
||||||
hound::SampleFormat::Float => Box::new(reader.samples::<f32>().map(|x| x.unwrap())),
|
hound::SampleFormat::Float => Box::new(reader.samples::<f32>().map(|x| x.unwrap())),
|
||||||
hound::SampleFormat::Int => match reader.spec().bits_per_sample {
|
hound::SampleFormat::Int => match reader.spec().bits_per_sample {
|
||||||
8 => Box::new(reader.samples::<i8>().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))),
|
8 => Box::new(
|
||||||
16 => Box::new(reader.samples::<i16>().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))),
|
reader
|
||||||
32 => Box::new(reader.samples::<i32>().map(|x| (x.unwrap() as f32) / (i32::max_value() as f32))),
|
.samples::<i8>()
|
||||||
_ => panic!("Unsupported sample rate")
|
.map(|x| (x.unwrap() as f32) / (i16::max_value() as f32)),
|
||||||
}
|
),
|
||||||
|
16 => Box::new(
|
||||||
|
reader
|
||||||
|
.samples::<i16>()
|
||||||
|
.map(|x| (x.unwrap() as f32) / (i16::max_value() as f32)),
|
||||||
|
),
|
||||||
|
32 => Box::new(
|
||||||
|
reader
|
||||||
|
.samples::<i32>()
|
||||||
|
.map(|x| (x.unwrap() as f32) / (i32::max_value() as f32)),
|
||||||
|
),
|
||||||
|
_ => panic!("Unsupported sample format"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue