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

A few miscellaneous fixes #57

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 7 additions & 16 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,34 @@ uuid = "bd7594eb-a658-542f-9e75-4c4d8908c167"
version = "2.1.0"

[deps]
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2"
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
TreeViews = "a2a6695c-b41b-5b7d-aed9-dbfdeacea5d7"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[compat]
Compat = "2"
DSP = "0.6.1"
FFTW = "1.1.0"
FixedPointNumbers = "0.6.1"
IntervalSets = "0.3.2"
TreeViews = "0.3"
Unitful = "0.17.0"
julia = "1"
TreeViews = "0.3"

[extras]
DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
# LibSndFile = "b13ce0c6-77b0-50c6-a2db-140568b8d1a5"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
SampledSignals = "bd7594eb-a658-542f-9e75-4c4d8908c167"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
TreeViews = "a2a6695c-b41b-5b7d-aed9-dbfdeacea5d7"
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[targets]
test = ["DSP",
"FileIO",
"FixedPointNumbers",
# "LibSndFile",
"Unitful",
"Compat",
"SampledSignals",
"TreeViews",
"IntervalSets"]
test = ["Test", "DSP", "FileIO", "FixedPointNumbers", "Unitful", "SampledSignals", "TreeViews", "IntervalSets"]
155 changes: 63 additions & 92 deletions src/SampleBuf.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Base.Broadcast: Broadcasted, ArrayStyle

abstract type AbstractSampleBuf{T, N} <: AbstractArray{T, N} end

"""
Expand Down Expand Up @@ -48,11 +50,36 @@ SpectrumBuf(T::Type, sr, len::Quantity, ch) =
# frame - a collection of samples from each channel that were sampled simultaneously

# audio methods
"""
samplerate(x)

Returns the sampling rate of `x`
"""
samplerate(buf::AbstractSampleBuf) = buf.samplerate

"""
nchannels(x)

Returns the number of channels in the buffer or stream `x`.
"""
nchannels(buf::AbstractSampleBuf{T, 2}) where {T} = size(buf.data, 2)
nchannels(buf::AbstractSampleBuf{T, 1}) where {T} = 1

"""
nframes(x)

Returns the length of `x` in frames (time instants). Each frame may have
multiple channels.
"""
nframes(buf::AbstractSampleBuf) = size(buf.data, 1)

"""
samplerate!(buf, 44100)

Set the samplerate of `buf` without modifying the audio. In effect this speeds
up or slows down the signal, assuming it's played back at the original
samplerate.
"""
function samplerate!(buf::AbstractSampleBuf, sr)
buf.samplerate = sr

Expand All @@ -67,95 +94,38 @@ nchannels(arr::AbstractArray) = size(arr, 2)
# right type, instead of just a bare array
Base.similar(buf::SampleBuf, ::Type{T}, dims::Dims) where {T} = SampleBuf(Array{T}(undef, dims), samplerate(buf))
Base.similar(buf::SpectrumBuf, ::Type{T}, dims::Dims) where {T} = SpectrumBuf(Array{T}(undef, dims), samplerate(buf))
domain(buf::AbstractSampleBuf) = range(0.0, stop=(nframes(buf)-1)/samplerate(buf), length=nframes(buf))

# There's got to be a better way to define these functions, but the dispatch
# and broadcast behavior for AbstractArrays is complex and has subtle differences
# between Julia versions, so we basically just override functions here as they
# come up as problems
import Base: +, -, *, /
import Base.broadcast

const ArrayIsh = Union{Array, SubArray, Compat.LinRange, StepRangeLen}


# Broadcasting in Julia 0.7
# `find_buf` has borrowed from https://docs.julialang.org/en/latest/manual/interfaces/#Selecting-an-appropriate-output-array-1
if VERSION >= v"0.7.0-DEV-4936" # Julia PR 26891
find_buf(args::Tuple) = find_buf(find_buf(args[1]), Base.tail(args))
find_buf(x) = x
end # if VERSION

"""
domain(buf)

for btype in (:SampleBuf, :SpectrumBuf)

if VERSION >= v"0.7.0-DEV-4936" # Julia PR 26891

@eval find_buf(bc::Base.Broadcast.Broadcasted{Broadcast.ArrayStyle{$btype{T,N}}}) where {T,N} = find_buf(bc.args)
@eval find_buf(::$btype, args::Tuple{$btype}) = args[1]
@eval find_buf(::Any, args::Tuple{$btype}) = args[1]
@eval find_buf(a::$btype, rest) = a

@eval Base.BroadcastStyle(::Type{$btype{T,N}}) where {T,N} = Broadcast.ArrayStyle{$btype{T,N}}()
@eval function Base.similar(bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{$btype{T,N}}}, ::Type{ElType}) where {T,N,ElType}
A = find_buf(bc)
$btype(Array{ElType}(undef, length.(axes(bc))), samplerate(A))
end

else

# define non-broadcasting arithmetic
for op in (:+, :-)
@eval function $op(A1::$btype, A2::$btype)
if !isapprox(samplerate(A1), samplerate(A2))
error("samplerate-converting arithmetic not supported yet")
end
$btype($op(A1.data, A2.data), samplerate(A1))
end
@eval function $op(A1::$btype, A2::ArrayIsh)
$btype($op(A1.data, A2), samplerate(A1))
end
@eval function $op(A1::ArrayIsh, A2::$btype)
$btype($op(A1, A2.data), samplerate(A2))
end
end

# define non-broadcast scalar arithmetic
for op in (:+, :-, :*, :/)
@eval function $op(A1::$btype, a2::Number)
$btype($op(A1.data, a2), samplerate(A1))
end
@eval function $op(a1::Number, A2::$btype)
$btype($op(a1, A2.data), samplerate(A2))
end
end
Returns the range of time (for `SampleBuf`s) or frequency (for `SpectrumBuf`s)
corresponding to each sample of `buf`.
"""
domain(buf::AbstractSampleBuf) = range(0.0, stop=(nframes(buf)-1)/samplerate(buf), length=nframes(buf))

# define broadcasting application
@eval function broadcast(op, A1::$btype, A2::$btype)
if !isapprox(samplerate(A1), samplerate(A2))
error("samplerate-converting arithmetic not supported yet")
end
$btype(broadcast(op, A1.data, A2.data), samplerate(A1))
end
@eval function broadcast(op, A1::$btype, A2::ArrayIsh)
$btype(broadcast(op, A1.data, A2), samplerate(A1))
end
@eval function broadcast(op, A1::ArrayIsh, A2::$btype)
$btype(broadcast(op, A1, A2.data), samplerate(A2))
end
@eval function broadcast(op, a1::Number, A2::$btype)
$btype(broadcast(op, a1, A2.data), samplerate(A2))
end
@eval function broadcast(op, A1::$btype, a2::Number)
$btype(broadcast(op, A1.data, a2), samplerate(A1))
end
@eval function broadcast(op, A1::$btype)
$btype(broadcast(op, A1.data), samplerate(A1))
# inherit the samplerate from the first argument. TODO: we should probably
# throw an error if there are multiple SampleBufs with different sample rates.
for B in (SampleBuf, SpectrumBuf)
@eval Base.BroadcastStyle(::Type{<:$B}) = ArrayStyle{$B}()
@eval function Base.similar(bc::Broadcasted{ArrayStyle{$B}}, ::Type{T}) where T
srs = find_srs(bc)
@assert length(srs) >= 1
if any(!=(first(srs)), srs)
throw(ArgumentError("All samplerates in a broadcasting expression must match"))
end
$B(similar(Array{T}, axes(bc)), first(srs))
end
end

end # if VERSION
# to see more about customizing broadcasting see this:
# https://docs.julialang.org/en/latest/manual/interfaces/#Selecting-an-appropriate-output-array-1

end # for btype
find_srs(bc::Broadcasted) = find_srs(bc.args)
find_srs(args::Tuple) = (find_srs(first(args))...,
find_srs(Base.tail(args))...)
find_srs(buf::AbstractSampleBuf) = (samplerate(buf),)
find_srs(::Tuple{}) = ()
find_srs(::Any) = ()

typename(::SampleBuf{T, N}) where {T, N} = "SampleBuf{$T, $N}"
unitname(::SampleBuf) = "s"
Expand All @@ -166,6 +136,7 @@ srname(::SpectrumBuf) = "s"

# from @mbauman's Sparklines.jl package
const ticks = ['▁','▂','▃','▄','▅','▆','▇','█']

# 3-arg version (with explicit mimetype) is needed because we subtype AbstractArray,
# and there's a 3-arg version defined in show.jl
function show(io::IO, ::MIME"text/plain", buf::AbstractSampleBuf)
Expand All @@ -185,7 +156,7 @@ function showchannels(io::IO, buf::AbstractSampleBuf, widthchars=80)
for blk in 1:nblocks
i = (blk-1)*blockwidth + 1
n = min(blockwidth, nframes(buf)-i+1)
peaks = Compat.maximum(abs.(float.(buf[(1:n) .+ i .- 1, :])), dims=1)
peaks = maximum(abs.(float.(buf[(1:n) .+ i .- 1, :])), dims=1)
# clamp to -60dB, 0dB
peaks = clamp.(20log10.(peaks), -60.0, 0.0)
idxs = trunc.(Int, (peaks.+60)/60 * (length(ticks)-1)) .+ 1
Expand Down Expand Up @@ -319,45 +290,45 @@ FFTW.ifft(buf::SpectrumBuf) = SampleBuf(FFTW.ifft(buf.data), nframes(buf)/sample

# does a per-channel convolution on SampleBufs
for buftype in (:SampleBuf, :SpectrumBuf)
@eval function DSP.conv(b1::$buftype{T, 1}, b2::$buftype{T, 1}) where {T}
@eval function DSP.conv(b1::$buftype{T1, 1}, b2::$buftype{T2, 1}) where {T1, T2}
if !isapprox(samplerate(b1), samplerate(b2))
error("Resampling convolution not yet supported")
end
$buftype(conv(b1.data, b2.data), samplerate(b1))
end

@eval function DSP.conv(b1::$buftype{T, N1}, b2::$buftype{T, N2}) where {T, N1, N2}
@eval function DSP.conv(b1::$buftype{T1, N1}, b2::$buftype{T2, N2}) where {T1, T2, N1, N2}
if !isapprox(samplerate(b1), samplerate(b2))
error("Resampling convolution not yet supported")
end
if nchannels(b1) != nchannels(b2)
error("Broadcasting convolution not yet supported")
end
out = $buftype(T, samplerate(b1), nframes(b1)+nframes(b2)-1, nchannels(b1))
out = $buftype(promote_type(T1, T2), samplerate(b1), nframes(b1)+nframes(b2)-1, nchannels(b1))
for ch in 1:nchannels(b1)
out[:, ch] = conv(b1.data[:, ch], b2.data[:, ch])
end

out
end

@eval function DSP.conv(b1::$buftype{T, 1}, b2::StridedVector{T}) where {T}
@eval function DSP.conv(b1::$buftype{T1, 1}, b2::StridedVector{T2}) where {T1, T2}
$buftype(conv(b1.data, b2), samplerate(b1))
end

@eval DSP.conv(b1::StridedVector{T}, b2::$buftype{T, 1}) where {T} = conv(b2, b1)
@eval DSP.conv(b1::StridedVector{T1}, b2::$buftype{T2, 1}) where {T1, T2} = conv(b2, b1)

@eval function DSP.conv(b1::$buftype{T, 2}, b2::StridedMatrix{T}) where {T}
@eval function DSP.conv(b1::$buftype{T1, 2}, b2::StridedMatrix{T2}) where {T1, T2}
if nchannels(b1) != nchannels(b2)
error("Broadcasting convolution not yet supported")
end
out = $buftype(T, samplerate(b1), nframes(b1)+nframes(b2)-1, nchannels(b1))
out = $buftype(promote_type(T1, T2), samplerate(b1), nframes(b1)+nframes(b2)-1, nchannels(b1))
for ch in 1:nchannels(b1)
out[:, ch] = conv(b1.data[:, ch], b2[:, ch])
end

out
end

@eval DSP.conv(b1::StridedMatrix{T}, b2::$buftype{T, 2}) where {T} = conv(b2, b1)
@eval DSP.conv(b1::StridedMatrix{T1}, b2::$buftype{T2, 2}) where {T1, T2} = conv(b2, b1)
end
20 changes: 4 additions & 16 deletions src/SampledSignals.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# precompile is now the default
VERSION < v"0.7.0-rc2" && __precompile__()

module SampledSignals

using IntervalSets
Expand All @@ -25,20 +22,12 @@ using Unitful
using Unitful: ns, ms, µs, s, Hz, kHz, MHz, GHz, THz
using FixedPointNumbers
using DSP
using Compat
using Compat: AbstractRange, undef, range
using Compat.Random: randstring
using Compat.Base64: base64encode
using Random: randstring
using Base64: base64encode
using TreeViews: TreeViews

if VERSION >= v"0.7.0-DEV"
using LinearAlgebra: mul!
import FFTW
else
const mul! = A_mul_B!
import Compat.FFTW
end

using LinearAlgebra: mul!
import FFTW
import Base: show

const PCM8Sample = Fixed{Int8, 7}
Expand All @@ -53,7 +42,6 @@ include("SampleBuf.jl")
include("SampleStream.jl")
include("SignalGen/SinSource.jl")
include("WAVDisplay.jl")
include("deprecated.jl")

"""
metadata(x, key::Symbol)
Expand Down
Empty file removed src/deprecated.jl
Empty file.
3 changes: 1 addition & 2 deletions test/DummySampleStream.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Compat.Test
import Compat: undef
using Test

@testset "DummySampleStream Tests" begin
DEFAULT_SR = 48000
Expand Down
Loading