diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 795bc3a..e2ec1c3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test + args: --features ml codecov: needs: test name: Code Coverage diff --git a/Cargo.lock b/Cargo.lock index 1d2a4d6..a9639b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1552,6 +1552,7 @@ dependencies = [ "pest_derive", "plotters", "pretty_assertions", + "proc-macro2", "rand", "rayon", "rodio", @@ -2265,9 +2266,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.52" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] diff --git a/Cargo.toml b/Cargo.toml index 296210b..f98e58b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ wasm = ["rodio/wasm-bindgen", "wasm-bindgen", "wasm-bindgen-futures", "js-sys", plot = ["plotters"] [dependencies] +proc-macro2 = "1.0.60" async-trait = "0.1.64" once_cell = "1.16.0" paste = "1.0.9" diff --git a/src/analyze/file.rs b/src/analyze/file.rs index 2e9fc6d..756fc1a 100644 --- a/src/analyze/file.rs +++ b/src/analyze/file.rs @@ -43,14 +43,14 @@ pub fn get_audio_data_from_file(file: impl AsRef, start: Option, } /// Play the given segment of an audio file. Used to preview a clip before guessing notes from it. -#[no_coverage] +#[coverage(off)] pub fn preview_audio_file_clip(file: impl AsRef, start: Option, end: Option) -> Res<()> { let file = File::open(file)?; preview_audio_clip(file, start, end) } /// Play the given segment of an audio stream. Used to preview a clip before guessing notes from it. -#[no_coverage] +#[coverage(off)] pub fn preview_audio_clip(stream: impl Read + Seek + Send + Sync + 'static, start: Option, end: Option) -> Res<()> { let start = start.unwrap_or_default(); let decoder = Decoder::new(stream)?.skip_duration(start).convert_samples(); diff --git a/src/analyze/mic.rs b/src/analyze/mic.rs index f507936..5829353 100644 --- a/src/analyze/mic.rs +++ b/src/analyze/mic.rs @@ -16,7 +16,7 @@ use crate::core::{base::Res, note::Note}; use super::base::get_notes_from_audio_data; /// Gets notes from the microphone input over the specified period of time. -#[no_coverage] +#[coverage(off)] pub async fn get_notes_from_microphone(length_in_seconds: u8) -> Res> { // Get data. @@ -30,7 +30,7 @@ pub async fn get_notes_from_microphone(length_in_seconds: u8) -> Res> } /// Gets audio data from the microphone. -#[no_coverage] +#[coverage(off)] pub async fn get_audio_data_from_microphone(length_in_seconds: u8) -> Res> { if length_in_seconds < 1 { return Err(anyhow::Error::msg("Listening length in seconds must be greater than 1.")); @@ -48,7 +48,7 @@ pub async fn get_audio_data_from_microphone(length_in_seconds: u8) -> Res Res<(cpal::Device, cpal::SupportedStreamConfig)> { let host = cpal::default_host(); @@ -60,7 +60,7 @@ fn get_device_and_config() -> Res<(cpal::Device, cpal::SupportedStreamConfig)> { } /// Records audio data from the device. -#[no_coverage] +#[coverage(off)] async fn record_from_device(device: cpal::Device, config: cpal::SupportedStreamConfig, length_in_seconds: u8) -> Res> { // Set up recording. diff --git a/src/core/chord.rs b/src/core/chord.rs index d54abe0..61b4893 100644 --- a/src/core/chord.rs +++ b/src/core/chord.rs @@ -1230,7 +1230,7 @@ use super::base::{Playable, PlaybackHandle}; #[cfg(feature = "audio")] impl Playable for Chord { - #[no_coverage] + #[coverage(off)] fn play(&self, delay: Duration, length: Duration, fade_in: Duration) -> Res { use rodio::{source::SineWave, OutputStream, Sink, Source}; diff --git a/src/core/modifier.rs b/src/core/modifier.rs index 0d42126..77b5379 100644 --- a/src/core/modifier.rs +++ b/src/core/modifier.rs @@ -117,7 +117,7 @@ impl HasIsDominant for Modifier { } impl HasStaticName for Degree { - #[no_coverage] + #[coverage(off)] fn static_name(&self) -> &'static str { match self { Degree::Seven => "7", @@ -129,7 +129,7 @@ impl HasStaticName for Degree { } impl HasStaticName for Modifier { - #[no_coverage] + #[coverage(off)] fn static_name(&self) -> &'static str { match self { Modifier::Minor => "m", @@ -151,7 +151,7 @@ impl HasStaticName for Modifier { } impl HasStaticName for Extension { - #[no_coverage] + #[coverage(off)] fn static_name(&self) -> &'static str { match self { Extension::Sus2 => "sus2", diff --git a/src/core/named_pitch.rs b/src/core/named_pitch.rs index 76546f1..26bc613 100644 --- a/src/core/named_pitch.rs +++ b/src/core/named_pitch.rs @@ -150,7 +150,7 @@ impl HasNamedPitch for NamedPitch { } impl HasLetter for NamedPitch { - #[no_coverage] + #[coverage(off)] fn letter(&self) -> &'static str { match self { NamedPitch::FTripleFlat => "F", @@ -213,7 +213,7 @@ impl HasLetter for NamedPitch { } impl HasStaticName for NamedPitch { - #[no_coverage] + #[coverage(off)] fn static_name(&self) -> &'static str { match self { NamedPitch::FTripleFlat => "F♭𝄫", @@ -276,7 +276,7 @@ impl HasStaticName for NamedPitch { } impl HasPitch for NamedPitch { - #[no_coverage] + #[coverage(off)] fn pitch(&self) -> Pitch { match self { NamedPitch::FTripleFlat => Pitch::D, diff --git a/src/core/note.rs b/src/core/note.rs index 9367ba8..8dfb6cc 100644 --- a/src/core/note.rs +++ b/src/core/note.rs @@ -191,7 +191,7 @@ impl Note { /// to identify the notes in the recorded audio. /// /// Currently, this does not work with WASM. - #[no_coverage] + #[coverage(off)] #[cfg(feature = "analyze_mic")] pub async fn try_from_mic(length_in_seconds: u8) -> Res> { use crate::analyze::mic::get_notes_from_microphone; @@ -211,7 +211,7 @@ impl Note { /// to identify the notes in the recorded audio using ML. /// /// Currently, this does not work with WASM. - #[no_coverage] + #[coverage(off)] #[cfg(all(feature = "ml_infer", feature = "analyze_mic"))] pub async fn try_from_mic_ml(length_in_seconds: u8) -> Res> { use crate::{analyze::mic::get_audio_data_from_microphone, ml::infer::infer}; diff --git a/src/core/parser.rs b/src/core/parser.rs index e09a6bd..e798c80 100644 --- a/src/core/parser.rs +++ b/src/core/parser.rs @@ -18,7 +18,7 @@ pub struct ChordParser; // Helpers. /// Parses a [`Note`] [`str`] into a [`Note`]. -#[no_coverage] +#[coverage(off)] pub fn note_str_to_note(note_str: &str) -> Res { let chord = match note_str { "A" => note::A, @@ -63,7 +63,7 @@ pub fn note_str_to_note(note_str: &str) -> Res { } /// Parses an [`Octave`] [`str`] into an [`Octave`]. -#[no_coverage] +#[coverage(off)] pub fn octave_str_to_octave(note_str: &str) -> Res { let octave = match note_str { "0" => Octave::Zero, diff --git a/src/core/pitch.rs b/src/core/pitch.rs index d9969e5..b45512f 100644 --- a/src/core/pitch.rs +++ b/src/core/pitch.rs @@ -93,7 +93,7 @@ pub enum Pitch { // Pitch impls. impl HasBaseFrequency for Pitch { - #[no_coverage] + #[coverage(off)] fn base_frequency(&self) -> f32 { match self { Pitch::C => 16.35, diff --git a/src/helpers.rs b/src/helpers.rs index d76d207..9ed3bc6 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,5 +1,3 @@ -//! Global helper functions. - /// Plot the frequency space of the microphone input using plotters. #[cfg(feature = "plot")] pub fn plot_frequency_space(frequency_space: &[(f32, f32)], title: &str, file_name: &str, x_min: f32, x_max: f32) { diff --git a/src/lib.rs b/src/lib.rs index a66db62..3bbc046 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,8 +46,8 @@ #![feature(specialization)] #![feature(concat_idents)] #![feature(iter_advance_by)] -#![feature(no_coverage)] #![feature(int_roundings)] +#![feature(coverage_attribute)] pub mod core; pub mod helpers; diff --git a/src/ml/base/gather.rs b/src/ml/base/gather.rs index 4a56195..2fb62be 100644 --- a/src/ml/base/gather.rs +++ b/src/ml/base/gather.rs @@ -18,7 +18,7 @@ use crate::analyze::mic::get_audio_data_from_microphone; use super::helpers::save_kord_item; /// Gather a sample from the microphone and save it to disk. -#[no_coverage] +#[coverage(off)] pub fn gather_sample(destination: impl AsRef, length_in_seconds: u8) -> Void { println!("Listening ..."); diff --git a/src/ml/base/helpers.rs b/src/ml/base/helpers.rs index 0111569..ff61ff2 100644 --- a/src/ml/base/helpers.rs +++ b/src/ml/base/helpers.rs @@ -12,7 +12,7 @@ use burn::tensor::{backend::Backend, Tensor}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use crate::{ - analyze::base::get_notes_from_smoothed_frequency_space, + analyze::base::{get_notes_from_smoothed_frequency_space}, core::{ base::Res, helpers::{inv_mel, mel}, @@ -111,7 +111,13 @@ pub fn mel_filter_banks_from(spectrum: &[f32]) -> [f32; MEL_SPACE_SIZE] { pub fn harmonic_convolution(spectrum: &[f32]) -> [f32; FREQUENCY_SPACE_SIZE] { let mut harmonic_convolution = [0f32; FREQUENCY_SPACE_SIZE]; - let (peak, _) = spectrum.iter().enumerate().fold((0usize, 0f32), |(k, max), (j, x)| if *x > max { (j, *x) } else { (k, max) }); + let (peak, _) = spectrum.iter().enumerate().fold((0usize, 0f32), |(k, max), (j, x)| { + if *x > max { + (j, *x) + } else { + (k, max) + } + }); for center in (peak / 2)..4000 { let mut sum = spectrum[center]; @@ -171,9 +177,6 @@ pub fn binary_to_u128(binary: &[f32]) -> u128 { num } -/// Folds a 128 element array of 0s and 1s into a 12 element array of 0s and 1s. -/// -/// Essentially, this is useful if we want to do inference on the pitches without octaves. #[allow(dead_code)] pub fn fold_binary(binary: &[f32; 128]) -> [f32; 12] { let mut folded = [0f32; 12]; @@ -191,21 +194,18 @@ pub fn fold_binary(binary: &[f32; 128]) -> [f32; 12] { // Common tensor operations. -/// A sigmoid activation function. #[derive(Debug, Clone)] pub struct Sigmoid { scale: f32, } impl Sigmoid { - /// Create a new sigmoid activation function. pub fn new(scale: f32) -> Self { Self { scale } } - /// Forward pass. pub fn forward(&self, input: Tensor) -> Tensor { let scaled = input.mul_scalar(self.scale); scaled.clone().exp().div(scaled.exp().add_scalar(1.0)) } -} +} \ No newline at end of file diff --git a/src/ml/base/model.rs b/src/ml/base/model.rs index 2dc5cca..5378a20 100644 --- a/src/ml/base/model.rs +++ b/src/ml/base/model.rs @@ -16,7 +16,6 @@ use crate::ml::train::{ helpers::{KordClassificationOutput, MeanSquareLoss}, }; -/// The primary model type for identifying notes / chords. #[derive(Module, Debug)] pub struct KordModel { input: Param>, @@ -26,7 +25,6 @@ pub struct KordModel { } impl KordModel { - /// Create a new model with the given parameters. pub fn new(mlp_layers: usize, mlp_size: usize, mlp_dropout: f64, sigmoid_strength: f32) -> Self { let input = nn::Linear::new(&nn::LinearConfig::new(INPUT_SPACE_SIZE, mlp_size)); let mlp = Mlp::new(mlp_layers, mlp_size, mlp_dropout); @@ -41,7 +39,6 @@ impl KordModel { } } - /// Forward pass through the model. pub fn forward(&self, input: Tensor) -> Tensor { let mut x = input; @@ -54,8 +51,8 @@ impl KordModel { } #[cfg(feature = "ml_train")] - /// Forward pass through the model, with loss calculation. pub fn forward_classification(&self, item: KordBatch) -> KordClassificationOutput { + let targets = item.targets; let output = self.forward(item.samples); @@ -80,18 +77,17 @@ impl KordModel { } } -/// A convolutional block. #[derive(Module, Debug)] pub struct ConvBlock { conv: Param>, activation: nn::ReLU, } -/// A convolutional block. impl ConvBlock { - /// Create a new convolutional block. pub fn new(in_channels: usize, out_channels: usize, kernel_size: usize) -> Self { - let conv = nn::conv::Conv1d::new(&nn::conv::Conv1dConfig::new(in_channels, out_channels, kernel_size).with_bias(false)); + let conv = nn::conv::Conv1d::new( + &nn::conv::Conv1dConfig::new(in_channels, out_channels, kernel_size).with_bias(false), + ); Self { conv: Param::from(conv), @@ -99,9 +95,8 @@ impl ConvBlock { } } - /// Forward pass through the block. pub fn forward(&self, input: Tensor) -> Tensor { let x = self.conv.forward(input); self.activation.forward(x) } -} +} \ No newline at end of file diff --git a/src/ml/train/execute.rs b/src/ml/train/execute.rs index 13500c8..957349a 100644 --- a/src/ml/train/execute.rs +++ b/src/ml/train/execute.rs @@ -113,7 +113,7 @@ where Ok(accuracy) } -#[no_coverage] +#[coverage(off)] pub fn compute_overall_accuracy(model_trained: &KordModel, device: &B::Device) -> f32 { let dataset = KordDataset::from_folder_and_simulation("samples", 0, 0.0, 0.0, 0.0); @@ -192,7 +192,7 @@ pub fn compute_overall_accuracy(model_trained: &KordModel, device inference_accuracy } -#[no_coverage] +#[coverage(off)] pub fn hyper_parameter_tuning(source: String, destination: String, log: String, device: String) -> Void { let peak_radiuses = [1.0]; let harmonic_decays = [0.1];