Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a "disable-ffmpeg" feature #65

Merged
merged 4 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
submodules: recursive
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2023-02-16
toolchain: nightly-2024-01-16
override: false
- name: Packages
run: sudo apt-get update && sudo apt-get install build-essential yasm libavutil-dev libavcodec-dev libavformat-dev libavfilter-dev libavfilter-dev libavdevice-dev libswresample-dev libfftw3-dev ffmpeg
Expand All @@ -32,14 +32,26 @@ jobs:
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Run library tests
run: cargo test --verbose --features=library
- name: Build benches
run: cargo +nightly-2024-01-16 bench --verbose --features=bench --no-run
- name: Run example tests
run: cargo test --verbose --examples
- name: Build benches
run: cargo +nightly-2023-02-16 bench --verbose --features=bench --no-run
- name: Build examples
- name: Build examples with library
run: cargo build --examples --verbose --features=serde,library
- name: Build with library
run: cargo build --features=library --verbose
- name: Run tests with library
run: cargo test --verbose --features=library
- name: Build without ffmpeg
run: cargo build --verbose --no-default-features
- name: Test without ffmpeg
run: cargo test --verbose --no-default-features
- name: Build library without ffmpeg
run: cargo build --verbose --no-default-features --features=library
- name: Test library without ffmpeg
run: cargo test --verbose --no-default-features --features=library
- name: Lint without ffmpeg
run: cargo clippy --examples --features=serde,library --no-default-features -- -D warnings

build-test-lint-windows:
name: Windows - build, test and lint
Expand Down
19 changes: 14 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
# Changelog

## bliss 0.7.0
* Bump ffmpeg-next version to 7.0
* Bump aubio-rs custom crate to disable compiling it with -std=c99
* Make ffmpeg an optional dependency, to decouple bliss from ffmpeg:
- Remove Song::from_path
- Add a specific `song` and `decoder` module
- Add a `Decoder` trait to make implementing decoders other than ffmpeg more easily
- Add an `FFmpeg` struct implementing the previous decoding behavior with ffmpeg
Existing code will need to be updated by replacing `Song::from_path` by
`song::decoder::bliss_ffmpeg::FFmpeg::song_from_path`, and the other
corresponding functions (see the updated examples for more details).
* Put the decoding logic in its own module.
* Bump ffmpeg-next version to 7.0.
* Bump aubio-rs custom crate to disable compiling it with -std=c99.
* Add the possibility to make playlists based on multiple songs using extended
isolation forest (Thanks @SimonTeixidor!)
* Remove *_by_key family of functions (Thanks @SimonTeixidor!)
isolation forest (Thanks @SimonTeixidor!).
* Remove *_by_key family of functions (Thanks @SimonTeixidor!).
* Remove circular dependency between playlist and song by removing distances
from the `Song` struct (Thanks @SimonTeixidor!)
from the `Song` struct (Thanks @SimonTeixidor!).

## bliss 0.6.11
* Bump rust-ffmpeg to 6.1.1 to fix build for raspberry pis.
Expand Down
30 changes: 22 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@ readme = "README.md"
exclude = ["data/"]

[package.metadata.docs.rs]
features = ["bliss-audio-aubio-rs/rustdoc", "library"]
features = ["bliss-audio-aubio-rs/rustdoc", "library", "ffmpeg"]
no-default-features = true

[features]
default = ["bliss-audio-aubio-rs/static"]
default = ["ffmpeg", "aubio-static"]
# Enable song decoding with ffmpeg. Activated by default, and needed for
# almost all use-cases, disable it at your own risk!
# It is only useful if you want to implement the decoding of the tracks yourself
# and just feed them to bliss, so you don't depend on ffmpeg.
# TODO make ffmpeg a test-dep
ffmpeg = ["dep:ffmpeg-next", "dep:ffmpeg-sys-next"]
aubio-static = ["bliss-audio-aubio-rs/static"]
# Build ffmpeg instead of using the host's.
build-ffmpeg = ["ffmpeg-next/build"]
ffmpeg-static = ["ffmpeg-next/static"]
Expand All @@ -40,8 +47,8 @@ serde = ["dep:serde", "extended-isolation-forest/serde"]
# Until https://github.com/aubio/aubio/issues/336 is somehow solved
# Hopefully we'll be able to use the official aubio-rs at some point.
bliss-audio-aubio-rs = "0.2.2"
ffmpeg-next = "7.0.1"
ffmpeg-sys-next = { version = "7.0.0", default-features = false }
ffmpeg-next = { version = "7.0.1", optional = true }
ffmpeg-sys-next = { version = "7.0.0", optional = true, default-features = false }
log = "0.4.17"
# `rayon` is used only by `par_mapv_inplace` in chroma.rs.
# TODO: is the speed gain that substantial?
Expand Down Expand Up @@ -77,13 +84,20 @@ serde_json = "1.0.59"

[[example]]
name = "library"
required-features = ["library"]
required-features = ["library", "ffmpeg"]

[[example]]
name = "library_extra_info"
required-features = ["library"]

required-features = ["library", "ffmpeg"]

[[example]]
name = "playlist"
required-features = ["serde"]
required-features = ["serde", "ffmpeg"]

[[example]]
name = "distance"
required-features = ["ffmpeg"]

[[example]]
name = "analyze"
required-features = ["ffmpeg"]
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ Ready to use code examples:

### Compute the distance between two songs
```
use bliss_audio::{BlissError, Song};
use bliss_audio::decoder::bliss_ffmpeg::FFmpeg as Decoder;
use bliss_audio::decoder::Decoder as DecoderTrait;
use bliss_audio::BlissError;

fn main() -> Result<(), BlissError> {
let song1 = Song::from_path("/path/to/song1")?;
let song2 = Song::from_path("/path/to/song2")?;
let song1 = Decoder::from_path("/path/to/song1")?;
let song2 = Decoder::from_path("/path/to/song2")?;

println!("Distance between song1 and song2 is {}", song1.distance(&song2));
Ok(())
Expand All @@ -61,14 +63,16 @@ fn main() -> Result<(), BlissError> {

### Make a playlist from a song
```
use bliss_audio::decoder::bliss_ffmpeg::FFmpeg as Decoder;
use bliss_audio::decoder::Decoder as DecoderTrait;
use bliss_audio::{BlissError, Song};
use noisy_float::prelude::n32;

fn main() -> Result<(), BlissError> {
let paths = vec!["/path/to/song1", "/path/to/song2", "/path/to/song3"];
let mut songs: Vec<Song> = paths
.iter()
.map(|path| Song::from_path(path))
.map(|path| Decoder::song_from_path(path))
.collect::<Result<Vec<Song>, BlissError>>()?;

// Assuming there is a first song
Expand Down
16 changes: 11 additions & 5 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@ ask questions if you want to tackle an item.
## Actual TODO

- Add a list of dependencies / installation guide
- Split out ffmpeg (see https://github.com/Polochon-street/bliss-rs/issues/63 and https://users.rust-lang.org/t/proper-way-to-abstract-a-third-party-provider/107076/8)
- Make ffmpeg an optional (but default) feature
- Make the tests that don't need it not dependent of ffmpeg
- Make the Song::from_path a trait that is by default implemented with the
ffmpeg feature (so you can theoretically implement the library trait without ffmpeg)
- Maybe add playlist functions for single songs as convenience methods?
- Regularly update the python bindings with the new code
- Check the chroma feature for anomalies (the last 4 numbers look anomalous in most of my cases -
compare with https://www.audiolabs-erlangen.de/resources/MIR/FMP/C5/C5S2_ChordRec_Templates.html etc)
- Freebsd support? (see https://github.com/Polochon-street/bliss-rs/issues/60)
Expand All @@ -38,3 +35,12 @@ ask questions if you want to tackle an item.
(probably reuse the interactive-playlist in blissify?)
- Improve bliss-python somehow / use it in a small demo project maybe?
A blissify in python?

## Done
- Split out ffmpeg (see https://github.com/Polochon-street/bliss-rs/issues/63 and https://users.rust-lang.org/t/proper-way-to-abstract-a-third-party-provider/107076/8)
- Make ffmpeg an optional (but default) feature
- The library trait must be Decoder-agnostic, and not depend on FFmpeg
- Make the tests that don't need it not dependent of ffmpeg
- Make the Song::from_path a trait that is by default implemented with the
ffmpeg feature (so you can theoretically implement the library trait without ffmpeg)

2 changes: 1 addition & 1 deletion ci_check.sh
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cargo fmt -- --check && cargo clippy --examples --features=serde -- -D warnings && cargo build --verbose && cargo test --verbose && cargo test --verbose --examples && cargo +nightly-2023-02-16 bench --verbose --features=bench --no-run && cargo build --examples --verbose --features=serde
cargo fmt -- --check && cargo clippy --examples --features=serde -- -D warnings && cargo build --verbose && cargo test --verbose && cargo test --verbose --examples && cargo +nightly-2024-01-16 bench --verbose --features=bench --no-run && cargo build --examples --verbose --features=serde && cargo build --no-default-features
5 changes: 3 additions & 2 deletions examples/analyze.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use bliss_audio::Song;
use bliss_audio::decoder::ffmpeg::FFmpeg as Decoder;
use bliss_audio::decoder::Decoder as DecoderTrait;
use std::env;

/**
Expand All @@ -9,7 +10,7 @@ use std::env;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
for path in &args {
match Song::from_path(path) {
match Decoder::song_from_path(path) {
Ok(song) => println!("{}: {:?}", path, song.analysis),
Err(e) => println!("{path}: {e}"),
}
Expand Down
8 changes: 5 additions & 3 deletions examples/distance.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use bliss_audio::{playlist::euclidean_distance, Song};
use bliss_audio::decoder::ffmpeg::FFmpeg as Decoder;
use bliss_audio::decoder::Decoder as DecoderTrait;
use bliss_audio::playlist::euclidean_distance;
use std::env;

/**
Expand All @@ -13,8 +15,8 @@ fn main() -> Result<(), String> {
let first_path = paths.next().ok_or("Help: ./distance <song1> <song2>")?;
let second_path = paths.next().ok_or("Help: ./distance <song1> <song2>")?;

let song1 = Song::from_path(first_path).map_err(|x| x.to_string())?;
let song2 = Song::from_path(second_path).map_err(|x| x.to_string())?;
let song1 = Decoder::song_from_path(first_path).map_err(|x| x.to_string())?;
let song2 = Decoder::song_from_path(second_path).map_err(|x| x.to_string())?;

println!(
"d({:?}, {:?}) = {}",
Expand Down
7 changes: 4 additions & 3 deletions examples/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/// For simplicity's sake, this example recursively gets songs from a folder
/// to emulate an audio player library, without handling CUE files.
use anyhow::Result;
use bliss_audio::decoder::ffmpeg::FFmpeg as Decoder;
use bliss_audio::library::{AppConfigTrait, BaseConfig, Library};
use clap::{App, Arg, SubCommand};
use glob::glob;
Expand Down Expand Up @@ -68,7 +69,7 @@ trait CustomLibrary {
fn song_paths(&self) -> Result<Vec<String>>;
}

impl CustomLibrary for Library<Config> {
impl CustomLibrary for Library<Config, Decoder> {
/// Get all songs in the player library
fn song_paths(&self) -> Result<Vec<String>> {
let music_path = &self.config.music_library_path;
Expand Down Expand Up @@ -180,7 +181,7 @@ fn main() -> Result<()> {
library.analyze_paths(library.song_paths()?, true)?;
} else if let Some(sub_m) = matches.subcommand_matches("update") {
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
let mut library: Library<Config> = Library::from_config_path(config_path)?;
let mut library: Library<Config, Decoder> = Library::from_config_path(config_path)?;
library.update_library(library.song_paths()?, true, true)?;
} else if let Some(sub_m) = matches.subcommand_matches("playlist") {
let song_path = sub_m.value_of("SONG_PATH").unwrap();
Expand All @@ -189,7 +190,7 @@ fn main() -> Result<()> {
.value_of("playlist-length")
.unwrap_or("20")
.parse::<usize>()?;
let library: Library<Config> = Library::from_config_path(config_path)?;
let library: Library<Config, Decoder> = Library::from_config_path(config_path)?;
let songs = library.playlist_from::<()>(&[song_path], playlist_length)?;
let song_paths = songs
.into_iter()
Expand Down
7 changes: 4 additions & 3 deletions examples/library_extra_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/// For simplicity's sake, this example recursively gets songs from a folder
/// to emulate an audio player library, without handling CUE files.
use anyhow::Result;
use bliss_audio::decoder::ffmpeg::FFmpeg as Decoder;
use bliss_audio::library::{AppConfigTrait, BaseConfig, Library};
use clap::{App, Arg, SubCommand};
use glob::glob;
Expand Down Expand Up @@ -69,7 +70,7 @@ trait CustomLibrary {
fn song_paths_info(&self) -> Result<Vec<(String, ExtraInfo)>>;
}

impl CustomLibrary for Library<Config> {
impl CustomLibrary for Library<Config, Decoder> {
/// Get all songs in the player library, along with the extra info
/// one would want to store along with each song.
fn song_paths_info(&self) -> Result<Vec<(String, ExtraInfo)>> {
Expand Down Expand Up @@ -198,7 +199,7 @@ fn main() -> Result<()> {
library.analyze_paths_extra_info(library.song_paths_info()?, true)?;
} else if let Some(sub_m) = matches.subcommand_matches("update") {
let config_path = sub_m.value_of("config-path").map(PathBuf::from);
let mut library: Library<Config> = Library::from_config_path(config_path)?;
let mut library: Library<Config, Decoder> = Library::from_config_path(config_path)?;
library.update_library_extra_info(library.song_paths_info()?, true, true)?;
} else if let Some(sub_m) = matches.subcommand_matches("playlist") {
let song_path = sub_m.value_of("SONG_PATH").unwrap();
Expand All @@ -207,7 +208,7 @@ fn main() -> Result<()> {
.value_of("playlist-length")
.unwrap_or("20")
.parse::<usize>()?;
let library: Library<Config> = Library::from_config_path(config_path)?;
let library: Library<Config, Decoder> = Library::from_config_path(config_path)?;
let songs = library.playlist_from::<ExtraInfo>(&[song_path], playlist_length)?;
let playlist = songs
.into_iter()
Expand Down
8 changes: 5 additions & 3 deletions examples/playlist.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use anyhow::Result;
use bliss_audio::decoder::ffmpeg::FFmpeg as Decoder;
use bliss_audio::decoder::Decoder as DecoderTrait;
use bliss_audio::playlist::{closest_to_songs, dedup_playlist, euclidean_distance};
use bliss_audio::{analyze_paths, Song};
use bliss_audio::Song;
use clap::{App, Arg};
use glob::glob;
use std::env;
Expand Down Expand Up @@ -56,14 +58,14 @@ fn main() -> Result<()> {
.map(|x| x.to_string_lossy().to_string())
.collect::<Vec<String>>();

let song_iterator = analyze_paths(
let song_iterator = Decoder::analyze_paths(
paths
.iter()
.filter(|p| !analyzed_paths.contains(&PathBuf::from(p)))
.map(|p| p.to_owned())
.collect::<Vec<String>>(),
);
let first_song = Song::from_path(file)?;
let first_song = Decoder::song_from_path(file)?;
let mut analyzed_songs = vec![first_song.to_owned()];
for (path, result) in song_iterator {
match result {
Expand Down
Loading
Loading