Add a features' version number
This commit is contained in:
parent
a27b91c6fd
commit
8468a9ab8f
10 changed files with 28 additions and 15 deletions
|
@ -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
2
Cargo.lock
generated
|
@ -86,7 +86,7 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||
|
||||
[[package]]
|
||||
name = "bliss-audio"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bliss-audio-aubio-rs",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
14
src/song.rs
14
src/song.rs
|
@ -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]) {
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
Loading…
Reference in a new issue