From 207ec25f1ebdfbe2094b190b447380547d670feb Mon Sep 17 00:00:00 2001 From: Spencer Russell Date: Wed, 27 Feb 2019 09:03:48 -0500 Subject: [PATCH] adds some examples of scrolling spctrograms with Makie. Probably with an old version of Makie --- examples/waterfall_heatmap.jl | 67 +++++++++++++++++++++++++++++++++++ examples/waterfall_lines.jl | 38 ++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 examples/waterfall_heatmap.jl create mode 100644 examples/waterfall_lines.jl diff --git a/examples/waterfall_heatmap.jl b/examples/waterfall_heatmap.jl new file mode 100644 index 0000000..7076f93 --- /dev/null +++ b/examples/waterfall_heatmap.jl @@ -0,0 +1,67 @@ +using Makie +using PortAudio +using DSP + +""" +Slide the values in the given matrix to the right by 1. +The rightmosts column is discarded and the leftmost column is +left alone. +""" +function shift1!(buf::AbstractMatrix) + for col in size(buf,2):-1:2 + @. buf[:, col] = buf[:, col-1] + end +end + +""" +takes a block of audio, FFT it, and write it to the beginning of the buffer +""" +function processbuf!(readbuf, win, dispbuf, fftbuf, fftplan) + readbuf .*= win + A_mul_B!(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) + read!(src, buf) + for dispbuf in dispbufs + processbuf!(buf, win, dispbuf, fftbuf, fftplan) + 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, blocksize=N) +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) + +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 + +heatmaps = map(enumerate(IndexCartesian(), 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 + +center!(scene) + +while isopen(scene[:screen]) + processblock!(src, buf, dispbufs, fftbuf, fftplan) + for (hm, db) in zip(heatmaps, dispbufs) + hm[:heatmap] = db + end + render_frame(scene) +end diff --git a/examples/waterfall_lines.jl b/examples/waterfall_lines.jl new file mode 100644 index 0000000..bce3b4a --- /dev/null +++ b/examples/waterfall_lines.jl @@ -0,0 +1,38 @@ +using Makie, GeometryTypes +using PortAudio + +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, blocksize=N) +buf = Array{Float32}(N) +fftbuf = Array{Complex{Float32}}(N2) +magbuf = Array{Float32}(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) + +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])) + end +end