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
## bliss 0.4.3
* Add features' version on each Song instance.
## bliss 0.4.2
* Add a binary example to easily make playlists.

2
Cargo.lock generated
View file

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

View file

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

View file

@ -321,7 +321,7 @@ fn estimate_tuning(
resolution: f64,
bins_per_octave: u32,
) -> 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
.iter()

View file

@ -107,7 +107,7 @@ pub fn dedup_playlist_custom_distance(
distance: impl DistanceMetric,
) {
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()
&& s2.title.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 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)]
/// Umbrella type for bliss error types

View file

@ -56,7 +56,7 @@ pub trait Library {
for song in &songs {
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
.push_row(song.analysis.as_arr1().view())
.map_err(|e| {
@ -65,7 +65,7 @@ pub trait Library {
} else {
let mut array = Array::zeros((1, song.analysis.as_arr1().len()));
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)| {
n32(euclidean_distance(
first_analysis.as_ref().unwrap(),
&analysis,
analysis,
))
});
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_npy;
use super::CHANNELS;
use crate::{CHANNELS, FEATURES_VERSION};
use crate::chroma::ChromaDesc;
use crate::distance::{euclidean_distance, DistanceMetric};
use crate::misc::LoudnessDesc;
@ -61,6 +61,10 @@ pub struct Song {
pub genre: Option<String>,
/// bliss analysis results
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)]
@ -251,6 +255,7 @@ impl Song {
track_number: raw_song.track_number,
genre: raw_song.genre,
analysis: Song::analyse(raw_song.sample_array)?,
features_version: FEATURES_VERSION,
})
}
@ -289,7 +294,7 @@ impl Song {
.step_by(BPMDesc::HOP_SIZE);
for window in windows {
tempo_desc.do_(&window)?;
tempo_desc.do_(window)?;
}
Ok(tempo_desc.get_value())
});
@ -310,7 +315,7 @@ impl Song {
.windows(SpectralDesc::WINDOW_SIZE)
.step_by(SpectralDesc::HOP_SIZE);
for window in windows {
spectral_desc.do_(&window)?;
spectral_desc.do_(window)?;
}
let centroid = spectral_desc.get_centroid();
let rolloff = spectral_desc.get_rolloff();
@ -330,7 +335,7 @@ impl Song {
let windows = sample_array.chunks(LoudnessDesc::WINDOW_SIZE);
for window in windows {
loudness_desc.do_(&window);
loudness_desc.do_(window);
}
Ok(loudness_desc.get_value())
});
@ -657,6 +662,7 @@ mod tests {
for (x, y) in song.analysis.as_vec().iter().zip(expected_analysis) {
assert!(0.01 > (x - y).abs());
}
assert_eq!(FEATURES_VERSION, song.features_version);
}
fn _test_decode(path: &Path, expected_hash: &[u8]) {

View file

@ -204,12 +204,12 @@ impl SpectralDesc {
self.values_rolloff.push(freq);
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 {
self.values_flatness.push(0.0);
return Ok(());
}
let flatness = geo_mean / mean(&cvec.norm());
let flatness = geo_mean / mean(cvec.norm());
self.values_flatness.push(flatness);
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,
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
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)
.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() {
Some(s) => fft.process(s),
None => {