diff --git a/Cargo.toml b/Cargo.toml index c19ff84..ddfb247 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,9 @@ update-aubio-bindings = ["bliss-audio-aubio-rs/bindgen"] python-bindings = ["bliss-audio-aubio-rs/fftw3"] # Enable the benchmarks with `cargo +nightly bench --features=bench` bench = [] +# Disable song decoding through ffmpeg - only use if you know what you +# are doing and want to decode songs yourself +disable-ffmpeg = [] library = [ "serde", "dep:rusqlite", "dep:dirs", "dep:tempdir", "dep:anyhow", "dep:serde_ini", "dep:serde_json", @@ -84,4 +87,12 @@ required-features = ["library"] [[example]] name = "playlist" -required-features = ["serde"] +required-features = ["serde", "ffmpeg"] + +[[example]] +name = "distance" +required-features = ["ffmpeg"] + +[[example]] +name = "analyze" +required-features = ["ffmpeg"] diff --git a/src/chroma.rs b/src/chroma.rs index a8a3727..04b2475 100644 --- a/src/chroma.rs +++ b/src/chroma.rs @@ -434,6 +434,7 @@ mod test { } #[test] + #[cfg(not(feature = "disable-ffmpeg"))] fn test_chroma_desc() { let song = Song::decode(Path::new("data/s16_mono_22_5kHz.flac")).unwrap(); let mut chroma_desc = ChromaDesc::new(SAMPLE_RATE, 12); @@ -456,6 +457,7 @@ mod test { } #[test] + #[cfg(not(feature = "disable-ffmpeg"))] fn test_chroma_stft_decode() { let signal = Song::decode(Path::new("data/s16_mono_22_5kHz.flac")) .unwrap() @@ -489,6 +491,7 @@ mod test { } #[test] + #[cfg(not(feature = "disable-ffmpeg"))] fn test_estimate_tuning_decode() { let signal = Song::decode(Path::new("data/s16_mono_22_5kHz.flac")) .unwrap() @@ -606,6 +609,7 @@ mod bench { } #[bench] + #[cfg(not(feature = "disable-ffmpeg"))] fn bench_chroma_desc(b: &mut Bencher) { let song = Song::decode(Path::new("data/s16_mono_22_5kHz.flac")).unwrap(); let mut chroma_desc = ChromaDesc::new(SAMPLE_RATE, 12); @@ -617,6 +621,7 @@ mod bench { } #[bench] + #[cfg(not(feature = "disable-ffmpeg"))] fn bench_chroma_stft(b: &mut Bencher) { let song = Song::decode(Path::new("data/s16_mono_22_5kHz.flac")).unwrap(); let mut chroma_desc = ChromaDesc::new(SAMPLE_RATE, 12); @@ -628,6 +633,7 @@ mod bench { } #[bench] + #[cfg(not(feature = "disable-ffmpeg"))] fn bench_chroma_stft_decode(b: &mut Bencher) { let signal = Song::decode(Path::new("data/s16_mono_22_5kHz.flac")) .unwrap() diff --git a/src/cue.rs b/src/cue.rs index 616899b..b7f0b4c 100644 --- a/src/cue.rs +++ b/src/cue.rs @@ -49,6 +49,7 @@ impl BlissCue { /// Each returned [Song] has a populated [cue_info](Song::cue_info) object, that can be /// be used to retrieve which CUE sheet was used to extract it, as well /// as the corresponding audio file. + #[cfg(not(feature = "disable-ffmpeg"))] pub fn songs_from_path>(path: P) -> BlissResult>> { let cue = BlissCue::from_path(&path)?; let cue_files = cue.files(); @@ -86,6 +87,7 @@ impl BlissCue { } // List all BlissCueFile from a BlissCue. + #[cfg(not(feature = "disable-ffmpeg"))] fn files(&self) -> Vec> { let mut cue_files = Vec::new(); for cue_file in self.cue.files.iter() { @@ -196,6 +198,7 @@ mod tests { use pretty_assertions::assert_eq; #[test] + #[cfg(not(feature = "disable-ffmpeg"))] fn test_empty_cue() { let songs = BlissCue::songs_from_path("data/empty.cue").unwrap(); let error = songs[0].to_owned().unwrap_err(); @@ -206,6 +209,7 @@ mod tests { } #[test] + #[cfg(not(feature = "disable-ffmpeg"))] fn test_cue_analysis() { let songs = BlissCue::songs_from_path("data/testcue.cue").unwrap(); let expected = vec![ diff --git a/src/lib.rs b/src/lib.rs index 72084b1..73ebdcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,9 +23,13 @@ //! an example of how the [Library] struct works, and a real-life demo of bliss //! implemented for [MPD](https://www.musicpd.org/). //! +#![cfg_attr( + not(feature = "disable-ffmpeg"), + doc = r##" //! # Examples //! //! ### Analyze & compute the distance between two songs +//! //! ```no_run //! use bliss_audio::{BlissResult, Song}; //! @@ -62,6 +66,8 @@ //! Ok(()) //! } //! ``` +"## +)] #![cfg_attr(feature = "bench", feature(test))] #![warn(missing_docs)] mod chroma; @@ -152,6 +158,7 @@ pub type BlissResult = Result; /// Ok(()) /// } /// ``` +#[cfg(not(feature = "disable-ffmpeg"))] pub fn analyze_paths, F: IntoIterator>( paths: F, ) -> mpsc::IntoIter<(PathBuf, BlissResult)> { @@ -199,6 +206,7 @@ pub fn analyze_paths, F: IntoIterator>( /// Ok(()) /// } /// ``` +#[cfg(not(feature = "disable-ffmpeg"))] pub fn analyze_paths_with_cores, F: IntoIterator>( paths: F, number_cores: NonZeroUsize, @@ -270,6 +278,7 @@ mod tests { } #[test] + #[cfg(not(feature = "disable-ffmpeg"))] fn test_analyze_paths() { let paths = vec![ "./data/s16_mono_22_5kHz.flac", diff --git a/src/misc.rs b/src/misc.rs index 1693590..fa0a201 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -67,6 +67,7 @@ mod tests { use std::path::Path; #[test] + #[cfg(not(feature = "disable-ffmpeg"))] fn test_loudness() { let song = Song::decode(Path::new("data/s16_mono_22_5kHz.flac")).unwrap(); let mut loudness_desc = LoudnessDesc::default(); diff --git a/src/song.rs b/src/song.rs index 3c63e45..d4279f7 100644 --- a/src/song.rs +++ b/src/song.rs @@ -8,6 +8,7 @@ //! a look at Library is instead recommended. extern crate crossbeam; +#[cfg(not(feature = "disable-ffmpeg"))] extern crate ffmpeg_next as ffmpeg; extern crate ndarray; @@ -24,13 +25,21 @@ use crate::{CHANNELS, FEATURES_VERSION}; use ::log::warn; use core::ops::Index; use crossbeam::thread; +#[cfg(not(feature = "disable-ffmpeg"))] use ffmpeg_next::codec::threading::{Config, Type as ThreadingType}; +#[cfg(not(feature = "disable-ffmpeg"))] use ffmpeg_next::util::channel_layout::ChannelLayout; +#[cfg(not(feature = "disable-ffmpeg"))] use ffmpeg_next::util::error::Error; +#[cfg(not(feature = "disable-ffmpeg"))] use ffmpeg_next::util::error::EINVAL; +#[cfg(not(feature = "disable-ffmpeg"))] use ffmpeg_next::util::format::sample::{Sample, Type}; +#[cfg(not(feature = "disable-ffmpeg"))] use ffmpeg_next::util::frame::audio::Audio; +#[cfg(not(feature = "disable-ffmpeg"))] use ffmpeg_next::util::log; +#[cfg(not(feature = "disable-ffmpeg"))] use ffmpeg_next::util::log::level::Level; use ffmpeg_next::{media, util}; use ndarray::{arr1, Array1}; @@ -84,6 +93,9 @@ pub struct Song { #[derive(Debug, EnumIter, EnumCount)] /// Indexes different fields of an [Analysis](Song::analysis). /// +#[cfg_attr( + not(feature = "disable-ffmpeg"), + doc = r##" /// * Example: /// ```no_run /// use bliss_audio::{AnalysisIndex, BlissResult, Song}; @@ -94,7 +106,8 @@ pub struct Song { /// Ok(()) /// } /// ``` -/// +"## +)] /// Prints the tempo value of an analysis. /// /// Note that this should mostly be used for debugging / distance metric @@ -296,6 +309,7 @@ impl Song { /// The error type returned should give a hint as to whether it was a /// decoding ([DecodingError](BlissError::DecodingError)) or an analysis /// ([AnalysisError](BlissError::AnalysisError)) error. + #[cfg(not(feature = "disable-ffmpeg"))] pub fn from_path>(path: P) -> BlissResult { let raw_song = Song::decode(path.as_ref())?; @@ -437,6 +451,7 @@ impl Song { .unwrap() } + #[cfg(not(feature = "disable-ffmpeg"))] pub(crate) fn decode(path: &Path) -> BlissResult { ffmpeg::init().map_err(|e| { BlissError::DecodingError(format!( @@ -658,6 +673,7 @@ pub(crate) struct InternalSong { pub sample_array: Vec, } +#[cfg(not(feature = "disable-ffmpeg"))] fn resample_frame( rx: Receiver