From ab5aa966f7b57f57aeda268cdee91ab75290b395 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Sun, 26 May 2024 01:33:14 +0300 Subject: [PATCH 01/13] Initial stft implementation --- Project.toml | 4 ++- src/NNlib.jl | 22 +++++++++++++++ src/audio/stft.jl | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/audio/stft.jl diff --git a/Project.toml b/Project.toml index 0496564e0..30e4a33e0 100644 --- a/Project.toml +++ b/Project.toml @@ -6,12 +6,14 @@ version = "0.9.17" Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" Atomix = "a9b6321e-bd34-4604-b9c9-b65b8de01458" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Requires = "ae029012-a4dd-5104-9daa-d747884805df" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [weakdeps] @@ -52,9 +54,9 @@ Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" EnzymeTestUtils = "12d8515a-0907-448a-8884-5fe00fdf1c5a" FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" +ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" ImageTransformations = "02fcd773-0e25-5acc-982a-7f6622650795" Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" -ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" diff --git a/src/NNlib.jl b/src/NNlib.jl index 7bf7bd172..870571c6b 100644 --- a/src/NNlib.jl +++ b/src/NNlib.jl @@ -17,6 +17,7 @@ using Random using Requires using Statistics using Statistics: mean +using FFTW const libblas = Base.libblas_name @@ -126,4 +127,25 @@ include("deprecations.jl") include("rotation.jl") export imrotate, ∇imrotate +include("audio/stft.jl") + +function main() + w = hann_window(10) + @show w + w = hann_window(10, Float32; periodic=false) + @show w + + w = hamming_window(10) + @show w + w = hamming_window(10, Float32; periodic=false) + @show w + + println() + + x = Float32.(collect(0:16)) + x = ones(Float32, 16) + stft(x; n_fft=16, center=true) + return +end + end # module NNlib diff --git a/src/audio/stft.jl b/src/audio/stft.jl new file mode 100644 index 000000000..c76524d21 --- /dev/null +++ b/src/audio/stft.jl @@ -0,0 +1,72 @@ +function hann_window( + window_length::Integer, ::Type{T} = Float32; periodic::Bool = true, +) where T <: Real + window_length < 1 && throw(ArgumentError( + "`window_length` must be > 0, instead: `$window_length`.")) + + n::T = ifelse(periodic, window_length, window_length - 1) + scale = T(2) * π / n + [T(0.5) * (T(1) - cos(scale * T(k))) for k in 0:(window_length - 1)] +end + +function hamming_window( + window_length::Integer, ::Type{T} = Float32; periodic::Bool = true, + α::T = T(0.54), β::T = T(0.46), +) where T <: Real + window_length < 1 && throw(ArgumentError( + "`window_length` must be > 0, instead: `$window_length`.")) + + n::T = ifelse(periodic, window_length, window_length - 1) + scale = T(2) * π / n + [α - β * cos(scale * T(k)) for k in 0:(window_length - 1)] +end + +function stft(x::AbstractArray{T}; + n_fft::Int, hop_length::Int = n_fft ÷ 4, window = hann_window(n_fft, T), + center::Bool = true, normalized::Bool = false, +) where T + # TODO: + # - check args are valid + # - for now input is only 1D time sequence + # - support 2D batch of time sequences + # - if window < n_fft - pad on both sides before applying + + kab = get_backend(x) + _window = adapt(kab, window) + + if center + pad_amount = n_fft ÷ 2 + x = pad_reflect(x, pad_amount; dims=1) + end + + if length(_window) < n_fft + left = ((n_fft - length(_window)) ÷ 2) + 1 + tmp = KernelAbstractions.zeros(kab, eltype(_window), n_fft) + tmp[left:left + length(_window) - 1] .= _window + _window = tmp + end + + n = size(x, 1) + n_frames = 1 + (n - n_fft) ÷ hop_length + + if n_frames > 1 + # TODO if we support something like torch.as_strided we can reduce memory + ids = [ + row + hop_length * col + for row in 1:n_fft, col in 0:(n_frames - 1)] + x = x[ids] + end + display(x); println() + + # TODO dispatch for GPU/CPU implementations + x .*= window + region = 1 + y = rfft(x, region) + # TODO if not onesided, use `fft` instead of `rfft` + + @show size(x) + @show size(y) + @show typeof(y) + display(y); println() + return +end From fb1bacaf55a070e48960400d7bb355328fd6827e Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Mon, 27 May 2024 00:39:33 +0300 Subject: [PATCH 02/13] Finish STFT --- Project.toml | 2 +- docs/src/reference.md | 9 + src/NNlib.jl | 20 +- src/audio/stft.jl | 328 ++++++++++++++++++++++++++++--- test/runtests.jl | 16 +- test/{ => testsuite}/gather.jl | 0 test/{ => testsuite}/rotation.jl | 0 test/{ => testsuite}/scatter.jl | 0 test/testsuite/spectral.jl | 88 +++++++++ test/{ => testsuite}/upsample.jl | 0 10 files changed, 405 insertions(+), 58 deletions(-) rename test/{ => testsuite}/gather.jl (100%) rename test/{ => testsuite}/rotation.jl (100%) rename test/{ => testsuite}/scatter.jl (100%) create mode 100644 test/testsuite/spectral.jl rename test/{ => testsuite}/upsample.jl (100%) diff --git a/Project.toml b/Project.toml index 30e4a33e0..4aeae01bb 100644 --- a/Project.toml +++ b/Project.toml @@ -13,8 +13,8 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Requires = "ae029012-a4dd-5104-9daa-d747884805df" -Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" [weakdeps] AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" diff --git a/docs/src/reference.md b/docs/src/reference.md index 4f90db762..3eac061aa 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -163,3 +163,12 @@ NNlib.glu NNlib.within_gradient bias_act! ``` + +## Spectral + +```@docs +hann_window +hamming_window +stft +istft +``` diff --git a/src/NNlib.jl b/src/NNlib.jl index 870571c6b..4b9f3d0c5 100644 --- a/src/NNlib.jl +++ b/src/NNlib.jl @@ -128,24 +128,6 @@ include("rotation.jl") export imrotate, ∇imrotate include("audio/stft.jl") - -function main() - w = hann_window(10) - @show w - w = hann_window(10, Float32; periodic=false) - @show w - - w = hamming_window(10) - @show w - w = hamming_window(10, Float32; periodic=false) - @show w - - println() - - x = Float32.(collect(0:16)) - x = ones(Float32, 16) - stft(x; n_fft=16, center=true) - return -end +export stft, istft, hann_window, hamming_window end # module NNlib diff --git a/src/audio/stft.jl b/src/audio/stft.jl index c76524d21..b7447033d 100644 --- a/src/audio/stft.jl +++ b/src/audio/stft.jl @@ -1,5 +1,58 @@ +""" + hann_window( + window_length::Int, ::Type{T} = Float32; periodic::Bool = true, + ) where T <: Real + +Hann window function. + +``w[n] = \\frac{1}{2}[1 - cos(\\frac{2 \\pi n}{N - 1})]`` + +Where ``N`` is the window length. + +```julia +julia> lineplot(hann_window(100)) + ┌────────────────────────────────────────┐ + 1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠎⠉⠉⠉⠣⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⡰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⡠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⡰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀│ + │⠀⠀⠀⠀⡔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢆⠀⠀⠀│ + 0 │⣀⣀⡔⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢆⣀│ + └────────────────────────────────────────┘ + ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀100⠀ +``` + +# Arguments: + +- `window_length::Int`: Size of the window. +- `::Type{T}`: Elemet type of the window. Default is `Float32`. + +# Keyword Arguments: + +- `periodic::Bool`: If `true` (default), returns a window to be used as + periodic function. If `false`, return a symmetric window. + + Following always holds: + +```julia +hann_window(N; periodic=true) ≈ hann_window(N + 1; periodic=false)[1:end - 1] +``` + +# Returns: + +Vector of length `window_length` and eltype `T`. +""" function hann_window( - window_length::Integer, ::Type{T} = Float32; periodic::Bool = true, + window_length::Int, ::Type{T} = Float32; periodic::Bool = true, ) where T <: Real window_length < 1 && throw(ArgumentError( "`window_length` must be > 0, instead: `$window_length`.")) @@ -9,8 +62,64 @@ function hann_window( [T(0.5) * (T(1) - cos(scale * T(k))) for k in 0:(window_length - 1)] end +""" + hamming_window( + window_length::Int, ::Type{T} = Float32; periodic::Bool = true, + α::T = T(0.54), β::T = T(0.46), + ) where T <: Real + +Hamming window function. Generalized version of `hann_window`. + +``w[n] = \\alpha - \\beta cos(\\frac{2 \\pi n}{N - 1})`` + +Where ``N`` is the window length. + +```julia +julia> lineplot(hamming_window(100)) + ┌────────────────────────────────────────┐ + 1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠉⠉⠉⠓⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠎⠀⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⡄⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⣠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⡠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⡄⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⡰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀│ + │⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠣⡀⠀⠀│ + │⣀⠤⠖⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⠤│ + 0 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + └────────────────────────────────────────┘ + ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀100⠀ +``` + +# Arguments: + +- `window_length::Int`: Size of the window. +- `::Type{T}`: Elemet type of the window. Default is `Float32`. + +# Keyword Arguments: + +- `periodic::Bool`: If `true` (default), returns a window to be used as + periodic function. If `false`, return a symmetric window. + + Following always holds: + +```julia +hamming_window(N; periodic=true) ≈ hamming_window(N + 1; periodic=false)[1:end - 1] +``` +- `α::Real`: Coefficient α in the equation above. +- `β::Real`: Coefficient β in the equation above. + +# Returns: + +Vector of length `window_length` and eltype `T`. +""" function hamming_window( - window_length::Integer, ::Type{T} = Float32; periodic::Bool = true, + window_length::Int, ::Type{T} = Float32; periodic::Bool = true, α::T = T(0.54), β::T = T(0.46), ) where T <: Real window_length < 1 && throw(ArgumentError( @@ -21,52 +130,207 @@ function hamming_window( [α - β * cos(scale * T(k)) for k in 0:(window_length - 1)] end -function stft(x::AbstractArray{T}; - n_fft::Int, hop_length::Int = n_fft ÷ 4, window = hann_window(n_fft, T), - center::Bool = true, normalized::Bool = false, -) where T - # TODO: - # - check args are valid - # - for now input is only 1D time sequence - # - support 2D batch of time sequences - # - if window < n_fft - pad on both sides before applying +""" + stft(x; + n_fft::Int, hop_length::Int = n_fft ÷ 4, window = nothing, + center::Bool = true, normalized::Bool = false, + ) + +Short-time Fourier transform (STFT). + +The STFT computes the Fourier transform of short overlapping windows of the input, +giving frequency components of the signal as they change over time. + +``Y[\\omega, m] = \\sum_{k = 0}^{N - 1} \\text{window}[k] \\text{input}[m \\times \\text{hop length} + k] exp(-j \\frac{2 \\pi \\omega k}{\\text{n fft}})`` + +where ``N`` is the window length, +``\\omega`` is the frequency ``0 \\le \\omega < \\text{n fft}`` +and ``m`` is the index of the sliding window. + +# Arguments: + +- `x`: Input, must be either a 1D time sequence (`(L,)` shape) + or a 2D batch of time sequence (`(L, B)` shape). + +# Positional Arguments: + +- `n_fft::Int`: Size of Fourier transform. +- `hop_length::Int`: Distance between neighboring sliding window frames. + Default is `n_fft ÷ 4`. +- `window`: Optional window function to apply. + Must be 1D vector `0 < length(window) ≤ n_fft`. + If window is shorter than `n_fft`, it is padded with zeros on both sides. + If `nothing` (default), then no window is applied. +- `center::Bool`: Whether to pad input on both sides so that ``t``-th frame + is centered at time ``t \\times \\text{hop length}``. + Default is `true`. Padding is done with `pad_reflect` function. +- `normalized::Bool`: Whether to return normalized STFT, + i.e. multiplied with ``\\text{n fft}^{-0.5}``. + +# Returns: +Complex array of shape `(n_fft, n_frames, B)`, +where `B` is the optional batch dimension. +""" +function stft(x; + n_fft::Int, hop_length::Int = n_fft ÷ 4, window = nothing, + center::Bool = true, normalized::Bool = false, +) kab = get_backend(x) - _window = adapt(kab, window) + use_window = !isnothing(window) + + use_window && kab != get_backend(window) && throw(ArgumentError( + "`window` must be on the same device as stft input `x` ($kab), \ + instead: `$(get_backend(window))`.")) + use_window && !(0 < length(window) ≤ n_fft) && throw(ArgumentError( + "Expected `0 < length(window) ≤ n_fft=$n_fft`, \ + but got `length(window)=$(length(window))`.")) + hop_length < 0 && throw(ArgumentError( + "Expected `hop_length > 0`, but got `hop_length=$hop_length`.")) + + # Pad window on both sides with `0` to `n_fft` length if needed. + if use_window && length(window) < n_fft + left = ((n_fft - length(window)) ÷ 2) + 1 + tmp = KernelAbstractions.zeros(kab, eltype(window), n_fft) + tmp[left:left + length(window) - 1] .= window + window = tmp + end if center pad_amount = n_fft ÷ 2 x = pad_reflect(x, pad_amount; dims=1) end - if length(_window) < n_fft - left = ((n_fft - length(_window)) ÷ 2) + 1 - tmp = KernelAbstractions.zeros(kab, eltype(_window), n_fft) - tmp[left:left + length(_window) - 1] .= _window - _window = tmp - end - n = size(x, 1) + (0 < n_fft ≤ n) || throw(ArgumentError( + "Expected `0 < n_fft ≤ size(x, 1)=$n`, but got `n_fft=$n_fft`.")) + n_frames = 1 + (n - n_fft) ÷ hop_length + # time2col. + # Reshape `x` to (n_fft, n_frames, B) if needed. + # Each row in `n_frames` is shifted by `hop_length`. if n_frames > 1 - # TODO if we support something like torch.as_strided we can reduce memory + # TODO can be more efficient if we support something like torch.as_strided ids = [ row + hop_length * col for row in 1:n_fft, col in 0:(n_frames - 1)] - x = x[ids] + x = x[ids, ntuple(_ -> Colon(), ndims(x) - 1)...] + end + + region = 1 + use_window && (x = x .* window;) + y = eltype(x) <: Complex ? fft(x, region) : rfft(x, region) + + normalized && (y .*= n_fft^-0.5;) + return y +end + +""" + istft(y; + n_fft::Int, hop_length::Int = n_fft ÷ 4, window = nothing, + center::Bool = true, normalized::Bool = false, + return_complex::Bool = false, + original_length::Union{Nothing, Int} = nothing, + ) + +Inverse Short-time Fourier Transform. + +Return the least squares estimation of the original signal + +# Arguments: + +- `y`: Input complex array in the `(n_fft, n_frames, B)` shape. + Where `B` is the optional batch dimension. + +# Positional Arguments: + +- `n_fft::Int`: Size of Fourier transform. +- `hop_length::Int`: Distance between neighboring sliding window frames. + Default is `n_fft ÷ 4`. +- `window`: Window function that was applied to the input of `stft`. + If `nothing` (default), then no window was applied. +- `center::Bool`: Whether input to `stft` was padded on both sides + so that ``t``-th frame is centered at time ``t \\times \\text{hop length}``. + Default is `true`. Padding is done with `pad_reflect` function. +- `normalized::Bool`: Whether input to `stft` was normalized. +- `return_complex::Bool`: Whether the output should be complex, + or if the input should be assumed to derive from a real signal and window. + Default `false`. +- `original_length::Union{Nothing, Int}`: Optional size of the first dimension + of the input to `stft`. Helps restoring the exact `stft` input size. + Otherwise, the array might be a bit shorter. +""" +function istft(y; + n_fft::Int, hop_length::Int = n_fft ÷ 4, window = nothing, + center::Bool = true, normalized::Bool = false, + return_complex::Bool = false, + original_length::Union{Nothing, Int} = nothing, +) + kab = get_backend(y) + use_window = !isnothing(window) + + use_window && kab != get_backend(window) && throw(ArgumentError( + "`window` must be on the same device as istft input `y` ($kab), \ + instead: `$(get_backend(window))`.")) + use_window && !(0 < length(window) ≤ n_fft) && throw(ArgumentError( + "Expected `0 < length(window) ≤ n_fft=$n_fft`, \ + but got `length(window)=$(length(window))`.")) + hop_length < 0 && throw(ArgumentError( + "Expected `hop_length > 0`, but got `hop_length=$hop_length`.")) + + # TODO check `y` eltype is complex + + n_frames = size(y, 2) + + # Pad window on both sides with `0` to `n_fft` length if needed. + if use_window && length(window) < n_fft + left = ((n_fft - length(window)) ÷ 2) + 1 + tmp = KernelAbstractions.zeros(kab, eltype(window), n_fft) + tmp[left:left + length(window) - 1] .= window + window = tmp end - display(x); println() - # TODO dispatch for GPU/CPU implementations - x .*= window + # Denormalize. + normalized && (y = y .* eltype(y)(n_fft^0.5);) + region = 1 - y = rfft(x, region) - # TODO if not onesided, use `fft` instead of `rfft` - - @show size(x) - @show size(y) - @show typeof(y) - display(y); println() - return + x = return_complex ? ifft(y, region) : irfft(y, n_fft, region) + + # De-apply window. + use_window && (x ./= window;) + + # col2time. + expected_output_len = n_fft + hop_length * (n_frames - 1) + + ids = Vector{Int}(undef, expected_output_len) + in_idx, out_idx = 0, 0 + prev_e, v = 0, 0 + + for col in 0:(n_frames - 1) + for row in 1:n_fft + in_idx += 1 + v = row + hop_length * col + v > prev_e || continue + + out_idx += 1 + ids[out_idx] = in_idx + end + prev_e = v + end + + # In case of batched input, reshaped it (n_fft, n_frames, batch) -> (:, batch). + nd = ntuple(_ -> Colon(), ndims(x) - 2) + ndims(x) == 3 && (x = reshape(x, (:, size(x, 3)));) + x = x[ids, nd...] + + # Trim padding. + left = center ? (n_fft ÷ 2 + 1) : 1 + right = if isnothing(original_length) + center ? (size(x, 1) - n_fft ÷ 2) : expected_output_len + else + left + original_length - 1 + end + x = x[left:right, nd...] + return x end diff --git a/test/runtests.jl b/test/runtests.jl index b79a8a011..c9290f89e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -40,10 +40,11 @@ end cpu(x) = adapt(CPU(), x) -include("gather.jl") -include("scatter.jl") -include("upsample.jl") -include("rotation.jl") +include("testsuite/gather.jl") +include("testsuite/scatter.jl") +include("testsuite/upsample.jl") +include("testsuite/rotation.jl") +include("testsuite/spectral.jl") function nnlib_testsuite(Backend; skip_tests = Set{String}()) @conditional_testset "Upsample" skip_tests begin @@ -58,12 +59,15 @@ function nnlib_testsuite(Backend; skip_tests = Set{String}()) @conditional_testset "Scatter" skip_tests begin scatter_testsuite(Backend) end + @conditional_testset "Spectral" skip_tests begin + spectral_testsuite(Backend) + end end @testset verbose=true "NNlib.jl" begin if get(ENV, "NNLIB_TEST_CPU", "true") == "true" - @testset "CPU" begin + @testset "CPU" begin @testset "Doctests" begin doctest(NNlib, manual=false) end @@ -145,7 +149,7 @@ end end else @info "Skipping CUDA tests, set NNLIB_TEST_CUDA=true to run them" - end + end if get(ENV, "NNLIB_TEST_AMDGPU", "false") == "true" using AMDGPU diff --git a/test/gather.jl b/test/testsuite/gather.jl similarity index 100% rename from test/gather.jl rename to test/testsuite/gather.jl diff --git a/test/rotation.jl b/test/testsuite/rotation.jl similarity index 100% rename from test/rotation.jl rename to test/testsuite/rotation.jl diff --git a/test/scatter.jl b/test/testsuite/scatter.jl similarity index 100% rename from test/scatter.jl rename to test/testsuite/scatter.jl diff --git a/test/testsuite/spectral.jl b/test/testsuite/spectral.jl new file mode 100644 index 000000000..b825759af --- /dev/null +++ b/test/testsuite/spectral.jl @@ -0,0 +1,88 @@ +using NNlib + +function spectral_testsuite(Backend) + @testset "Window functions" begin + for window_fn in (hann_window, hamming_window) + @inferred window_fn(10, Float32) + @inferred window_fn(10, Float64) + + w = window_fn(10) + @test length(w) == 10 + @test eltype(w) == Float32 + + wp = window_fn(10; periodic=false) + @test wp[1:5] ≈ reverse(wp[6:10]) + + @test window_fn(10; periodic=true) ≈ window_fn(10 + 1; periodic=false)[1:10] + end + end + + @testset "STFT" begin + cpu(x) = adapt(CPU(), x) + device(x) = adapt(Backend(), x) + + for batch in ((), (3,)) + @testset "Batch $batch" begin + x = device(ones(Float32, 16, batch...)) + # TODO fix type stability for pad_reflect + # @inferred stft(x; n_fft=16) + + bd = ntuple(_ -> Colon(), length(batch)) + + y = stft(x; n_fft=16) + @test size(y) == (9, 5, batch...) + @test all(real(cpu(y))[1, :, bd...] .≈ 16) + + xx = istft(y; n_fft=16) + @test size(xx) == (16, batch...) + @test cpu(x) ≈ cpu(xx) + + # Test multiple hops. + x = device(rand(Float32, 2048, batch...)) + y = stft(x; n_fft=1024) + xx = istft(y; n_fft=1024) + @test cpu(x) ≈ cpu(xx) + + # Test odd sizes. + x = device(rand(Float32, 1111, batch...)) + y = stft(x; n_fft=256) + xx = istft(y; n_fft=256, original_length=size(x, 1)) + @test cpu(x) ≈ cpu(xx) + + # Output from inverse is cropped on the right + # without knowing the original size. + xx = istft(y; n_fft=256) + @test length(xx) < length(x) + @test cpu(x)[[1:s for s in size(xx)]...] ≈ cpu(xx) + + # Test different options. + + # Normalized. + x = device(rand(Float32, 1234, batch...)) + y = stft(x; n_fft=512, normalized=true) + xx = istft(y; n_fft=512, normalized=true, original_length=size(x, 1)) + @test cpu(x) ≈ cpu(xx) + + # With window. + window = device(hann_window(512)) + y = stft(x; n_fft=512, window) + xx = istft(y; n_fft=512, window, original_length=size(x, 1)) + @test cpu(x) ≈ cpu(xx) + + # Hop. + for hop_length in (32, 33, 255, 256, 511, 512) + y = stft(x; n_fft=512, hop_length) + xx = istft(y; n_fft=512, hop_length, original_length=size(x, 1)) + @test cpu(x) ≈ cpu(xx) + end + + # N FFT. + for n_fft in (32, 33, 64, 65, 128, 129, 512) + y = stft(x; n_fft) + xx = istft(y; n_fft, original_length=size(x, 1)) + @test cpu(x) ≈ cpu(xx) + end + end + end + end +end diff --git a/test/upsample.jl b/test/testsuite/upsample.jl similarity index 100% rename from test/upsample.jl rename to test/testsuite/upsample.jl From e3c8df7fd9582e714ca0953e91756bdd48ff5181 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Mon, 27 May 2024 00:41:04 +0300 Subject: [PATCH 03/13] Cleanup --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4aeae01bb..286669cd4 100644 --- a/Project.toml +++ b/Project.toml @@ -14,7 +14,6 @@ Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Requires = "ae029012-a4dd-5104-9daa-d747884805df" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" [weakdeps] AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" From ab6a4bffa23ceee5818de3c35d7f05edcee0b4d3 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Mon, 27 May 2024 00:44:06 +0300 Subject: [PATCH 04/13] Bump AMDGPU compat --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 286669cd4..c732d37e5 100644 --- a/Project.toml +++ b/Project.toml @@ -28,7 +28,7 @@ NNlibCUDAExt = "CUDA" NNlibEnzymeCoreExt = "EnzymeCore" [compat] -AMDGPU = "0.8, 0.9" +AMDGPU = "0.9.3" Adapt = "3.2, 4" Atomix = "0.1" CUDA = "4, 5" From 01cc8a25f3c31cbeed258619319c245a23461553 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Mon, 27 May 2024 11:01:46 +0300 Subject: [PATCH 05/13] Install GPU backends only when testing them --- Project.toml | 23 ----------------------- test/Project.toml | 23 +++++++++++++++++++++++ test/runtests.jl | 5 +++++ 3 files changed, 28 insertions(+), 23 deletions(-) create mode 100644 test/Project.toml diff --git a/Project.toml b/Project.toml index c732d37e5..eb49aeb85 100644 --- a/Project.toml +++ b/Project.toml @@ -43,26 +43,3 @@ Requires = "1.0" Statistics = "1" cuDNN = "1" julia = "1.9" - -[extras] -AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" -CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" -ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a" -Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" -EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" -EnzymeTestUtils = "12d8515a-0907-448a-8884-5fe00fdf1c5a" -FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" -ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" -ImageTransformations = "02fcd773-0e25-5acc-982a-7f6622650795" -Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" -Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" -ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" -StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" -Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" -cuDNN = "02a925ec-e4fe-4b08-9a7e-0d78e3d38ccd" - -[targets] -test = ["AMDGPU", "CUDA", "ChainRulesTestUtils", "Documenter", "FiniteDifferences", "ForwardDiff", "Logging", "ReverseDiff", "StableRNGs", "Test", "UnicodePlots", "Zygote", "cuDNN", "Enzyme", "EnzymeCore", "EnzymeTestUtils", "Interpolations", "ImageTransformations"] diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 000000000..e45b230e3 --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,23 @@ +[deps] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" +ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" +EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" +EnzymeTestUtils = "12d8515a-0907-448a-8884-5fe00fdf1c5a" +ImageTransformations = "02fcd773-0e25-5acc-982a-7f6622650795" +Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" +Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" +ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" +StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" +UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" +Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" +Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/test/runtests.jl b/test/runtests.jl index c9290f89e..30c95ca46 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,6 +14,7 @@ using ImageTransformations using Interpolations: Constant using KernelAbstractions import ReverseDiff as RD # used in `pooling.jl` +import Pkg const Test_Enzyme = VERSION <= v"1.10-" @@ -137,6 +138,8 @@ end end if get(ENV, "NNLIB_TEST_CUDA", "false") == "true" + Pkg.add(["CUDA", "cuDNN"]) + using CUDA if CUDA.functional() @testset "CUDA" begin @@ -152,6 +155,8 @@ end end if get(ENV, "NNLIB_TEST_AMDGPU", "false") == "true" + Pkg.add("AMDGPU") + using AMDGPU AMDGPU.versioninfo() if AMDGPU.functional() && AMDGPU.functional(:MIOpen) From bd76e9fa14b56e7f40eb11dfb6601fda8cd35d0c Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Wed, 5 Jun 2024 18:25:00 +0300 Subject: [PATCH 06/13] Add spectrogram --- docs/src/reference.md | 3 ++ src/NNlib.jl | 3 +- src/audio/spectrogram.jl | 89 ++++++++++++++++++++++++++++++++++++++ test/testsuite/spectral.jl | 42 ++++++++++++++++-- 4 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 src/audio/spectrogram.jl diff --git a/docs/src/reference.md b/docs/src/reference.md index 3eac061aa..d9769e396 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -171,4 +171,7 @@ hann_window hamming_window stft istft +spectrogram +NNlib.power_to_db +NNlib.db_to_power ``` diff --git a/src/NNlib.jl b/src/NNlib.jl index 4b9f3d0c5..d303620ab 100644 --- a/src/NNlib.jl +++ b/src/NNlib.jl @@ -128,6 +128,7 @@ include("rotation.jl") export imrotate, ∇imrotate include("audio/stft.jl") -export stft, istft, hann_window, hamming_window +include("audio/spectrogram.jl") +export stft, istft, hann_window, hamming_window, spectrogram end # module NNlib diff --git a/src/audio/spectrogram.jl b/src/audio/spectrogram.jl new file mode 100644 index 000000000..0b600a6ef --- /dev/null +++ b/src/audio/spectrogram.jl @@ -0,0 +1,89 @@ +""" + +Create a spectrogram or a batch of spectrograms from a raw audio signal. + +# Arguments + +- `pad::Int`: + Then amount of padding to apply on both sides. + Default is `0`. +- `window_normalized::Bool`: + Whether to normalize the waveform by the window’s L2 energy. + Default is `false`. +- `power::Real`: + Exponent for the magnitude spectrogram (must be ≥ 0) + e.g., `1` for magnitude, `2` for power, etc. + If `0`, complex spectrum is returned instead. + +See [`stft`](@ref) for other arguments. + +# Returns + +Spectrogram in the shape `(T, F, B)`, where +`T` is the number of window hops and `F = n_fft ÷ 2 + 1`. + +# Example + +```julia +julia> waveform, sampling_rate = load("test.flac"); + +julia> spec = spectrogram(waveform; + n_fft=1024, hop_length=128, window=hann_window(1024)); + +julia> spec_db = NNlib.power_to_db(spec); + +julia> Makie.heatmap(spec_db[:, :, 1]) +``` +""" +function spectrogram(waveform; + pad::Int = 0, n_fft::Int, hop_length::Int, window, + center::Bool = true, power::Real = 2.0, + normalized::Bool = false, window_normalized::Bool = false, +) + pad > 0 && (waveform = pad_zeros(waveform, pad; dims=1);) + + # Pack batch dimensions. + sz = size(waveform) + spec_ = stft(reshape(waveform, (sz[1], :)); + n_fft, hop_length, window, center, normalized) + # Unpack batch dimensions. + spec = reshape(spec_, (size(spec_)[1:2]..., sz[2:end]...)) + window_normalized && (spec .*= inv(norm(window));) + + if power > 0 + p = real(eltype(spec)(power)) + spec = abs.(spec).^p + end + return spec +end + +""" + power_to_db(s; ref::Real = 1f0, amin::Real = 1f-10, top_db::Real = 80f0) + +Convert a power spectrogram (amplitude squared) to decibel (dB) units. + +# Arguments + +- `s`: Input power. +- `ref`: Scalar w.r.t. which the input is scaled. Default is `1`. +- `amin`: Minimum threshold for `s`. Default is `1f-10`. +- `top_db`: Threshold the output at `top_db` below the peak: + `max.(s_db, maximum(s_db) - top_db)`. Default is `80`. + +# Returns + +`s_db ~= 10 * log10(s) - 10 * log10(ref)` +""" +function power_to_db(s; ref::Real = 1f0, amin::Real = 1f-10, top_db::Real = 80f0) + log_spec = 10f0 .* (log10.(max.(amin, s)) .- log10.(max.(amin, ref))) + return max.(log_spec, maximum(log_spec) - top_db) +end + +""" + db_to_power(s_db; ref::Real = 1f0) + +Inverse of [`power_to_db`](@ref). +""" +function db_to_power(s_db; ref::Real = 1f0) + return ref .* 10f0.^(s_db .* 0.1f0) +end diff --git a/test/testsuite/spectral.jl b/test/testsuite/spectral.jl index b825759af..79a0268f5 100644 --- a/test/testsuite/spectral.jl +++ b/test/testsuite/spectral.jl @@ -1,6 +1,9 @@ using NNlib function spectral_testsuite(Backend) + cpu(x) = adapt(CPU(), x) + device(x) = adapt(Backend(), x) + @testset "Window functions" begin for window_fn in (hann_window, hamming_window) @inferred window_fn(10, Float32) @@ -18,9 +21,6 @@ function spectral_testsuite(Backend) end @testset "STFT" begin - cpu(x) = adapt(CPU(), x) - device(x) = adapt(Backend(), x) - for batch in ((), (3,)) @testset "Batch $batch" begin x = device(ones(Float32, 16, batch...)) @@ -85,4 +85,40 @@ function spectral_testsuite(Backend) end end end + + @testset "Spectrogram" begin + x = device(rand(Float32, 1024)) + window = device(hann_window(1024)) + + y = stft(x; + n_fft=1024, hop_length=128, window, + center=true, normalized=false) + spec = spectrogram(x; + n_fft=1024, hop_length=128, window, + center=true, normalized=false) + + @test abs.(y).^2 ≈ spec + + # Batched. + x = device(rand(Float32, 1024, 3)) + spec = spectrogram(x; + n_fft=1024, hop_length=128, window, + center=true, normalized=false) + for i in 1:3 + y = stft(x[:, i]; + n_fft=1024, hop_length=128, window, + center=true, normalized=false) + @test abs.(y).^2 ≈ spec[:, :, i] + end + end + + @testset "Power to dB" begin + x = device(rand(Float32, 1024)) + window = device(hann_window(1024)) + spec = spectrogram(x; pad=0, n_fft=1024, hop_length=128, window) + + @test spec ≈ NNlib.db_to_power(NNlib.power_to_db(spec)) + @inferred NNlib.power_to_db(spec) + @inferred NNlib.db_to_power(NNlib.power_to_db(spec)) + end end From 5460ed7cd0854e8a6ad4ecb8981422418736bc46 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Thu, 6 Jun 2024 23:56:34 +0300 Subject: [PATCH 07/13] Move audio documentation to its own page - Convert examples to doctests or evaluate during build time -More tests --- docs/Project.toml | 3 + docs/make.jl | 24 +++---- docs/src/assets/jfk.flac | Bin 0 -> 199531 bytes docs/src/audio.md | 40 +++++++++++ docs/src/reference.md | 12 ---- src/audio/spectrogram.jl | 28 +++----- src/audio/stft.jl | 132 ++++++++++++++++++------------------- test/testsuite/spectral.jl | 35 ++++++++++ 8 files changed, 163 insertions(+), 111 deletions(-) create mode 100644 docs/src/assets/jfk.flac create mode 100644 docs/src/audio.md diff --git a/docs/Project.toml b/docs/Project.toml index ce9aa3290..618b7e55f 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,3 +1,6 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +FLAC = "abae9e3b-a9a0-4778-b5c6-ca109b507d99" +FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" +UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" diff --git a/docs/make.jl b/docs/make.jl index 599aaf9dc..6b06f971e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -3,17 +3,19 @@ using Documenter, NNlib DocMeta.setdocmeta!(NNlib, :DocTestSetup, :(using NNlib); recursive = true) makedocs(modules = [NNlib], - sitename = "NNlib.jl", - doctest = false, - pages = ["Home" => "index.md", - "Reference" => "reference.md"], - format = Documenter.HTML( - canonical = "https://fluxml.ai/NNlib.jl/stable/", - # analytics = "UA-36890222-9", - assets = ["assets/flux.css"], - prettyurls = get(ENV, "CI", nothing) == "true"), - warnonly=[:missing_docs,] - ) + sitename = "NNlib.jl", + doctest = true, + pages = ["Home" => "index.md", + "Reference" => "reference.md", + "Audio" => "audio.md"], + format = Documenter.HTML( + canonical = "https://fluxml.ai/NNlib.jl/stable/", + # analytics = "UA-36890222-9", + assets = ["assets/flux.css"], + prettyurls = get(ENV, "CI", nothing) == "true", + size_threshold=nothing), + warnonly=[:missing_docs,] +) deploydocs(repo = "github.com/FluxML/NNlib.jl.git", target = "build", diff --git a/docs/src/assets/jfk.flac b/docs/src/assets/jfk.flac new file mode 100644 index 0000000000000000000000000000000000000000..24841d55a6a66f7e0e6c97f925da1b8f10380fd2 GIT binary patch literal 199531 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%0Aa9g1CrE-@!kj+g_6@ern=5;{~ZC^P~)i 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
  • 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 waveform, sampling_rate = load("test.flac"); - -julia> spec = spectrogram(waveform; - n_fft=1024, hop_length=128, window=hann_window(1024)); - -julia> spec_db = NNlib.power_to_db(spec); - -julia> Makie.heatmap(spec_db[:, :, 1]) -``` """ function spectrogram(waveform; pad::Int = 0, n_fft::Int, hop_length::Int, window, @@ -48,7 +38,7 @@ function spectrogram(waveform; n_fft, hop_length, window, center, normalized) # Unpack batch dimensions. spec = reshape(spec_, (size(spec_)[1:2]..., sz[2:end]...)) - window_normalized && (spec .*= inv(norm(window));) + window_normalized && (spec = spec .* inv(norm(window));) if power > 0 p = real(eltype(spec)(power)) @@ -65,10 +55,10 @@ Convert a power spectrogram (amplitude squared) to decibel (dB) units. # Arguments - `s`: Input power. -- `ref`: Scalar w.r.t. which the input is scaled. Default is `1`. -- `amin`: Minimum threshold for `s`. Default is `1f-10`. +- `ref`: Scalar w.r.t. which the input is scaled. +- `amin`: Minimum threshold for `s`. - `top_db`: Threshold the output at `top_db` below the peak: - `max.(s_db, maximum(s_db) - top_db)`. Default is `80`. + `max.(s_db, maximum(s_db) - top_db)`. # Returns diff --git a/src/audio/stft.jl b/src/audio/stft.jl index b7447033d..421aaeb67 100644 --- a/src/audio/stft.jl +++ b/src/audio/stft.jl @@ -1,40 +1,38 @@ """ - hann_window( + hamming_window( window_length::Int, ::Type{T} = Float32; periodic::Bool = true, + α::T = T(0.54), β::T = T(0.46), ) where T <: Real -Hann window function. +Hamming window function +(ref: https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows). +Generalized version of `hann_window`. -``w[n] = \\frac{1}{2}[1 - cos(\\frac{2 \\pi n}{N - 1})]`` +``w[n] = \\alpha - \\beta cos(\\frac{2 \\pi n}{N - 1})`` Where ``N`` is the window length. ```julia -julia> lineplot(hann_window(100)) - ┌────────────────────────────────────────┐ - 1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠎⠉⠉⠉⠣⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⡰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⡠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⡰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀│ - │⠀⠀⠀⠀⡔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢆⠀⠀⠀│ - 0 │⣀⣀⡔⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢆⣀│ - └────────────────────────────────────────┘ - ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀100⠀ +julia> lineplot(hamming_window(100); width=30, height=10) + ┌──────────────────────────────┐ + 1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠚⠉⠉⠉⠢⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠁⠀⠀⠀⠀⠀⠈⢢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⡀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⢰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⣠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⡀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⢰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡄⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⡰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀│ + │⠀⠀⠀⢀⠴⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀│ + │⠀⢀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⣀⠀│ + 0 │⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉│ + └──────────────────────────────┘ + ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀100⠀ ``` # Arguments: - `window_length::Int`: Size of the window. -- `::Type{T}`: Elemet type of the window. Default is `Float32`. +- `::Type{T}`: Elemet type of the window. # Keyword Arguments: @@ -43,63 +41,64 @@ julia> lineplot(hann_window(100)) Following always holds: -```julia -hann_window(N; periodic=true) ≈ hann_window(N + 1; periodic=false)[1:end - 1] +```jldoctest +julia> N = 256; + +julia> hamming_window(N; periodic=true) ≈ hamming_window(N + 1; periodic=false)[1:end - 1] +true ``` +- `α::Real`: Coefficient α in the equation above. +- `β::Real`: Coefficient β in the equation above. # Returns: Vector of length `window_length` and eltype `T`. """ -function hann_window( +function hamming_window( window_length::Int, ::Type{T} = Float32; periodic::Bool = true, + α::T = T(0.54), β::T = T(0.46), ) where T <: Real window_length < 1 && throw(ArgumentError( "`window_length` must be > 0, instead: `$window_length`.")) n::T = ifelse(periodic, window_length, window_length - 1) scale = T(2) * π / n - [T(0.5) * (T(1) - cos(scale * T(k))) for k in 0:(window_length - 1)] + return [α - β * cos(scale * T(k)) for k in 0:(window_length - 1)] end """ - hamming_window( + hann_window( window_length::Int, ::Type{T} = Float32; periodic::Bool = true, - α::T = T(0.54), β::T = T(0.46), ) where T <: Real -Hamming window function. Generalized version of `hann_window`. +Hann window function +(ref: https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows). -``w[n] = \\alpha - \\beta cos(\\frac{2 \\pi n}{N - 1})`` +``w[n] = \\frac{1}{2}[1 - cos(\\frac{2 \\pi n}{N - 1})]`` Where ``N`` is the window length. ```julia -julia> lineplot(hamming_window(100)) - ┌────────────────────────────────────────┐ - 1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠉⠉⠉⠓⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠎⠀⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⡄⠀⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⠀⣠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⠀⡠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⡄⠀⠀⠀⠀⠀│ - │⠀⠀⠀⠀⠀⡰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀│ - │⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠣⡀⠀⠀│ - │⣀⠤⠖⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⠤│ - 0 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - └────────────────────────────────────────┘ - ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀100⠀ +julia> lineplot(hann_window(100); width=30, height=10) + ┌──────────────────────────────┐ + 1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠚⠉⠉⠉⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡔⠁⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠞⠀⠀⠀⠀⠀⠀⠀⠀⠈⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⢀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢣⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢦⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⢀⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢆⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⢀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢇⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢦⠀⠀⠀⠀│ + │⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀│ + 0 │⣀⣀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢤⣀│ + └──────────────────────────────┘ + ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀100⠀ ``` # Arguments: - `window_length::Int`: Size of the window. -- `::Type{T}`: Elemet type of the window. Default is `Float32`. +- `::Type{T}`: Elemet type of the window. # Keyword Arguments: @@ -108,26 +107,24 @@ julia> lineplot(hamming_window(100)) Following always holds: -```julia -hamming_window(N; periodic=true) ≈ hamming_window(N + 1; periodic=false)[1:end - 1] +```jldoctest +julia> N = 256; + +julia> hann_window(N; periodic=true) ≈ hann_window(N + 1; periodic=false)[1:end - 1] +true + +julia> hann_window(N) ≈ hamming_window(N; α=0.5f0, β=0.5f0) +true ``` -- `α::Real`: Coefficient α in the equation above. -- `β::Real`: Coefficient β in the equation above. # Returns: Vector of length `window_length` and eltype `T`. """ -function hamming_window( +function hann_window( window_length::Int, ::Type{T} = Float32; periodic::Bool = true, - α::T = T(0.54), β::T = T(0.46), ) where T <: Real - window_length < 1 && throw(ArgumentError( - "`window_length` must be > 0, instead: `$window_length`.")) - - n::T = ifelse(periodic, window_length, window_length - 1) - scale = T(2) * π / n - [α - β * cos(scale * T(k)) for k in 0:(window_length - 1)] + hamming_window(window_length, T; periodic, α=T(0.5), β=T(0.5)) end """ @@ -156,14 +153,13 @@ and ``m`` is the index of the sliding window. - `n_fft::Int`: Size of Fourier transform. - `hop_length::Int`: Distance between neighboring sliding window frames. - Default is `n_fft ÷ 4`. - `window`: Optional window function to apply. Must be 1D vector `0 < length(window) ≤ n_fft`. If window is shorter than `n_fft`, it is padded with zeros on both sides. If `nothing` (default), then no window is applied. - `center::Bool`: Whether to pad input on both sides so that ``t``-th frame is centered at time ``t \\times \\text{hop length}``. - Default is `true`. Padding is done with `pad_reflect` function. + Padding is done with `pad_reflect` function. - `normalized::Bool`: Whether to return normalized STFT, i.e. multiplied with ``\\text{n fft}^{-0.5}``. @@ -222,7 +218,7 @@ function stft(x; use_window && (x = x .* window;) y = eltype(x) <: Complex ? fft(x, region) : rfft(x, region) - normalized && (y .*= n_fft^-0.5;) + normalized && (y = y .* n_fft^-0.5;) return y end @@ -247,16 +243,14 @@ Return the least squares estimation of the original signal - `n_fft::Int`: Size of Fourier transform. - `hop_length::Int`: Distance between neighboring sliding window frames. - Default is `n_fft ÷ 4`. - `window`: Window function that was applied to the input of `stft`. If `nothing` (default), then no window was applied. - `center::Bool`: Whether input to `stft` was padded on both sides so that ``t``-th frame is centered at time ``t \\times \\text{hop length}``. - Default is `true`. Padding is done with `pad_reflect` function. + Padding is done with `pad_reflect` function. - `normalized::Bool`: Whether input to `stft` was normalized. - `return_complex::Bool`: Whether the output should be complex, or if the input should be assumed to derive from a real signal and window. - Default `false`. - `original_length::Union{Nothing, Int}`: Optional size of the first dimension of the input to `stft`. Helps restoring the exact `stft` input size. Otherwise, the array might be a bit shorter. diff --git a/test/testsuite/spectral.jl b/test/testsuite/spectral.jl index 79a0268f5..fee5cf396 100644 --- a/test/testsuite/spectral.jl +++ b/test/testsuite/spectral.jl @@ -3,6 +3,7 @@ using NNlib function spectral_testsuite(Backend) cpu(x) = adapt(CPU(), x) device(x) = adapt(Backend(), x) + gradtest_fn = Backend == CPU ? gradtest : gputest @testset "Window functions" begin for window_fn in (hann_window, hamming_window) @@ -22,6 +23,23 @@ function spectral_testsuite(Backend) @testset "STFT" begin for batch in ((), (3,)) + @testset "Grads" begin + if Backend != CPU + x = rand(Float32, 16, batch...) + window = hann_window(16) + + gradtest_fn(s -> abs.(stft(s; n_fft=16)), x) + gradtest_fn((s, w) -> abs.(stft(s; n_fft=16, window=w)), x, window) + + x = rand(Float32, 2045, batch...) + n_fft = 256 + window = hann_window(n_fft) + gradtest_fn((s, w) -> abs.(stft(s; n_fft, window=w)), x, window) + gradtest_fn((s, w) -> abs.(stft(s; n_fft, window=w, center=false)), x, window) + gradtest_fn((s, w) -> abs.(stft(s; n_fft, window=w, normalized=true)), x, window) + end + end + @testset "Batch $batch" begin x = device(ones(Float32, 16, batch...)) # TODO fix type stability for pad_reflect @@ -43,6 +61,12 @@ function spectral_testsuite(Backend) xx = istft(y; n_fft=1024) @test cpu(x) ≈ cpu(xx) + if ndims(x) == 2 + for b in 1:size(x, 2) + @test cpu(stft(x[:, b]; n_fft=1024)) ≈ cpu(@view(y[:, :, b])) + end + end + # Test odd sizes. x = device(rand(Float32, 1111, batch...)) y = stft(x; n_fft=256) @@ -110,6 +134,17 @@ function spectral_testsuite(Backend) center=true, normalized=false) @test abs.(y).^2 ≈ spec[:, :, i] end + + @testset "Grads" begin + if Backend != CPU + x = rand(Float32, 2045, batch...) + n_fft = 256 + window = hann_window(n_fft) + gradtest_fn((s, w) -> spectrogram(s; n_fft, hop_length=n_fft ÷ 4, window=w), x, window) + gradtest_fn((s, w) -> spectrogram(s; n_fft, hop_length=n_fft ÷ 4, window=w, center=false), x, window) + gradtest_fn((s, w) -> spectrogram(s; n_fft, hop_length=n_fft ÷ 4, window=w, normalized=true), x, window) + end + end end @testset "Power to dB" begin From 3eb94203452bd22f64af32781935de04d5f765cf Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Sat, 8 Jun 2024 16:52:05 +0300 Subject: [PATCH 08/13] Fixes --- Project.toml | 2 +- test/runtests.jl | 5 +- test/testsuite/spectral.jl | 168 ++++++++++++++++++------------------- 3 files changed, 83 insertions(+), 92 deletions(-) diff --git a/Project.toml b/Project.toml index eb49aeb85..765527d21 100644 --- a/Project.toml +++ b/Project.toml @@ -28,7 +28,7 @@ NNlibCUDAExt = "CUDA" NNlibEnzymeCoreExt = "EnzymeCore" [compat] -AMDGPU = "0.9.3" +AMDGPU = "0.9.4" Adapt = "3.2, 4" Atomix = "0.1" CUDA = "4, 5" diff --git a/test/runtests.jl b/test/runtests.jl index 30c95ca46..e7922a750 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -160,13 +160,12 @@ end using AMDGPU AMDGPU.versioninfo() if AMDGPU.functional() && AMDGPU.functional(:MIOpen) - @show AMDGPU.MIOpen.version() @testset "AMDGPU" begin nnlib_testsuite(ROCBackend) - AMDGPU.synchronize(; blocking=false) + AMDGPU.synchronize(; blocking=false, stop_hostcalls=true) include("ext_amdgpu/runtests.jl") - AMDGPU.synchronize(; blocking=false) + AMDGPU.synchronize(; blocking=false, stop_hostcalls=true) end else @info "AMDGPU.jl package is not functional. Skipping AMDGPU tests." diff --git a/test/testsuite/spectral.jl b/test/testsuite/spectral.jl index fee5cf396..12e38cc4a 100644 --- a/test/testsuite/spectral.jl +++ b/test/testsuite/spectral.jl @@ -1,5 +1,3 @@ -using NNlib - function spectral_testsuite(Backend) cpu(x) = adapt(CPU(), x) device(x) = adapt(Backend(), x) @@ -21,91 +19,83 @@ function spectral_testsuite(Backend) end end - @testset "STFT" begin - for batch in ((), (3,)) - @testset "Grads" begin - if Backend != CPU - x = rand(Float32, 16, batch...) - window = hann_window(16) + @testset "STFT" for batch in ((), (3,)) + @testset "Grads" begin + if Backend != CPU + x = rand(Float32, 16, batch...) + window = hann_window(16) - gradtest_fn(s -> abs.(stft(s; n_fft=16)), x) - gradtest_fn((s, w) -> abs.(stft(s; n_fft=16, window=w)), x, window) + gradtest_fn(s -> abs.(stft(s; n_fft=16)), x) + gradtest_fn((s, w) -> abs.(stft(s; n_fft=16, window=w)), x, window) - x = rand(Float32, 2045, batch...) - n_fft = 256 - window = hann_window(n_fft) - gradtest_fn((s, w) -> abs.(stft(s; n_fft, window=w)), x, window) - gradtest_fn((s, w) -> abs.(stft(s; n_fft, window=w, center=false)), x, window) - gradtest_fn((s, w) -> abs.(stft(s; n_fft, window=w, normalized=true)), x, window) - end + x = rand(Float32, 2045, batch...) + n_fft = 256 + window = hann_window(n_fft) + gradtest_fn((s, w) -> abs.(stft(s; n_fft, window=w)), x, window) + gradtest_fn((s, w) -> abs.(stft(s; n_fft, window=w, center=false)), x, window) + gradtest_fn((s, w) -> abs.(stft(s; n_fft, window=w, normalized=true)), x, window) end + end - @testset "Batch $batch" begin - x = device(ones(Float32, 16, batch...)) - # TODO fix type stability for pad_reflect - # @inferred stft(x; n_fft=16) - - bd = ntuple(_ -> Colon(), length(batch)) - - y = stft(x; n_fft=16) - @test size(y) == (9, 5, batch...) - @test all(real(cpu(y))[1, :, bd...] .≈ 16) - - xx = istft(y; n_fft=16) - @test size(xx) == (16, batch...) - @test cpu(x) ≈ cpu(xx) - - # Test multiple hops. - x = device(rand(Float32, 2048, batch...)) - y = stft(x; n_fft=1024) - xx = istft(y; n_fft=1024) - @test cpu(x) ≈ cpu(xx) - - if ndims(x) == 2 - for b in 1:size(x, 2) - @test cpu(stft(x[:, b]; n_fft=1024)) ≈ cpu(@view(y[:, :, b])) - end - end - - # Test odd sizes. - x = device(rand(Float32, 1111, batch...)) - y = stft(x; n_fft=256) - xx = istft(y; n_fft=256, original_length=size(x, 1)) - @test cpu(x) ≈ cpu(xx) - - # Output from inverse is cropped on the right - # without knowing the original size. - xx = istft(y; n_fft=256) - @test length(xx) < length(x) - @test cpu(x)[[1:s for s in size(xx)]...] ≈ cpu(xx) - - # Test different options. - - # Normalized. - x = device(rand(Float32, 1234, batch...)) - y = stft(x; n_fft=512, normalized=true) - xx = istft(y; n_fft=512, normalized=true, original_length=size(x, 1)) + @testset "Batch $batch" begin + x = device(ones(Float32, 16, batch...)) + # TODO fix type stability for pad_reflect + # @inferred stft(x; n_fft=16) + + bd = ntuple(_ -> Colon(), length(batch)) + + y = stft(x; n_fft=16) + @test size(y) == (9, 5, batch...) + @test all(real(cpu(y))[1, :, bd...] .≈ 16) + + xx = istft(y; n_fft=16) + @test size(xx) == (16, batch...) + @test cpu(x) ≈ cpu(xx) + + # Test multiple hops. + x = device(rand(Float32, 2048, batch...)) + y = stft(x; n_fft=1024) + xx = istft(y; n_fft=1024) + @test cpu(x) ≈ cpu(xx) + + # Test odd sizes. + x = device(rand(Float32, 1111, batch...)) + y = stft(x; n_fft=256) + xx = istft(y; n_fft=256, original_length=size(x, 1)) + @test cpu(x) ≈ cpu(xx) + + # Output from inverse is cropped on the right + # without knowing the original size. + xx = istft(y; n_fft=256) + @test length(xx) < length(x) + @test cpu(x)[[1:s for s in size(xx)]...] ≈ cpu(xx) + + # Test different options. + + # Normalized. + x = device(rand(Float32, 1234, batch...)) + y = stft(x; n_fft=512, normalized=true) + xx = istft(y; n_fft=512, normalized=true, original_length=size(x, 1)) + @test cpu(x) ≈ cpu(xx) + + # With window. + window = device(hann_window(512)) + y = stft(x; n_fft=512, window) + xx = istft(y; n_fft=512, window, original_length=size(x, 1)) + @test cpu(x) ≈ cpu(xx) + + # Hop. + for hop_length in (32, 33, 255, 256, 511, 512) + y = stft(x; n_fft=512, hop_length) + xx = istft(y; n_fft=512, hop_length, original_length=size(x, 1)) @test cpu(x) ≈ cpu(xx) + end - # With window. - window = device(hann_window(512)) - y = stft(x; n_fft=512, window) - xx = istft(y; n_fft=512, window, original_length=size(x, 1)) + # N FFT. + for n_fft in (32, 33, 64, 65, 128, 129, 512) + y = stft(x; n_fft) + xx = istft(y; n_fft, original_length=size(x, 1)) @test cpu(x) ≈ cpu(xx) - - # Hop. - for hop_length in (32, 33, 255, 256, 511, 512) - y = stft(x; n_fft=512, hop_length) - xx = istft(y; n_fft=512, hop_length, original_length=size(x, 1)) - @test cpu(x) ≈ cpu(xx) - end - - # N FFT. - for n_fft in (32, 33, 64, 65, 128, 129, 512) - y = stft(x; n_fft) - xx = istft(y; n_fft, original_length=size(x, 1)) - @test cpu(x) ≈ cpu(xx) - end end end end @@ -135,14 +125,16 @@ function spectral_testsuite(Backend) @test abs.(y).^2 ≈ spec[:, :, i] end - @testset "Grads" begin - if Backend != CPU - x = rand(Float32, 2045, batch...) - n_fft = 256 - window = hann_window(n_fft) - gradtest_fn((s, w) -> spectrogram(s; n_fft, hop_length=n_fft ÷ 4, window=w), x, window) - gradtest_fn((s, w) -> spectrogram(s; n_fft, hop_length=n_fft ÷ 4, window=w, center=false), x, window) - gradtest_fn((s, w) -> spectrogram(s; n_fft, hop_length=n_fft ÷ 4, window=w, normalized=true), x, window) + if Backend != CPU + @testset "Grads" begin + for batch in ((), (3,)) + x = rand(Float32, 2045, batch...) + n_fft = 256 + window = hann_window(n_fft) + gradtest_fn((s, w) -> spectrogram(s; n_fft, hop_length=n_fft ÷ 4, window=w), x, window) + gradtest_fn((s, w) -> spectrogram(s; n_fft, hop_length=n_fft ÷ 4, window=w, center=false), x, window) + gradtest_fn((s, w) -> spectrogram(s; n_fft, hop_length=n_fft ÷ 4, window=w, normalized=true), x, window) + end end end end From 97dbf6595a9897365a57a79de3e7095670c572c3 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Sat, 8 Jun 2024 17:18:32 +0300 Subject: [PATCH 09/13] Use Makie for spectrogram plots --- docs/Project.toml | 3 ++- docs/make.jl | 3 +-- docs/src/audio.md | 17 +++++++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index 618b7e55f..afc3c46df 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,6 +1,7 @@ [deps] +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" FLAC = "abae9e3b-a9a0-4778-b5c6-ca109b507d99" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" -UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" diff --git a/docs/make.jl b/docs/make.jl index 6b06f971e..ad5682e6b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -12,8 +12,7 @@ makedocs(modules = [NNlib], canonical = "https://fluxml.ai/NNlib.jl/stable/", # analytics = "UA-36890222-9", assets = ["assets/flux.css"], - prettyurls = get(ENV, "CI", nothing) == "true", - size_threshold=nothing), + prettyurls = get(ENV, "CI", nothing) == "true"), warnonly=[:missing_docs,] ) diff --git a/docs/src/audio.md b/docs/src/audio.md index 92de0de9b..379515cf3 100644 --- a/docs/src/audio.md +++ b/docs/src/audio.md @@ -27,14 +27,23 @@ Example: ```@example 1 using NNlib using FileIO -using UnicodePlots +using Makie, CairoMakie +CairoMakie.activate!() waveform, sampling_rate = load("./assets/jfk.flac") -lineplot(reshape(waveform, :); width=50, color=:white) +fig = lines(reshape(waveform, :)) +save("waveform.png", fig) +nothing # hide ``` +![](waveform.png) + ```@example 1 -n_fft = 256 +n_fft = 1024 spec = spectrogram(waveform; n_fft, hop_length=n_fft ÷ 4, window=hann_window(n_fft)) -heatmap(NNlib.power_to_db(spec)[:, :, 1]; width=80, height=30) +fig = heatmap(transpose(NNlib.power_to_db(spec)[:, :, 1])) +save("spectrogram.png", fig) +nothing # hide ``` + +![](spectrogram.png) From 0e5b94bb8dece336c1cb98e4a56ee7699cdd8142 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Sun, 9 Jun 2024 12:56:48 +0300 Subject: [PATCH 10/13] Add mel-scale filterbanks --- docs/Project.toml | 1 + docs/make.jl | 3 +- docs/src/audio.md | 18 +++++--- src/NNlib.jl | 3 +- src/audio/mel.jl | 102 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 src/audio/mel.jl diff --git a/docs/Project.toml b/docs/Project.toml index afc3c46df..6983e344c 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -5,3 +5,4 @@ FLAC = "abae9e3b-a9a0-4778-b5c6-ca109b507d99" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" +UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" diff --git a/docs/make.jl b/docs/make.jl index ad5682e6b..d532f0734 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,6 +1,7 @@ using Documenter, NNlib -DocMeta.setdocmeta!(NNlib, :DocTestSetup, :(using NNlib); recursive = true) +DocMeta.setdocmeta!(NNlib, :DocTestSetup, + :(using NNlib, UnicodePlots); recursive = true) makedocs(modules = [NNlib], sitename = "NNlib.jl", diff --git a/docs/src/audio.md b/docs/src/audio.md index 379515cf3..1e5435318 100644 --- a/docs/src/audio.md +++ b/docs/src/audio.md @@ -19,6 +19,7 @@ NNlib.db_to_power ## Spectrogram ```@docs +melscale_filterbanks spectrogram ``` @@ -33,17 +34,24 @@ CairoMakie.activate!() waveform, sampling_rate = load("./assets/jfk.flac") fig = lines(reshape(waveform, :)) save("waveform.png", fig) -nothing # hide -``` -![](waveform.png) +# Spectrogram. -```@example 1 n_fft = 1024 spec = spectrogram(waveform; n_fft, hop_length=n_fft ÷ 4, window=hann_window(n_fft)) fig = heatmap(transpose(NNlib.power_to_db(spec)[:, :, 1])) save("spectrogram.png", fig) + +# Mel-scale spectrogram. + +n_freqs = n_fft ÷ 2 + 1 +fb = melscale_filterbanks(; n_freqs, n_mels=128, sample_rate=Int(sampling_rate)) +mel_spec = permutedims(spec, (2, 1, 3)) ⊠ fb # (time, n_mels) +fig = heatmap(NNlib.power_to_db(mel_spec)[:, :, 1]) +save("mel-spectrogram.png", fig) nothing # hide ``` -![](spectrogram.png) +|Waveform|Spectrogram|Mel Spectrogram| +|:---:|:---:|:---:| +|![](waveform.png)|![](spectrogram.png)|![](mel-spectrogram.png)| diff --git a/src/NNlib.jl b/src/NNlib.jl index d303620ab..b6a32eba1 100644 --- a/src/NNlib.jl +++ b/src/NNlib.jl @@ -129,6 +129,7 @@ export imrotate, ∇imrotate include("audio/stft.jl") include("audio/spectrogram.jl") -export stft, istft, hann_window, hamming_window, spectrogram +include("audio/mel.jl") +export stft, istft, hann_window, hamming_window, spectrogram, melscale_filterbanks end # module NNlib diff --git a/src/audio/mel.jl b/src/audio/mel.jl new file mode 100644 index 000000000..6fda9a091 --- /dev/null +++ b/src/audio/mel.jl @@ -0,0 +1,102 @@ +""" + melscale_filterbanks(; + n_freqs::Int, n_mels::Int, sample_rate::Int, + fmin::Float32 = 0f0, fmax::Float32 = Float32(sample_rate ÷ 2)) + +Create triangular Mel scale filter banks +(ref: https://en.wikipedia.org/wiki/Mel_scale). +Each column is a filterbank that highlights its own frequency. + +# Arguments: + +- `n_freqs::Int`: Number of frequencies to highlight. +- `n_mels::Int`: Number of mel filterbanks. +- `sample_rate::Int`: Sample rate of the audio waveform. +- `fmin::Float32`: Minimum frequency in Hz. +- `fmax::Float32`: Maximum frequency in Hz. + +# Returns: + +Filterbank matrix of shape `(n_freqs, n_mels)` where each column is a filterbank. + +```jldoctest +julia> n_mels = 8; + +julia> fb = melscale_filterbanks(; n_freqs=200, n_mels, sample_rate=16000); + +julia> plot = lineplot(fb[:, 1]); + +julia> for i in 2:n_mels + lineplot!(plot, fb[:, i]) + end + +julia> plot + ┌────────────────────────────────────────┐ + 1 │⠀⡀⢸⠀⢸⠀⠀⣧⠀⠀⢸⡄⠀⠀⠀⣷⠀⠀⠀⠀⠀⣷⠀⠀⠀⠀⠀⠀⢀⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⡇⢸⡆⢸⡇⠀⣿⠀⠀⡜⡇⠀⠀⢰⠋⡆⠀⠀⠀⢰⠁⡇⠀⠀⠀⠀⠀⡸⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⣿⢸⡇⡇⡇⢰⠹⡄⠀⡇⢱⠀⠀⢸⠀⢣⠀⠀⠀⡜⠀⢸⡀⠀⠀⠀⢀⠇⠀⠈⡇⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⣿⡇⡇⡇⡇⢸⠀⡇⢀⠇⠸⡀⠀⡇⠀⠸⡀⠀⢀⠇⠀⠀⢇⠀⠀⠀⡸⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀│ + │⢠⢻⡇⡇⡇⢱⢸⠀⢇⢸⠀⠀⡇⢀⠇⠀⠀⡇⠀⢸⠀⠀⠀⠸⡀⠀⢠⠇⠀⠀⠀⠀⢱⠀⠀⠀⠀⠀⠀⠀│ + │⢸⢸⡇⢱⡇⢸⡇⠀⢸⢸⠀⠀⢣⢸⠀⠀⠀⢸⠀⡇⠀⠀⠀⠀⢇⠀⡜⠀⠀⠀⠀⠀⠈⢇⠀⠀⠀⠀⠀⠀│ + │⢸⢸⡇⢸⠀⢸⡇⠀⢸⡇⠀⠀⢸⡎⠀⠀⠀⠈⣶⠁⠀⠀⠀⠀⠸⣤⠃⠀⠀⠀⠀⠀⠀⠘⡆⠀⠀⠀⠀⠀│ + │⢸⠀⡇⢸⠀⠀⡇⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⢱⡀⠀⠀⠀⠀│ + │⢸⢸⡇⢸⠀⢸⡇⠀⢸⡇⠀⠀⢸⢇⠀⠀⠀⢀⠿⡀⠀⠀⠀⠀⢰⠛⡄⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀│ + │⢸⢸⡇⡸⡇⢸⡇⠀⢸⢸⠀⠀⡜⢸⠀⠀⠀⢸⠀⡇⠀⠀⠀⠀⡎⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠀⠀│ + │⢸⢸⡇⡇⡇⡸⢸⠀⡎⢸⠀⠀⡇⠈⡆⠀⠀⡇⠀⢸⠀⠀⠀⢰⠁⠀⠘⡆⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀│ + │⡇⢸⡇⡇⡇⡇⢸⠀⡇⠈⡆⢰⠁⠀⡇⠀⢰⠁⠀⠈⡆⠀⠀⡎⠀⠀⠀⢱⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀│ + │⡇⢸⢸⡇⡇⡇⠸⣰⠃⠀⡇⡸⠀⠀⢸⠀⡜⠀⠀⠀⢣⠀⢸⠁⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠈⢇⠀│ + │⡇⡇⢸⠇⢸⡇⠀⣿⠀⠀⢣⡇⠀⠀⠸⣄⠇⠀⠀⠀⠸⡀⡇⠀⠀⠀⠀⠀⢱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄│ + 0 │⣇⣇⣸⣀⣸⣀⣀⣟⣀⣀⣸⣃⣀⣀⣀⣿⣀⣀⣀⣀⣀⣿⣀⣀⣀⣀⣀⣀⣈⣇⣀⣀⣀⣀⣀⣀⣀⣀⣀⣱│ + └────────────────────────────────────────┘ + ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀200⠀ +``` +""" +function melscale_filterbanks(; + n_freqs::Int, n_mels::Int, sample_rate::Int, + fmin::Float32 = 0f0, fmax::Float32 = Float32(sample_rate ÷ 2), +) + mel_min, mel_max = _hz_to_mel(fmin), _hz_to_mel(fmax) + mel_points = range(mel_min, mel_max; length=n_mels + 2) + + all_freqs = collect(range(0f0, Float32(sample_rate ÷ 2); length=n_freqs)) + freq_points = _mel_to_hz.(mel_points) + filter_banks = _triangular_filterbanks(freq_points, all_freqs) + + if any(maximum(filter_banks; dims=1) .≈ 0f0) + @warn """At least one mel filterbank has all zero values. + The value for `n_mels=$n_mels` may be set too high. + Or the value for `n_freqs=$n_freqs` may be set too low. + """ + end + return filter_banks +end + +_hz_to_mel(freq::T) where T = T(2595) * log10(T(1) + (freq / T(700))) + +_mel_to_hz(mel::T) where T = T(700) * (T(10)^(mel / T(2595)) - T(1)) + +""" + _triangular_filterbanks( + freq_points::Vector{Float32}, all_freqs::Vector{Float32}) + +Create triangular filter banks. + +# Arguments: + +- `freq_points::Vector{Float32}`: Filter midpoints of size `n_filters`. +- `all_freqs::Vector{Float32}`: Frequency points of size `n_freqs`. + +# Returns: + +Array of size `(n_freqs, n_filters)`. +""" +function _triangular_filterbanks( + freq_points::Vector{Float32}, all_freqs::Vector{Float32}, +) + diff = @view(freq_points[2:end]) .- @view(freq_points[1:end - 1]) + slopes = transpose(reshape(freq_points, :, 1) .- reshape(all_freqs, 1, :)) + + down_slopes = -(@view(slopes[:, 1:end - 2]) ./ reshape(@view(diff[1:end - 1]), 1, :)) + up_slopes = @view(slopes[:, 3:end]) ./ reshape(@view(diff[2:end]), 1, :) + return max.(0f0, min.(down_slopes, up_slopes)) +end From b54888b5285e861793bd227ab82720221eaafec9 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Sun, 9 Jun 2024 13:04:37 +0300 Subject: [PATCH 11/13] Run doctests when building documentation instead of a separate CI stage --- .github/workflows/ci.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7620fb6b..6b8493e43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,14 +93,6 @@ jobs: using Pkg Pkg.develop(PackageSpec(path=pwd())) Pkg.instantiate()' - - run: | - julia --color=yes --project=docs/ -e ' - using NNlib - # using Pkg; Pkg.activate("docs") - using Documenter - using Documenter: doctest - DocMeta.setdocmeta!(NNlib, :DocTestSetup, :(using NNlib); recursive=true) - doctest(NNlib)' - run: julia --project=docs docs/make.jl env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 8f7745569e3fb536471dd1b1a624d1e7460a63a4 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Mon, 24 Jun 2024 00:38:17 +0300 Subject: [PATCH 12/13] Minor fix --- src/audio/stft.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio/stft.jl b/src/audio/stft.jl index 421aaeb67..a765589fc 100644 --- a/src/audio/stft.jl +++ b/src/audio/stft.jl @@ -218,7 +218,7 @@ function stft(x; use_window && (x = x .* window;) y = eltype(x) <: Complex ? fft(x, region) : rfft(x, region) - normalized && (y = y .* n_fft^-0.5;) + normalized && (y = y .* eltype(y)(n_fft^-0.5);) return y end @@ -292,7 +292,7 @@ function istft(y; x = return_complex ? ifft(y, region) : irfft(y, n_fft, region) # De-apply window. - use_window && (x ./= window;) + use_window && (x = x ./ window;) # col2time. expected_output_len = n_fft + hop_length * (n_frames - 1) From d85ff99a02299bad9c49f1a278e2f21cca7263c8 Mon Sep 17 00:00:00 2001 From: Anton Smirnov Date: Tue, 2 Jul 2024 23:11:22 +0300 Subject: [PATCH 13/13] Move FFTW-dependent functions to extension --- Project.toml | 8 +- docs/Project.toml | 1 + docs/make.jl | 2 +- docs/src/audio.md | 4 + ext/NNlibFFTWExt/NNlibFFTWExt.jl | 9 +++ ext/NNlibFFTWExt/stft.jl | 127 ++++++++++++++++++++++++++++++ src/NNlib.jl | 1 - src/audio/stft.jl | 128 +------------------------------ test/Project.toml | 1 + test/runtests.jl | 1 + 10 files changed, 151 insertions(+), 131 deletions(-) create mode 100644 ext/NNlibFFTWExt/NNlibFFTWExt.jl create mode 100644 ext/NNlibFFTWExt/stft.jl diff --git a/Project.toml b/Project.toml index 765527d21..f3ad440ca 100644 --- a/Project.toml +++ b/Project.toml @@ -6,7 +6,6 @@ version = "0.9.17" Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" Atomix = "a9b6321e-bd34-4604-b9c9-b65b8de01458" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -18,22 +17,26 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [weakdeps] AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" -EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" cuDNN = "02a925ec-e4fe-4b08-9a7e-0d78e3d38ccd" +EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" +FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" [extensions] NNlibAMDGPUExt = "AMDGPU" NNlibCUDACUDNNExt = ["CUDA", "cuDNN"] NNlibCUDAExt = "CUDA" NNlibEnzymeCoreExt = "EnzymeCore" +NNlibFFTWExt = "FFTW" [compat] AMDGPU = "0.9.4" Adapt = "3.2, 4" Atomix = "0.1" CUDA = "4, 5" +cuDNN = "1" ChainRulesCore = "1.13" EnzymeCore = "0.5, 0.6, 0.7" +FFTW = "1.8.0" GPUArraysCore = "0.1" KernelAbstractions = "0.9.2" LinearAlgebra = "<0.0.1, 1" @@ -41,5 +44,4 @@ Pkg = "<0.0.1, 1" Random = "<0.0.1, 1" Requires = "1.0" Statistics = "1" -cuDNN = "1" julia = "1.9" diff --git a/docs/Project.toml b/docs/Project.toml index 6983e344c..9de5539c2 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -6,3 +6,4 @@ FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" +FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" diff --git a/docs/make.jl b/docs/make.jl index d532f0734..4bffca944 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,7 +1,7 @@ using Documenter, NNlib DocMeta.setdocmeta!(NNlib, :DocTestSetup, - :(using NNlib, UnicodePlots); recursive = true) + :(using FFTW, NNlib, UnicodePlots); recursive = true) makedocs(modules = [NNlib], sitename = "NNlib.jl", diff --git a/docs/src/audio.md b/docs/src/audio.md index 1e5435318..e56a5bf43 100644 --- a/docs/src/audio.md +++ b/docs/src/audio.md @@ -1,5 +1,8 @@ # Reference +!!! note + Spectral functions require importing `FFTW` package to enable them. + ## Window functions ```@docs @@ -26,6 +29,7 @@ spectrogram Example: ```@example 1 +using FFTW # <- required for STFT support. using NNlib using FileIO using Makie, CairoMakie diff --git a/ext/NNlibFFTWExt/NNlibFFTWExt.jl b/ext/NNlibFFTWExt/NNlibFFTWExt.jl new file mode 100644 index 000000000..ee314cd51 --- /dev/null +++ b/ext/NNlibFFTWExt/NNlibFFTWExt.jl @@ -0,0 +1,9 @@ +module NNlibFFTWExt + +using FFTW +using NNlib +using KernelAbstractions + +include("stft.jl") + +end diff --git a/ext/NNlibFFTWExt/stft.jl b/ext/NNlibFFTWExt/stft.jl new file mode 100644 index 000000000..dda76cec1 --- /dev/null +++ b/ext/NNlibFFTWExt/stft.jl @@ -0,0 +1,127 @@ +function NNlib.stft(x; + n_fft::Int, hop_length::Int = n_fft ÷ 4, window = nothing, + center::Bool = true, normalized::Bool = false, +) + kab = get_backend(x) + use_window = !isnothing(window) + + use_window && kab != get_backend(window) && throw(ArgumentError( + "`window` must be on the same device as stft input `x` ($kab), \ + instead: `$(get_backend(window))`.")) + use_window && !(0 < length(window) ≤ n_fft) && throw(ArgumentError( + "Expected `0 < length(window) ≤ n_fft=$n_fft`, \ + but got `length(window)=$(length(window))`.")) + hop_length < 0 && throw(ArgumentError( + "Expected `hop_length > 0`, but got `hop_length=$hop_length`.")) + + # Pad window on both sides with `0` to `n_fft` length if needed. + if use_window && length(window) < n_fft + left = ((n_fft - length(window)) ÷ 2) + 1 + tmp = KernelAbstractions.zeros(kab, eltype(window), n_fft) + tmp[left:left + length(window) - 1] .= window + window = tmp + end + + if center + pad_amount = n_fft ÷ 2 + x = pad_reflect(x, pad_amount; dims=1) + end + + n = size(x, 1) + (0 < n_fft ≤ n) || throw(ArgumentError( + "Expected `0 < n_fft ≤ size(x, 1)=$n`, but got `n_fft=$n_fft`.")) + + n_frames = 1 + (n - n_fft) ÷ hop_length + + # time2col. + # Reshape `x` to (n_fft, n_frames, B) if needed. + # Each row in `n_frames` is shifted by `hop_length`. + if n_frames > 1 + # TODO can be more efficient if we support something like torch.as_strided + ids = [ + row + hop_length * col + for row in 1:n_fft, col in 0:(n_frames - 1)] + x = x[ids, ntuple(_ -> Colon(), ndims(x) - 1)...] + end + + region = 1 + use_window && (x = x .* window;) + y = eltype(x) <: Complex ? fft(x, region) : rfft(x, region) + + normalized && (y = y .* eltype(y)(n_fft^-0.5);) + return y +end + +function NNlib.istft(y; + n_fft::Int, hop_length::Int = n_fft ÷ 4, window = nothing, + center::Bool = true, normalized::Bool = false, + return_complex::Bool = false, + original_length::Union{Nothing, Int} = nothing, +) + kab = get_backend(y) + use_window = !isnothing(window) + + use_window && kab != get_backend(window) && throw(ArgumentError( + "`window` must be on the same device as istft input `y` ($kab), \ + instead: `$(get_backend(window))`.")) + use_window && !(0 < length(window) ≤ n_fft) && throw(ArgumentError( + "Expected `0 < length(window) ≤ n_fft=$n_fft`, \ + but got `length(window)=$(length(window))`.")) + hop_length < 0 && throw(ArgumentError( + "Expected `hop_length > 0`, but got `hop_length=$hop_length`.")) + + # TODO check `y` eltype is complex + + n_frames = size(y, 2) + + # Pad window on both sides with `0` to `n_fft` length if needed. + if use_window && length(window) < n_fft + left = ((n_fft - length(window)) ÷ 2) + 1 + tmp = KernelAbstractions.zeros(kab, eltype(window), n_fft) + tmp[left:left + length(window) - 1] .= window + window = tmp + end + + # Denormalize. + normalized && (y = y .* eltype(y)(n_fft^0.5);) + + region = 1 + x = return_complex ? ifft(y, region) : irfft(y, n_fft, region) + + # De-apply window. + use_window && (x = x ./ window;) + + # col2time. + expected_output_len = n_fft + hop_length * (n_frames - 1) + + ids = Vector{Int}(undef, expected_output_len) + in_idx, out_idx = 0, 0 + prev_e, v = 0, 0 + + for col in 0:(n_frames - 1) + for row in 1:n_fft + in_idx += 1 + v = row + hop_length * col + v > prev_e || continue + + out_idx += 1 + ids[out_idx] = in_idx + end + prev_e = v + end + + # In case of batched input, reshaped it (n_fft, n_frames, batch) -> (:, batch). + nd = ntuple(_ -> Colon(), ndims(x) - 2) + ndims(x) == 3 && (x = reshape(x, (:, size(x, 3)));) + x = x[ids, nd...] + + # Trim padding. + left = center ? (n_fft ÷ 2 + 1) : 1 + right = if isnothing(original_length) + center ? (size(x, 1) - n_fft ÷ 2) : expected_output_len + else + left + original_length - 1 + end + x = x[left:right, nd...] + return x +end diff --git a/src/NNlib.jl b/src/NNlib.jl index b6a32eba1..73d5c4f56 100644 --- a/src/NNlib.jl +++ b/src/NNlib.jl @@ -17,7 +17,6 @@ using Random using Requires using Statistics using Statistics: mean -using FFTW const libblas = Base.libblas_name diff --git a/src/audio/stft.jl b/src/audio/stft.jl index a765589fc..a5b84cff0 100644 --- a/src/audio/stft.jl +++ b/src/audio/stft.jl @@ -168,59 +168,7 @@ and ``m`` is the index of the sliding window. Complex array of shape `(n_fft, n_frames, B)`, where `B` is the optional batch dimension. """ -function stft(x; - n_fft::Int, hop_length::Int = n_fft ÷ 4, window = nothing, - center::Bool = true, normalized::Bool = false, -) - kab = get_backend(x) - use_window = !isnothing(window) - - use_window && kab != get_backend(window) && throw(ArgumentError( - "`window` must be on the same device as stft input `x` ($kab), \ - instead: `$(get_backend(window))`.")) - use_window && !(0 < length(window) ≤ n_fft) && throw(ArgumentError( - "Expected `0 < length(window) ≤ n_fft=$n_fft`, \ - but got `length(window)=$(length(window))`.")) - hop_length < 0 && throw(ArgumentError( - "Expected `hop_length > 0`, but got `hop_length=$hop_length`.")) - - # Pad window on both sides with `0` to `n_fft` length if needed. - if use_window && length(window) < n_fft - left = ((n_fft - length(window)) ÷ 2) + 1 - tmp = KernelAbstractions.zeros(kab, eltype(window), n_fft) - tmp[left:left + length(window) - 1] .= window - window = tmp - end - - if center - pad_amount = n_fft ÷ 2 - x = pad_reflect(x, pad_amount; dims=1) - end - - n = size(x, 1) - (0 < n_fft ≤ n) || throw(ArgumentError( - "Expected `0 < n_fft ≤ size(x, 1)=$n`, but got `n_fft=$n_fft`.")) - - n_frames = 1 + (n - n_fft) ÷ hop_length - - # time2col. - # Reshape `x` to (n_fft, n_frames, B) if needed. - # Each row in `n_frames` is shifted by `hop_length`. - if n_frames > 1 - # TODO can be more efficient if we support something like torch.as_strided - ids = [ - row + hop_length * col - for row in 1:n_fft, col in 0:(n_frames - 1)] - x = x[ids, ntuple(_ -> Colon(), ndims(x) - 1)...] - end - - region = 1 - use_window && (x = x .* window;) - y = eltype(x) <: Complex ? fft(x, region) : rfft(x, region) - - normalized && (y = y .* eltype(y)(n_fft^-0.5);) - return y -end +function stft end """ istft(y; @@ -255,76 +203,4 @@ Return the least squares estimation of the original signal of the input to `stft`. Helps restoring the exact `stft` input size. Otherwise, the array might be a bit shorter. """ -function istft(y; - n_fft::Int, hop_length::Int = n_fft ÷ 4, window = nothing, - center::Bool = true, normalized::Bool = false, - return_complex::Bool = false, - original_length::Union{Nothing, Int} = nothing, -) - kab = get_backend(y) - use_window = !isnothing(window) - - use_window && kab != get_backend(window) && throw(ArgumentError( - "`window` must be on the same device as istft input `y` ($kab), \ - instead: `$(get_backend(window))`.")) - use_window && !(0 < length(window) ≤ n_fft) && throw(ArgumentError( - "Expected `0 < length(window) ≤ n_fft=$n_fft`, \ - but got `length(window)=$(length(window))`.")) - hop_length < 0 && throw(ArgumentError( - "Expected `hop_length > 0`, but got `hop_length=$hop_length`.")) - - # TODO check `y` eltype is complex - - n_frames = size(y, 2) - - # Pad window on both sides with `0` to `n_fft` length if needed. - if use_window && length(window) < n_fft - left = ((n_fft - length(window)) ÷ 2) + 1 - tmp = KernelAbstractions.zeros(kab, eltype(window), n_fft) - tmp[left:left + length(window) - 1] .= window - window = tmp - end - - # Denormalize. - normalized && (y = y .* eltype(y)(n_fft^0.5);) - - region = 1 - x = return_complex ? ifft(y, region) : irfft(y, n_fft, region) - - # De-apply window. - use_window && (x = x ./ window;) - - # col2time. - expected_output_len = n_fft + hop_length * (n_frames - 1) - - ids = Vector{Int}(undef, expected_output_len) - in_idx, out_idx = 0, 0 - prev_e, v = 0, 0 - - for col in 0:(n_frames - 1) - for row in 1:n_fft - in_idx += 1 - v = row + hop_length * col - v > prev_e || continue - - out_idx += 1 - ids[out_idx] = in_idx - end - prev_e = v - end - - # In case of batched input, reshaped it (n_fft, n_frames, batch) -> (:, batch). - nd = ntuple(_ -> Colon(), ndims(x) - 2) - ndims(x) == 3 && (x = reshape(x, (:, size(x, 3)));) - x = x[ids, nd...] - - # Trim padding. - left = center ? (n_fft ÷ 2 + 1) : 1 - right = if isnothing(original_length) - center ? (size(x, 1) - n_fft ÷ 2) : expected_output_len - else - left + original_length - 1 - end - x = x[left:right, nd...] - return x -end +function istft end diff --git a/test/Project.toml b/test/Project.toml index e45b230e3..b30ef0e13 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -21,3 +21,4 @@ Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" diff --git a/test/runtests.jl b/test/runtests.jl index e7922a750..77f841d31 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,6 +13,7 @@ using Adapt using ImageTransformations using Interpolations: Constant using KernelAbstractions +using FFTW import ReverseDiff as RD # used in `pooling.jl` import Pkg