Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

change tests, fix compat with 1.6 #40

Merged
merged 12 commits into from
Dec 5, 2023
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
16 changes: 14 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,25 @@ on:

jobs:
test:
name: Julia ${{ matrix.julia-version }} - ${{ matrix.os }} - ${{ matrix.julia-arch }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
julia-version: ['1', '1.8', 'nightly']
julia-version:
- 1
julia-arch: [x64]
os: [ubuntu-latest, macOS-latest]
os:
- ubuntu-latest
- windows-latest
- macOS-latest
include:
- os: ubuntu-latest
julia-arch: x64
julia-version: 1.6
- os: ubuntu-latest
julia-arch: x64
julia-version: nightly
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@latest
Expand Down
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ authors = ["David van Leeuwen"]
version = "0.3.5"

[deps]
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
Expand All @@ -17,6 +18,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
WAV = "8149f6b0-98f6-5db9-b78f-408fbbb8ef88"

[compat]
Compat = "3.44, 4"
DSP = "0.6, 0.7"
Distributed = "1"
FFTW = "1"
Expand Down
1 change: 1 addition & 0 deletions src/MFCC.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ using DSP
using WAV
using SpecialFunctions ## erfinv
using Statistics
using Compat

include("rasta.jl")
include("mfccs.jl")
Expand Down
25 changes: 15 additions & 10 deletions src/mfccs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function mfcc(x::AbstractVector{T}, sr::Real=16000.0; wintime=0.025, steptime=0.
return (cepstra, pspec', meta)
end

mfcc(x::AbstractMatrix{<:AbstractFloat}, args...; kwargs...) = @distributed (tuple) for i in axes(x, 2) mfcc(x[:, i], args...; kwargs...) end
mfcc(x::AbstractMatrix{<:AbstractFloat}, args...; kwargs...) = @distributed (vcat) for i in axes(x, 2) mfcc(x[:, i], args...; kwargs...) end


## default feature configurations, :rasta, :htk, :spkid_toolkit, :wbspeaker
Expand Down Expand Up @@ -87,8 +87,12 @@ function deltas(x::AbstractMatrix{T}, w::Integer=9) where {T<:AbstractFloat}
return delta_v
end

sortperm_along(a::AbstractArray, dim::Integer) =
(v=similar(a, Int, size(a, dim)); mapslices(x->sortperm!(v, x), a; dims=dim))
function sortperm_along(a::AbstractArray, dim::Integer)
v = similar(a, Int, size(a, dim))
scr = similar(v)
kw = VERSION >= v"1.9" ? (; scratch=scr) : (;)
mapslices(x -> sortperm!(v, x; kw...), a; dims=dim)
end

@memoize Dict erfinvtab(wl::Integer) = @. 2 * erfinv(2(1:wl) / (wl + 1) - 1)

Expand Down Expand Up @@ -140,10 +144,9 @@ function znorm!(x::AbstractArray, dim::Integer=1)
end

## short-term mean and variance normalization
function stmvn(x::AbstractVector{T}, w::Integer=399) where T
y = similar(x, promote_type(Float64, T))
function stmvn!(out, x::AbstractVector{T}, w::Integer=399) where T
nx = length(x)
nx 1 || w <= 1 && return copy!(y, x)
nx 1 || w <= 1 && return copy!(out, x)
hw = w ÷ 2 ## effectively makes `w` odd...
len_w = min(nx, hw + 1)
v = Iterators.take(x, len_w)
Expand All @@ -154,19 +157,21 @@ function stmvn(x::AbstractVector{T}, w::Integer=399) where T
sx += (hw + 1 - nx) * last(x)
sxx += (hw + 1 - nx) * last(x)^2
end
for i in eachindex(x, y)
for i in eachindex(x, out)
μ = sx / w
σ = sqrt((sxx - μ * sx) / (w - 1))
y[i] = (x[i] - μ) / σ
out[i] = (x[i] - μ) / σ
mi = max(i - hw, firstindex(x))
ma = min(i + hw + 1, lastindex(x))
sx += x[ma] - x[mi]
sxx += x[ma]^2 - x[mi]^2
end
return y
return out
end

stmvn(m::AbstractMatrix, w::Integer=399, dim::Integer=1) = mapslices(x->stmvn(x, w), m; dims=dim)
stmvn(x::AbstractVector{T}, w::Integer=399) where T = stmvn!(similar(x, promote_type(Float64, T)), x, w)
stmvn(m::AbstractMatrix{T}, w::Integer=399, dim::Integer=1) where T =
(out = similar(m, promote_type(Float64, T), axes(m, 1)); mapslices(x -> stmvn!(out, x, w), m; dims=dim))

## Shifted Delta Coefficients by copying
function sdc(x::AbstractMatrix{T}, n::Integer=7, d::Integer=1, p::Integer=3, k::Integer=7) where {T<:AbstractFloat}
Expand Down
86 changes: 36 additions & 50 deletions src/rasta.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function powspec(x::AbstractVector{<:AbstractFloat}, sr::Real=8000.0;
noverlap = nwin - nstep

y = spectrogram(x .* (1<<15), nwin, noverlap, nfft=nfft, fs=sr, window=window, onesided=true).power
## for compability with previous specgram method, remove the last frequency and scale
## for compatibility with previous specgram method, remove the last frequency and scale
y = y[begin:end-1, :] ## * sumabs2(window) * sr / 2
y .+= dither * nwin / (sum(abs2, window) * sr / 2) ## OK with julia 0.5, 0.6 interpretation as broadcast!

Expand Down Expand Up @@ -282,73 +282,59 @@ function lifter(x::AbstractArray{<:AbstractFloat}, lift::Real=0.6, invs::Bool=fa
end

## Freely after octave's implementation, by Paul Kienzle <pkienzle@users.sf.net>
## Permission granted to usee this in a MIT license on 20 dec 2013 by the author Paul Kienzle:
## Permission granted to use this in a MIT license on 20 dec 2013 by the author Paul Kienzle:
## "You are welcome to move my octave code from GPL to MIT like core Julia."
## untested
## only returns a, v
@views function levinson(acf::AbstractVector{<:Number}, p::Int)
function levinson(acf::AbstractVector{<:Number}, p::Int)
if isempty(acf)
throw(ArgumentError("Empty autocorrelation function"))
elseif p < 0
throw(DomainError(p, "Negative model order"))
elseif p < 100
## direct solution [O(p^3), but no loops so slightly faster for small p]
## Kay & Marple Eqn (2.39)
R = toeplitz(acf[begin:begin+p-1])
a = R \ acf[begin+1:begin+p]
@. a = -a
pushfirst!(a, 1)
v = @. real(a*conj(acf[begin:begin+p]))
else
## durbin-levinson [O(p^2), so significantly faster for large p]
## Kay & Marple Eqns (2.42-2.46)
# ref = zeros(p)
g = -acf[begin+1] / acf[begin]
a = zeros(p+1); a[1] = 1; a[2] = g
buf = similar(a)
v = real((1 - abs2(g)) * acf[begin])
# ref[begin] = g # is not used???
for t in 2:p
g = -(acf[begin+t] + a[2:t] acf[begin+t-1:-1:begin+1]) / v
@. buf[2:t] = g * conj(a[t:-1:2])
@. a[2:t] += buf[2:t]
a[t+1] = g
v *= 1 - abs2(g)
# ref[t] = g
end
a, v = _durbin_levinson(acf, p)
end
return a, v
end

"""
Durbin-Levinson [O(p^2), so significantly faster for large p]
## Kay & Marple Eqns (2.42-2.46)
"""
@views function _durbin_levinson(acf::AbstractVector{<:Number}, p::Int)
g = -acf[begin+1] / acf[begin]
a = zeros(p + 1); a[1] = 1; a[2] = g
buf = similar(a)
v = real((1 - abs2(g)) * acf[begin])
# ref[begin] = g
for t in 2:p
g = -(acf[begin+t] + simple_dot(a[2:t], acf[begin+t-1:-1:begin+1])) / v
@. buf[2:t] = g * conj(a[t:-1:2])
@. a[2:t] += buf[2:t]
a[t+1] = g
v *= 1 - abs2(g)
# ref[t] = g
end
a, v
end

# for 1.6 compat (BLAS negative stride views bug)
function simple_dot(x::AbstractVector{T}, y::AbstractVector{V}) where {T,V}
val = zero(promote_type(T, V))
for i in eachindex(x, y)
val += x[i] * y[i]
end
val
end

function levinson(acf::AbstractMatrix{<:Number}, p::Integer)
nr, nc = size(acf)
a = zeros(p + 1, nc)
v = zeros(p + 1, nc)
v = zeros(1, nc)
for i in axes(acf, 2)
a_i, v_i = levinson(view(acf, :, i), p)
a[:, i] = a_i
v[:, i] .= v_i
v[i] = v_i
end
return a, v
end

## Freely after octave's implementation, ver 3.2.4, by jwe && jh
## toeplitz(c) is Hermitian with column c
## skipped sparse implementation
function toeplitz(c::AbstractVector{T}, r::AbstractVector{V}=conj(c)) where {T, V <: Number}
nc = length(c)
nr = length(r)
res = Matrix{promote_type(T, V)}(undef, nc, nr)
if iszero(nc) || iszero(nr)
return res
end
if first(r) != first(c)
@warn "First elements of a Toeplitz matrix should be equal."
end
## if issparse(c) && ispsparse(r)
data = [r[end:-1:begin+1]; c]
for j in axes(res, 2), i in axes(res, 1)
res[i, j] = data[nr-j+i]
end
return res
end
8 changes: 3 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ using WAV
using MFCC
using Test

import MFCC: sad, levinson, toeplitz
import MFCC: sad, levinson

x, meta, params = feacalc("bl2.wav", normtype=:none, method=:wav, augtype=:none, sadtype=:none)

Expand Down Expand Up @@ -80,12 +80,10 @@ end
@test_throws ArgumentError feacalc("bl2.wav", :bosespeaker)
@test_throws ArgumentError mfcc(y, sr, :pasta)
@test_throws ArgumentError postaud(y_mat, 4000, :cough)
@test_throws "Lift number is too high (>10)" lifter(y, 100)
@test_throws "Negative lift must be integer" lifter(y, -0.6)
@test_throws DomainError lifter(y, 100)
@test_throws DomainError lifter(y, -0.6)
@test_throws ArgumentError levinson(Int[], 1)
@test_throws DomainError levinson(x, -1)

@test_warn "First elements of a Toeplitz matrix should be equal." toeplitz([1 + im])
end

println("Tests passed")