diff --git a/.github/workflows/Tests.yml b/.github/workflows/Tests.yml index 594900c..f1d472d 100644 --- a/.github/workflows/Tests.yml +++ b/.github/workflows/Tests.yml @@ -18,8 +18,6 @@ jobs: matrix: version: - '1.3' - - '1.4' - - '1.5' - '1' - 'nightly' os: diff --git a/examples/waterfall_heatmap.jl b/examples/waterfall_heatmap.jl index 624c6ee..d79774a 100644 --- a/examples/waterfall_heatmap.jl +++ b/examples/waterfall_heatmap.jl @@ -1,6 +1,8 @@ using Makie using PortAudio using DSP +using LinearAlgebra +using FFTW """ Slide the values in the given matrix to the right by 1. @@ -16,52 +18,64 @@ end """ takes a block of audio, FFT it, and write it to the beginning of the buffer """ -function processbuf!(readbuf, win, dispbuf, fftbuf, fftplan) +function processbuf!(readbuf, win, dispbuf, fftbuf, fftplan; D = 200) readbuf .*= win - A_mul_B!(fftbuf, fftplan, readbuf) + mul!(fftbuf, fftplan, readbuf) shift1!(dispbuf) @. dispbuf[end:-1:1, 1] = log(clamp(abs(fftbuf[1:D]), 0.0001, Inf)) end -function processblock!(src, buf, win, dispbufs, fftbuf, fftplan) +function processblock!(src, buf, win, dispbufs, fftbuf, fftplan; D = 200) read!(src, buf) for dispbuf in dispbufs - processbuf!(buf, win, dispbuf, fftbuf, fftplan) + processbuf!(buf, win, dispbuf, fftbuf, fftplan; D = D) end end -N = 1024 # size of audio read -N2 = N ÷ 2 + 1 # size of rfft output -D = 200 # number of bins to display -M = 200 # amount of history to keep -src = PortAudioStream(1, 2) -buf = Array{Float32}(N) # buffer for reading -fftplan = plan_rfft(buf; flags = FFTW.EXHAUSTIVE) -fftbuf = Array{Complex{Float32}}(N2) # destination buf for FFT -dispbufs = [zeros(Float32, D, M) for i in 1:5, j in 1:5] # STFT bufs -win = gaussian(N, 0.125) +function waterfall_heatmap(seconds; + N = 1024, # size of audio read + D = 200, # number of bins to display + M = 200, # amount of history to keep +) + N2 = N ÷ 2 + 1 # size of rfft output + PortAudioStream(1, 2) do src + buf = Array{Float32}(undef, N) # buffer for reading + fftplan = plan_rfft(buf; flags = FFTW.EXHAUSTIVE) + fftbuf = Array{Complex{Float32}}(undef, N2) # destination buf for FFT + dispbufs = [zeros(Float32, D, M) for i in 1:5, j in 1:5] # STFT bufs + win = gaussian(N, 0.125) -scene = Scene(resolution = (1000, 1000)) + scene = Scene(resolution = (1000, 1000)) -#pre-fill the display buffer so we can do a reasonable colormap -for _ in 1:M - processblock!(src, buf, win, dispbufs, fftbuf, fftplan) -end + #pre-fill the display buffer so we can do a reasonable colormap + for _ in 1:M + processblock!(src, buf, win, dispbufs, fftbuf, fftplan; D = D) + end -heatmaps = map(enumerate(IndexCartesian(), dispbufs)) do ibuf - i = ibuf[1] - buf = ibuf[2] + heatmaps = map(zip(CartesianIndices(dispbufs), dispbufs)) do ibuf + i = ibuf[1] + buf = ibuf[2] - # some function of the 2D index and the value - heatmap(buf, offset = (i[2] * size(buf, 2), i[1] * size(buf, 1))) -end + # some function of the 2D index and the value + heatmap(buf, offset = (i[2] * size(buf, 2), i[1] * size(buf, 1))) + end -center!(scene) + center!(scene) -while isopen(scene[:screen]) - processblock!(src, buf, dispbufs, fftbuf, fftplan) - for (hm, db) in zip(heatmaps, dispbufs) - hm[:heatmap] = db + done = false + + @sync begin + @async while !done + processblock!(src, buf, win, dispbufs, fftbuf, fftplan) + for (hm, db) in zip(heatmaps, dispbufs) + hm[:heatmap] = db + end + render_frame(scene) + end + sleep(seconds) + done = true + end end - render_frame(scene) end + +waterfall_heatmap(5) \ No newline at end of file diff --git a/examples/waterfall_lines.jl b/examples/waterfall_lines.jl index d4578d2..4da9707 100644 --- a/examples/waterfall_lines.jl +++ b/examples/waterfall_lines.jl @@ -1,43 +1,57 @@ using Makie, GeometryTypes using PortAudio +using FFTW +using CairoMakie -N = 1024 # size of audio read -N2 = N ÷ 2 + 1 # size of rfft output -D = 200 # number of bins to display -M = 100 # number of lines to draw -S = 0.5 # motion speed of lines -src = PortAudioStream(1, 2) -buf = Array{Float32}(N) -fftbuf = Array{Complex{Float32}}(N2) -magbuf = Array{Float32}(N2) -fftplan = plan_rfft(buf; flags = FFTW.EXHAUSTIVE) +function waterfall_lines(seconds; + N = 1024, # size of audio read + D = 200, # number of bins to display + M = 100, # number of lines to draw + S = 0.5, # motion speed of lines +) + PortAudioStream(1, 2) do src + N2 = N ÷ 2 + 1 # size of rfft output + buf = Array{Float32}(undef, N) + fftbuf = Array{Complex{Float32}}(undef, N2) + magbuf = Array{Float32}(undef, N2) + fftplan = plan_rfft(buf; flags = FFTW.EXHAUSTIVE) -scene = Scene(resolution = (500, 500)) -ax = axis(0:0.1:1, 0:0.1:1, 0:0.1:0.5) -center!(scene) + scene = Scene(resolution = (500, 500)) + ax = axis(0:0.1:1, 0:0.1:1, 0:0.1:0.5) + center!(scene) -ls = map(1:M) do _ - yoffset = to_node(to_value(scene[:time])) - offset = lift_node(scene[:time], yoffset) do t, yoff - Point3f0(0.0f0, (t - yoff) * S, 0.0f0) - end - l = lines( - linspace(0, 1, D), - 0.0f0, - zeros(Float32, D), - offset = offset, - color = (:black, 0.1), - ) - (yoffset, l) -end - -while isopen(scene[:screen]) - for (yoffset, line) in ls - isopen(scene[:screen]) || break - read!(src, buf) - A_mul_B!(fftbuf, fftplan, buf) - @. magbuf = log(clamp(abs(fftbuf), 0.0001, Inf)) / 10 + 0.5 - line[:z] = magbuf[1:D] - push!(yoffset, to_value(scene[:time])) + ls = map(1:M) do _ + yoffset = to_node(to_value(scene[:time])) + offset = lift_node(scene[:time], yoffset) do t, yoff + Point3f0(0.0f0, (t - yoff) * S, 0.0f0) + end + l = lines( + linspace(0, 1, D), + 0.0f0, + zeros(Float32, D), + offset = offset, + color = (:black, 0.1), + ) + (yoffset, l) + end + done = false + @sync begin + @async begin + while !done + for (yoffset, line) in ls + isopen(scene[:screen]) || break + read!(src, buf) + A_mul_B!(fftbuf, fftplan, buf) + @. magbuf = log(clamp(abs(fftbuf), 0.0001, Inf)) / 10 + 0.5 + line[:z] = magbuf[1:D] + push!(yoffset, to_value(scene[:time])) + end + end + end + sleep(seconds) + done = true + end end end + +waterfall_lines(5) \ No newline at end of file