Add a features' version number

This commit is contained in:
Polochon-street 2022-01-05 19:19:50 +01:00
parent a27b91c6fd
commit 8468a9ab8f
10 changed files with 28 additions and 15 deletions

View file

@ -1,5 +1,8 @@
# Changelog # Changelog
## bliss 0.4.3
* Add features' version on each Song instance.
## bliss 0.4.2 ## bliss 0.4.2
* Add a binary example to easily make playlists. * Add a binary example to easily make playlists.

2
Cargo.lock generated
View file

@ -86,7 +86,7 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bliss-audio" name = "bliss-audio"
version = "0.4.2" version = "0.4.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bliss-audio-aubio-rs", "bliss-audio-aubio-rs",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "bliss-audio" name = "bliss-audio"
version = "0.4.2" version = "0.4.3"
authors = ["Polochon-street <polochonstreet@gmx.fr>"] authors = ["Polochon-street <polochonstreet@gmx.fr>"]
edition = "2018" edition = "2018"
license = "GPL-3.0-only" license = "GPL-3.0-only"

View file

@ -321,7 +321,7 @@ fn estimate_tuning(
resolution: f64, resolution: f64,
bins_per_octave: u32, bins_per_octave: u32,
) -> BlissResult<f64> { ) -> BlissResult<f64> {
let (pitch, mag) = pip_track(sample_rate, &spectrum, n_fft)?; let (pitch, mag) = pip_track(sample_rate, spectrum, n_fft)?;
let (filtered_pitch, filtered_mag): (Vec<N64>, Vec<N64>) = pitch let (filtered_pitch, filtered_mag): (Vec<N64>, Vec<N64>) = pitch
.iter() .iter()

View file

@ -107,7 +107,7 @@ pub fn dedup_playlist_custom_distance(
distance: impl DistanceMetric, distance: impl DistanceMetric,
) { ) {
songs.dedup_by(|s1, s2| { songs.dedup_by(|s1, s2| {
n32(s1.custom_distance(&s2, &distance)) < distance_threshold.unwrap_or(0.05) n32(s1.custom_distance(s2, &distance)) < distance_threshold.unwrap_or(0.05)
|| (s1.title.is_some() || (s1.title.is_some()
&& s2.title.is_some() && s2.title.is_some()
&& s1.artist.is_some() && s1.artist.is_some()

View file

@ -85,6 +85,10 @@ pub use song::{Analysis, AnalysisIndex, Song, NUMBER_FEATURES};
const CHANNELS: u16 = 1; const CHANNELS: u16 = 1;
const SAMPLE_RATE: u32 = 22050; const SAMPLE_RATE: u32 = 22050;
/// Stores the current version of bliss-rs' features.
/// It is bumped every time one or more feature is added, updated or removed,
/// so plug-ins can rescan libraries when there is a major change.
const FEATURES_VERSION: u16 = 1;
#[derive(Error, Clone, Debug, PartialEq)] #[derive(Error, Clone, Debug, PartialEq)]
/// Umbrella type for bliss error types /// Umbrella type for bliss error types

View file

@ -56,7 +56,7 @@ pub trait Library {
for song in &songs { for song in &songs {
if let Some(album) = &song.album { if let Some(album) = &song.album {
if let Some(analysis) = albums_analysis.get_mut(&album as &str) { if let Some(analysis) = albums_analysis.get_mut(album as &str) {
analysis analysis
.push_row(song.analysis.as_arr1().view()) .push_row(song.analysis.as_arr1().view())
.map_err(|e| { .map_err(|e| {
@ -65,7 +65,7 @@ pub trait Library {
} else { } else {
let mut array = Array::zeros((1, song.analysis.as_arr1().len())); let mut array = Array::zeros((1, song.analysis.as_arr1().len()));
array.assign(&song.analysis.as_arr1()); array.assign(&song.analysis.as_arr1());
albums_analysis.insert(&album, array); albums_analysis.insert(album, array);
} }
} }
} }
@ -92,7 +92,7 @@ pub trait Library {
albums.sort_by_key(|(_, analysis)| { albums.sort_by_key(|(_, analysis)| {
n32(euclidean_distance( n32(euclidean_distance(
first_analysis.as_ref().unwrap(), first_analysis.as_ref().unwrap(),
&analysis, analysis,
)) ))
}); });
let albums = albums.get(..playlist_length).unwrap_or(&albums); let albums = albums.get(..playlist_length).unwrap_or(&albums);

View file

@ -12,7 +12,7 @@ extern crate ffmpeg_next as ffmpeg;
extern crate ndarray; extern crate ndarray;
extern crate ndarray_npy; extern crate ndarray_npy;
use super::CHANNELS; use crate::{CHANNELS, FEATURES_VERSION};
use crate::chroma::ChromaDesc; use crate::chroma::ChromaDesc;
use crate::distance::{euclidean_distance, DistanceMetric}; use crate::distance::{euclidean_distance, DistanceMetric};
use crate::misc::LoudnessDesc; use crate::misc::LoudnessDesc;
@ -61,6 +61,10 @@ pub struct Song {
pub genre: Option<String>, pub genre: Option<String>,
/// bliss analysis results /// bliss analysis results
pub analysis: Analysis, pub analysis: Analysis,
/// Version of the features the song was analyzed with.
/// A simple integer that is bumped every time a breaking change
/// is introduced in the features.
pub features_version: u16,
} }
#[derive(Debug, EnumIter, EnumCount)] #[derive(Debug, EnumIter, EnumCount)]
@ -251,6 +255,7 @@ impl Song {
track_number: raw_song.track_number, track_number: raw_song.track_number,
genre: raw_song.genre, genre: raw_song.genre,
analysis: Song::analyse(raw_song.sample_array)?, analysis: Song::analyse(raw_song.sample_array)?,
features_version: FEATURES_VERSION,
}) })
} }
@ -289,7 +294,7 @@ impl Song {
.step_by(BPMDesc::HOP_SIZE); .step_by(BPMDesc::HOP_SIZE);
for window in windows { for window in windows {
tempo_desc.do_(&window)?; tempo_desc.do_(window)?;
} }
Ok(tempo_desc.get_value()) Ok(tempo_desc.get_value())
}); });
@ -310,7 +315,7 @@ impl Song {
.windows(SpectralDesc::WINDOW_SIZE) .windows(SpectralDesc::WINDOW_SIZE)
.step_by(SpectralDesc::HOP_SIZE); .step_by(SpectralDesc::HOP_SIZE);
for window in windows { for window in windows {
spectral_desc.do_(&window)?; spectral_desc.do_(window)?;
} }
let centroid = spectral_desc.get_centroid(); let centroid = spectral_desc.get_centroid();
let rolloff = spectral_desc.get_rolloff(); let rolloff = spectral_desc.get_rolloff();
@ -330,7 +335,7 @@ impl Song {
let windows = sample_array.chunks(LoudnessDesc::WINDOW_SIZE); let windows = sample_array.chunks(LoudnessDesc::WINDOW_SIZE);
for window in windows { for window in windows {
loudness_desc.do_(&window); loudness_desc.do_(window);
} }
Ok(loudness_desc.get_value()) Ok(loudness_desc.get_value())
}); });
@ -657,6 +662,7 @@ mod tests {
for (x, y) in song.analysis.as_vec().iter().zip(expected_analysis) { for (x, y) in song.analysis.as_vec().iter().zip(expected_analysis) {
assert!(0.01 > (x - y).abs()); assert!(0.01 > (x - y).abs());
} }
assert_eq!(FEATURES_VERSION, song.features_version);
} }
fn _test_decode(path: &Path, expected_hash: &[u8]) { fn _test_decode(path: &Path, expected_hash: &[u8]) {

View file

@ -204,12 +204,12 @@ impl SpectralDesc {
self.values_rolloff.push(freq); self.values_rolloff.push(freq);
let cvec: CVec = fftgrain.as_slice().into(); let cvec: CVec = fftgrain.as_slice().into();
let geo_mean = geometric_mean(&cvec.norm()); let geo_mean = geometric_mean(cvec.norm());
if geo_mean == 0.0 { if geo_mean == 0.0 {
self.values_flatness.push(0.0); self.values_flatness.push(0.0);
return Ok(()); return Ok(());
} }
let flatness = geo_mean / mean(&cvec.norm()); let flatness = geo_mean / mean(cvec.norm());
self.values_flatness.push(flatness); self.values_flatness.push(flatness);
Ok(()) Ok(())
} }

View file

@ -29,7 +29,7 @@ pub(crate) fn stft(signal: &[f32], window_length: usize, hop_length: usize) -> A
(signal.len() as f32 / hop_length as f32).ceil() as usize, (signal.len() as f32 / hop_length as f32).ceil() as usize,
window_length / 2 + 1, window_length / 2 + 1,
)); ));
let signal = reflect_pad(&signal, window_length / 2); let signal = reflect_pad(signal, window_length / 2);
// Periodic, so window_size + 1 // Periodic, so window_size + 1
let mut hann_window = Array::zeros(window_length + 1); let mut hann_window = Array::zeros(window_length + 1);
@ -45,7 +45,7 @@ pub(crate) fn stft(signal: &[f32], window_length: usize, hop_length: usize) -> A
.step_by(hop_length) .step_by(hop_length)
.zip(stft.rows_mut()) .zip(stft.rows_mut())
{ {
let mut signal = (arr1(&window) * &hann_window).mapv(|x| Complex::new(x, 0.)); let mut signal = (arr1(window) * &hann_window).mapv(|x| Complex::new(x, 0.));
match signal.as_slice_mut() { match signal.as_slice_mut() {
Some(s) => fft.process(s), Some(s) => fft.process(s),
None => { None => {