From f903d98cd7ab33a5457378fe216463cc7382e215 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 16 Dec 2024 07:54:08 +0100 Subject: [PATCH 1/5] nix: switch to flake --- .envrc | 2 +- flake.lock | 27 +++++++++++++++++++++++++++ flake.nix | 34 ++++++++++++++++++++++++++++++++++ shell.nix | 17 ----------------- 4 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix delete mode 100644 shell.nix diff --git a/.envrc b/.envrc index 1d953f4..3550a30 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -use nix +use flake diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..0b27cdc --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1734083684, + "narHash": "sha256-5fNndbndxSx5d+C/D0p/VF32xDiJCJzyOqorOYW4JEo=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "314e12ba369ccdb9b352a4db26ff419f7c49fa84", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..265c47a --- /dev/null +++ b/flake.nix @@ -0,0 +1,34 @@ +{ + description = "spectrum-analyzer Rust crate"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; + }; + + outputs = + { self, nixpkgs }@inputs: + { + devShells.x86_64-linux.default = + let + pkgs = inputs.nixpkgs.legacyPackages.x86_64-linux; + + # Mainly runtime deps of the examples, not the base lib. + runtimeDeps = with pkgs; [ + alsa-lib + fontconfig + libxkbcommon + xorg.libXcursor + xorg.libX11 + ]; + in + pkgs.mkShell { + packages = + with pkgs; + [ + pkg-config + ] + ++ runtimeDeps; + LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath runtimeDeps}"; + }; + }; +} diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 59bf4be..0000000 --- a/shell.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ pkgs ? import {} }: - pkgs.mkShell rec { - nativeBuildInputs = with pkgs; [ - pkg-config - cargo-nextest - ]; - - buildInputs = with pkgs; [ - alsa-lib - fontconfig - libxkbcommon - xorg.libXcursor - xorg.libX11 - ]; - - LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath buildInputs}"; -} From d953e264aec25ce071010daedfeb50d26e462a4b Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 16 Dec 2024 08:22:05 +0100 Subject: [PATCH 2/5] cargo/ci: update deps, better CI MSRV test, prepare v1.5.1 --- .github/dependabot.yml | 4 ++-- .github/workflows/rust.yml | 24 ++++++++++++++++++++--- CHANGELOG.md | 8 ++++++-- Cargo.toml | 39 ++++++++++++++++++-------------------- README.md | 6 ++++++ check-build.sh | 22 --------------------- 6 files changed, 53 insertions(+), 50 deletions(-) delete mode 100755 check-build.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d02eba7..9ab178d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,7 @@ updates: - package-ecosystem: cargo directory: "/" schedule: - interval: daily + interval: monthly open-pull-requests-limit: 10 ignore: - dependency-name: "*" @@ -11,5 +11,5 @@ updates: - package-ecosystem: github-actions directory: "/" schedule: - interval: daily + interval: monthly open-pull-requests-limit: 10 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a44cb90..a5b0d5e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -7,14 +7,31 @@ env: CARGO_TERM_COLOR: always jobs: - build: + build_library_msrv: + strategy: + matrix: + runs-on: + - macos-latest + - ubuntu-latest + - windows-latest + runs-on: ${{ matrix.runs-on }} + steps: + - uses: actions/checkout@v4 + - name: Setup Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: 1.63.0 # MSRV + - run: cargo build + - run: rustup target add thumbv7em-none-eabihf + - run: cargo build --target thumbv7em-none-eabihf + + build_all_targets: runs-on: ubuntu-latest strategy: matrix: rust: - stable - nightly - - 1.63.0 # MSRV steps: - uses: actions/checkout@v4 - name: Setup Rust toolchain @@ -41,7 +58,8 @@ jobs: strategy: matrix: rust: - - 1.63.0 # MSRV + - stable + - nightly steps: - uses: actions/checkout@v4 - name: Setup Rust toolchain diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dee405..5c457f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,16 @@ # Changelog -# 1.5.0 (2023-09-21) +## 1.5.1 (2024-12-16) +- dependency updates +- MSRV bump but only for the tests and examples, not library users + +## 1.5.0 (2023-09-21) - fixed the build by updating the dependencies - apart from that, no changes happened - **BREAKING** MSRV is now `1.63.0` - internal code improvements -# 1.4.0 (2023-03-04) +## 1.4.0 (2023-03-04) - dropped all optional FFT features (`microfft-complex`, `microfft-real`, `rustfft-complex`) and made `microfft::real` the default FFT implementation. This is breaking but only for a small percentage of users. There was no diff --git a/Cargo.toml b/Cargo.toml index 1b366b4..f84a1b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,12 @@ description = """ An easy to use and fast `no_std` library (with `alloc`) to get the frequency spectrum of a digital signal (e.g. audio) using FFT. """ -version = "1.5.0" +version = "1.5.1" authors = ["Philipp Schuster "] edition = "2021" keywords = ["fft", "spectrum", "frequencies", "audio", "dsp"] categories = ["multimedia", "no-std"] +rust-version = "1.63" # MSRV of base library, not examples and benchmarks readme = "README.md" license = "MIT" homepage = "https://github.com/phip1611/spectrum-analyzer" @@ -25,29 +26,25 @@ name = "fft_spectrum_bench" harness = false [dependencies] -microfft = { version = "0.5.1", features = ["size-16384"] } -# approx. compare floats; not only in tests but also during runtime -float-cmp = "0.9.0" -# sin() cos() log10() etc for no_std-environments; these are not part of Core library -libm = "0.2.7" -paste = "1.0.14" +float-cmp = "~0.10.0" +libm = "~0.2.7" +microfft = { version = "~0.5.1", features = ["size-16384"] } +paste = "~1.0.14" [dev-dependencies] -# readmp3 files in tests and examples -minimp3 = "0.5.1" -# visualize spectrum in tests and examples -audio-visualizer = "0.4.0" -# get audio input in examples -cpal = "0.15.2" -# audio data buffering -ringbuffer = "0.15.0" -rand = "0.8.5" # for benchmark -# exit in examples -ctrlc = "~3.3.1" # locked because of repo MSRV -# for benchmark -criterion = "~0.4.0" # locked because of repo MSRV +audio-visualizer = "~0.4.0" +minimp3 = "~0.5.1" +# Additional dependencies for: examples +cpal = "~0.15.2" +ctrlc = "~3.4.0" +ringbuffer = "~0.15.0" -# otherwise FFT and other code is too slow +# Additional dependencies for: benchmarks +criterion = "~0.5.0" +rand = "0.8.5" + + +# Faster code in tests, otherwise FFT is too slow [profile.dev] opt-level = 1 diff --git a/README.md b/README.md index f946694..ff3a2d7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ spectrum of a digital signal (e.g. audio) using FFT. The **MSRV** (minimum supported Rust version) is `1.63.0`. +## Supported Platforms + +The base library supports all standard and non-standard targets, such as +machines running Linux, Ubuntu, Windows, but also embedded systems running +custom software. + ## I want to understand how FFT can be used to get a spectrum Please see file [/EDUCATIONAL.md](/EDUCATIONAL.md). diff --git a/check-build.sh b/check-build.sh deleted file mode 100755 index 9435bbc..0000000 --- a/check-build.sh +++ /dev/null @@ -1,22 +0,0 @@ -set -e -set -x - -echo "checks that this builds on std+no_std + that all tests run + that all features compile" -cargo build --all-targets - -cargo test --all-targets - -cargo bench - -cargo fmt -- --check # (--check doesn't change the files) - -cargo doc --document-private-items - -cargo clippy --all-targets - -# test no_std -rustup target add thumbv7em-none-eabihf -cargo build --target thumbv7em-none-eabihf - -# run examples -cargo run --release --example mp3-samples From b9c4b9f1849ba5140c74cf0d4eb56e0634746c8d Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 16 Dec 2024 08:30:10 +0100 Subject: [PATCH 3/5] ci: use cache --- .github/workflows/rust.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a5b0d5e..a9b9702 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -21,6 +21,7 @@ jobs: uses: dtolnay/rust-toolchain@stable with: toolchain: 1.63.0 # MSRV + - uses: Swatinem/rust-cache@v2 - run: cargo build - run: rustup target add thumbv7em-none-eabihf - run: cargo build --target thumbv7em-none-eabihf @@ -39,6 +40,7 @@ jobs: with: toolchain: ${{ matrix.rust }} components: clippy, rustfmt + - uses: Swatinem/rust-cache@v2 - name: Install required Linux packages for "audio-visualizer"/cpal/minifb run: sudo apt update && sudo apt -y install libasound2-dev libxkbcommon-dev @@ -67,6 +69,7 @@ jobs: with: toolchain: ${{ matrix.rust }} components: clippy, rustfmt + - uses: Swatinem/rust-cache@v2 - name: Install required Linux packages for "audio-visualizer"/cpal/minifb run: sudo apt update && sudo apt -y install libasound2-dev libxkbcommon-dev - name: Rustfmt From 73904b768b2c0837473a75314da4f7b0d7394de4 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 16 Dec 2024 08:39:16 +0100 Subject: [PATCH 4/5] style: various clippy and rustdoc fixes --- src/frequency.rs | 17 ++++++------- src/lib.rs | 14 +++++------ src/limit.rs | 8 +++--- src/scaling.rs | 65 +++++++++++++++++++++++++++++++----------------- src/spectrum.rs | 5 ++-- 5 files changed, 65 insertions(+), 44 deletions(-) diff --git a/src/frequency.rs b/src/frequency.rs index b1ff06b..66685d9 100644 --- a/src/frequency.rs +++ b/src/frequency.rs @@ -64,7 +64,13 @@ impl Display for OrderableF32 { impl Ord for OrderableF32 { #[inline] fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other).unwrap() + if self.val() < other.val() { + Ordering::Less + } else if self.val() == other.val() { + Ordering::Equal + } else { + Ordering::Greater + } } } @@ -81,14 +87,7 @@ impl PartialOrd for OrderableF32 { #[allow(clippy::float_cmp)] #[inline] fn partial_cmp(&self, other: &Self) -> Option { - // self.cmp(other).is_eq() - Some(if self.val() < other.val() { - Ordering::Less - } else if self.val() == other.val() { - Ordering::Equal - } else { - Ordering::Greater - }) + Some(self.cmp(other)) } } diff --git a/src/lib.rs b/src/lib.rs index 0e66d29..b50cc11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,14 +115,14 @@ mod tests; /// to their magnitude. /// /// * `samples` raw audio, e.g. 16bit audio data but as f32. -/// You should apply an window function (like Hann) on the data first. +/// You should apply a window function (like Hann) on the data first. /// The final frequency resolution is `sample_rate / (N / 2)` /// e.g. `44100/(16384/2) == 5.383Hz`, i.e. more samples => /// better accuracy/frequency resolution. The amount of samples must /// be a power of 2. If you don't have enough data, provide zeroes. -/// * `sampling_rate` sampling_rate, e.g. `44100 [Hz]` -/// * `frequency_limit` Frequency limit. See [`FrequencyLimit´] -/// * `scaling_fn` See [`crate::scaling::SpectrumScalingFunction`] for details. +/// * `sampling_rate` The used sampling_rate, e.g. `44100 [Hz]`. +/// * `frequency_limit` The [`FrequencyLimit`]. +/// * `scaling_fn` See [`SpectrumScalingFunction`] for details. /// /// ## Returns value /// New object of type [`FrequencySpectrum`]. @@ -219,9 +219,9 @@ pub fn samples_fft_to_spectrum( /// derived from `fft_result.len()`. There are for example differences for /// `fft_result.len()` in real and complex FFT algorithms. /// * `fft_result` Result buffer from FFT. Has the same length as the samples array. -/// * `sampling_rate` sampling_rate, e.g. `44100 [Hz]` -/// * `frequency_limit` Frequency limit. See [`FrequencyLimit´] -/// * `scaling_fn` See [`crate::scaling::SpectrumScalingFunction`]. +/// * `sampling_rate` The used sampling_rate, e.g. `44100 [Hz]`. +/// * `frequency_limit` The [`FrequencyLimit`]. +/// * `scaling_fn` See [`SpectrumScalingFunction`] for details. /// /// ## Return value /// New object of type [`FrequencySpectrum`]. diff --git a/src/limit.rs b/src/limit.rs index 3aafbed..bd33684 100644 --- a/src/limit.rs +++ b/src/limit.rs @@ -23,9 +23,11 @@ SOFTWARE. */ //! Module for the struct [`FrequencyLimit`]. -/// Can be used to specify a desired frequency limit. If you know that you only -/// need frequencies `f <= 1000Hz`, `1000 <= f <= 6777`, or `10000 <= f`, then this -/// can help you to accelerate overall computation speed and memory usage. +/// Can be used to specify a desired frequency limit. +/// +/// If you know that you only need frequencies `f <= 1000Hz`, +/// `1000 <= f <= 6777`, or `10000 <= f`, then this can help you to accelerate +/// overall computation speed and memory usage. /// /// Please note that due to frequency inaccuracies the FFT result may not contain /// a value for `1000Hz` but for `998.76Hz`! diff --git a/src/scaling.rs b/src/scaling.rs index 0267d5c..541fcf1 100644 --- a/src/scaling.rs +++ b/src/scaling.rs @@ -22,19 +22,26 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //! This module contains convenient public transform functions that you can use -//! as parameters in [`crate::samples_fft_to_spectrum`] for scaling the -//! frequency value (the FFT result). They act as "idea/inspiration". Feel free -//! to either compose them or create your own derivation from them. - +//! as parameters in [`samples_fft_to_spectrum`] for scaling the frequency value +//! (the FFT result). +//! +//! They act as "idea/inspiration". Feel free to either compose them or create +//! your own derivation from them. +//! +//! [`samples_fft_to_spectrum`]: crate::samples_fft_to_spectrum use alloc::boxed::Box; /// Helper struct for [`SpectrumScalingFunction`] that is passed into the -/// scaling function together with the current frequency value. This structure -/// can be used to scale each value. All properties reference the current data -/// of a [`crate::spectrum::FrequencySpectrum`]. +/// scaling function together with the current frequency value. +/// +/// This structure can be used to scale each value. All properties reference the +/// current data of a [`FrequencySpectrum`]. /// -/// This uses `f32` in favor of [`crate::FrequencyValue`] because the latter led to +/// This uses `f32` in favor of [`FrequencyValue`] because the latter led to /// some implementation problems. +/// +/// [`FrequencySpectrum`]: crate::FrequencySpectrum +/// [`FrequencyValue`]: crate::FrequencyValue #[derive(Debug)] pub struct SpectrumDataStats { /// Minimal frequency value in spectrum. @@ -50,25 +57,35 @@ pub struct SpectrumDataStats { pub n: f32, } -/// Describes the type for a function that scales/normalizes the data inside [`crate::FrequencySpectrum`]. -/// The scaling only affects the value/amplitude of the frequency, but not the frequency itself. -/// It is applied to every single element. +/// Describes the type for a function that scales/normalizes the data inside +/// [`FrequencySpectrum`]. +/// +/// The scaling only affects the value/amplitude of the frequency, but not the +/// frequency itself. It is applied to every single element. /// -/// A scaling function can be used for example to subtract the minimum (`min`) from each value. -/// It is optional to use the second parameter [`SpectrumDataStats`]. -/// and the type works with static functions as well as dynamically created closures. +/// A scaling function can be used for example to subtract the minimum (`min`) +/// from each value. It is optional to use the second parameter +/// [`SpectrumDataStats`]. /// -/// You must take care of, that you don't have division by zero in your function or -/// that the result is NaN or Infinity (regarding IEEE-754). If the result is NaN or Infinity, -/// the library will return `Err`. +/// The type works with static functions as well as dynamically created +/// closures. /// -/// This uses `f32` in favor of [`crate::FrequencyValue`] because the latter led to +/// You must take care of, that you don't have division by zero in your function +/// or that the result is NaN or Infinity (regarding IEEE-754). If the result +/// is NaN or Infinity, the library will return `Err`. +/// +/// This uses `f32` in favor of [`FrequencyValue`] because the latter led to /// some implementation problems. +/// +/// [`FrequencySpectrum`]: crate::FrequencySpectrum +/// [`FrequencyValue`]: crate::FrequencyValue pub type SpectrumScalingFunction = dyn Fn(f32, &SpectrumDataStats) -> f32; /// Calculates the base 10 logarithm of each frequency magnitude and -/// multiplies it with 20. This scaling is quite common, you can -/// find more information for example here: +/// multiplies it with 20. +/// +/// This scaling is quite common, you can find more information for example +/// here: /// /// /// ## Usage @@ -125,8 +142,10 @@ pub fn divide_by_N(fr_val: f32, stats: &SpectrumDataStats) -> f32 { } } -/// Like [`divide_by_N`] but divides each value by `sqrt(N)`. This is the recommended scaling -/// in the `rustfft` documentation (but is generally applicable). +/// Like [`divide_by_N`] but divides each value by `sqrt(N)`. +/// +/// This is the recommended scaling in the `rustfft` documentation (but is +/// generally applicable). /// See #[allow(non_snake_case)] #[must_use] @@ -183,7 +202,7 @@ mod tests { .into_iter() .map(|x| scaling_fn(x, &stats)) .collect::>(); - let expected = vec![0.0_f32, 0.2, 0.4, 0.6, 0.8, 1.0]; + let expected = [0.0_f32, 0.2, 0.4, 0.6, 0.8, 1.0]; for (expected_val, actual_val) in expected.iter().zip(scaled_data.iter()) { float_cmp::approx_eq!(f32, *expected_val, *actual_val, ulps = 3); } diff --git a/src/spectrum.rs b/src/spectrum.rs index 7b1a76f..c40997d 100644 --- a/src/spectrum.rs +++ b/src/spectrum.rs @@ -31,8 +31,9 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; /// Convenient wrapper around the processed FFT result which describes each -/// frequency and its value/amplitude from the analyzed samples. It only -/// contains the frequencies that were desired, e.g., specified via +/// frequency and its value/amplitude from the analyzed samples. +/// +/// It only contains the frequencies that were desired, e.g., specified via /// [`crate::limit::FrequencyLimit`] when [`crate::samples_fft_to_spectrum`] /// was called. /// From 5015895335123fc5487b8ac4357d9fa206b10dfa Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 16 Dec 2024 08:50:08 +0100 Subject: [PATCH 5/5] cargo: switch to 1.6.0 as next version already This aligns with a feature added shortly on upstream in an open PR. --- CHANGELOG.md | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c457f5..4b933f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 1.5.1 (2024-12-16) +## 1.6.0 (2024-12-16) - dependency updates - MSRV bump but only for the tests and examples, not library users diff --git a/Cargo.toml b/Cargo.toml index f84a1b3..ca8c5fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ description = """ An easy to use and fast `no_std` library (with `alloc`) to get the frequency spectrum of a digital signal (e.g. audio) using FFT. """ -version = "1.5.1" +version = "1.6.0" authors = ["Philipp Schuster "] edition = "2021" keywords = ["fft", "spectrum", "frequencies", "audio", "dsp"]