Phase Vocoder

This page illustrates the phase vocoder feature of the Julia package Sound.

This page comes from a single Julia file: 02-vocoder.jl.

You can access the source code for such Julia documentation using the 'Edit on GitHub' link in the top right. You can view the corresponding notebook in nbviewer here: 02-vocoder.ipynb, or open it in binder here: 02-vocoder.ipynb.

Setup

Packages needed here.

using Sound: phase_vocoder, soundsc, hann
using DSP: spectrogram
using MIRTjim: jim, prompt
using Plots: plot
using InteractiveUtils: versioninfo

The following line is helpful when running this file as a script; this way it will prompt user to hit a key after each figure is displayed.

isinteractive() ? jim(:prompt, true) : prompt(:draw);

Overview

Here we illustrate applying the phase vocoder to stretch time (by the default factor of 2).

S = 8192*2^2
x1 = cos.(2π*400*(1:2S)/S) .* hann(2S)
x2 = cos.(2π*300*(1:S)/S) .* hann(S)
x3 = cos.(2π*500*(1:S)/S).^3 .* hann(S) # nonlinearity for harmonics
x = [x1; x2; x3] # 4-note song
y = phase_vocoder(x, S)
length(x), length(y)
(131072, 262144)

spectrograms

sx = spectrogram(x / maximum(abs, x); fs=S)
sy = spectrogram(y / maximum(abs, y); fs=S)
dB = x -> max(log10.(x), -10)
fun = (s) -> jim(s.time, s.freq, dB.(s.power)', aspect_ratio = :auto,
    color = :viridis, clim = (-10,0), yflip = false,
    yaxis = ("freq", (0,1800), 0:300:1800),
    xlabel="time",
    prompt = false,
)
p0 = jim(fun(sx), fun(sy))
Example block output

listen to before/after:

isinteractive() && soundsc([x; y], S)
false

The following time plots show the original signal and the time-stretched version. Clearly something is awry.

p1 = plot((1:length(x))/S, x, label="x(t) original",
 xaxis = ("t [s]", (0, 4), 0:4),
 yaxis = ("", (-1, 1), -1:1),
 size = (600, 300),
)
ymax = ceil(maximum(abs, y)) # why not "1" ?
p2 = plot((1:length(y))/S, y, label="y(t) processed",
 xaxis = ("t [s]", (0, 8), 0:2:8),
 yaxis = ("", (-1, 1).*ymax, (-1:1)*ymax),
 size = (600, 300),
)
p12 = plot(p1, p2; layout=(2,1))
Example block output

Reproducibility

This page was generated with the following version of Julia:

using InteractiveUtils: versioninfo
io = IOBuffer(); versioninfo(io); split(String(take!(io)), '\n')
12-element Vector{SubString{String}}:
 "Julia Version 1.10.2"
 "Commit bd47eca2c8a (2024-03-01 10:14 UTC)"
 "Build Info:"
 "  Official https://julialang.org/ release"
 "Platform Info:"
 "  OS: Linux (x86_64-linux-gnu)"
 "  CPU: 4 × AMD EPYC 7763 64-Core Processor"
 "  WORD_SIZE: 64"
 "  LIBM: libopenlibm"
 "  LLVM: libLLVM-15.0.7 (ORCJIT, znver3)"
 "Threads: 1 default, 0 interactive, 1 GC (on 4 virtual cores)"
 ""

And with the following package versions

import Pkg; Pkg.status()
Status `~/work/Sound.jl/Sound.jl/docs/Project.toml`
  [717857b8] DSP v0.7.9
  [e30172f5] Documenter v1.4.0
  [98b081ad] Literate v2.18.0
  [170b2178] MIRTjim v0.23.0
  [91a5bcdd] Plots v1.40.4
  [bd7594eb] SampledSignals v2.1.4
  [afe32e48] Sound v0.5.0 `~/work/Sound.jl/Sound.jl`
  [1986cc42] Unitful v1.19.0
  [b77e0a4c] InteractiveUtils

This page was generated using Literate.jl.