Creating & managing signals
Signals are represented by array-like data types with a series (time, frequency or space domain) per column. The SampleBuf
data type wraps an array and carries sampling rate as metadata with it. This allows the API to be used without having to specify sampling rate at each call. However, if a user prefers to use other array-like data types, the sampling rate may be provided as a fs
keyword argument for API calls that require sampling rate.
All code examples in this manual assume you have imported SignalAnalysis
and SignalAnalysis.Units
:
using SignalAnalysis, SignalAnalysis.Units
The SignalAnalysis.Units
package re-exports commonly used units (s
, ms
, Hz
, kHz
, etc) from Unitful.jl
.
Creating and wrapping signals
Signals wrapped in a SampleBuf
data type may be easily created using signal()
:
x = signal(data, fs)
Properties such as frame rate (sampling rate), number of channels, etc may be accessed using the SignalBase
API. The signals can be treated as arrays, but carry sampling rate metadata with them. While most operations infer metadata from the input signal, some operations may be unable to automatically infer the frame rate of the output signal. In such cases, the @rate
and @samerateas
macros prove handy:
# create a 1-second signal sampled at 20 kHz
y = @rate 20kHz randn(20000)
# use DSP.filt() to filter a signal and set output signal sampling rate to be
# the same as y
y = @samerateas y filt(lpf, y)
# use DSP.resample() to resample a signal and set output signal sampling rate
# to be 2/3 that of y
y = @samerateas 2//3 y resample(y, 2//3)
Processing signals block-by-block
When processing long signals, it is common to break the signal into blocks, process each block, and collect the results. To simplify this, we have slide()
to slide a window along the data, and call a function on each window:
SignalAnalysis.slide
— Functionslide(f::Function, s::AbstractVector, nframes, overlap=0, args...; showprogress=true)
Slides a window over a signal, processing each window. If the total number of frames in the signal is not an integral multiple of nframes
, the last incomplete block of samples remains unprocessed.
The processing function receives a view on the original signal, and therefore may modify the signal if desired.
Examples:
julia> x = signal(ones(1000), 8kHz);
julia> slide(x, 250) do x1, blknum, firstframe
println(size(x1), ", ", blknum, ", ", firstframe)
end
(250,), 1, 1
(250,), 2, 251
(250,), 3, 501
(250,), 4, 751
julia> slide(x, 250) do x1, blknum, firstframe
x1 .= blknum
end
julia> x[1], x[251], x[501], x[751]
(1.0, 2.0, 3.0, 4.0)
The function f
is called for each block with 3 arguments:
x
: windowed view of the original arrayblknum
: block numberfirstframe
: frame number of the first frame in the window
The results can be optionally collated by providing the return data type as the second argument of slide()
:
SignalAnalysis.slide
— Methodslide(f::Function, ::Type{T}, s::AbstractVector, nframes, overlap=0, args...; showprogress=true) where T
Slides a window over a signal, processing each window, and collecting the results of type T
. If the total number of frames in the signal is not an integral multiple of nframes
, the last incomplete block of samples remains unprocessed.
Examples:
julia> x = signal(ones(1000), 8kHz);
julia> slide(Float32, x, 250) do x1, blknum, firstframe
sum(x1)*blknum
end
4-element Array{Float32,1}:
250.0
500.0
750.0
1000.0
julia> slide(Tuple{Int,Float64}, x, 250) do x1, blknum, firstframe
(blknum, sum(x1)*blknum)
end
4-element Array{Tuple{Int64,Float64},1}:
(1, 250.0)
(2, 500.0)
(3, 750.0)
(4, 1000.0)
Blocks may optionally also be overlapped by specifying noverlap
. Any extra arguments to slide()
are passed on to the f
function. An optional keyword argument showprogress
is useful in long running operations to automatically show a progress bar.
API reference
SignalAnalysis.analytic
— Methodanalytic(s)
Converts a signal to analytic representation.
SignalAnalysis.isanalytic
— Methodisanalytic(s)
Checks if a signal is analytic.
SignalAnalysis.padded
— Methodpadded(s, padding; delay, fill)
Generates a padded view of a signal with optional delay/advance.
SignalAnalysis.samples
— Methodsamples(s)
Gets the underlying samples in the signal.
SignalAnalysis.signal
— Methodsignal(x, fs)
Creates a signal with frame rate fs
.
SignalAnalysis.signal
— Methodsignal(x, fs)
Creates a signal with frame rate fs
. If the original signal's frame rate is the same as fs
, this method simply returns the original signal. Otherwise, it creates a new signal with the specified frame rate and data from the original signal. Do note that this method does not resample the signal.
SignalAnalysis.slide
— Functionslide(f::Function, s::AbstractVector, nframes, overlap=0, args...; showprogress=true)
Slides a window over a signal, processing each window. If the total number of frames in the signal is not an integral multiple of nframes
, the last incomplete block of samples remains unprocessed.
The processing function receives a view on the original signal, and therefore may modify the signal if desired.
Examples:
julia> x = signal(ones(1000), 8kHz);
julia> slide(x, 250) do x1, blknum, firstframe
println(size(x1), ", ", blknum, ", ", firstframe)
end
(250,), 1, 1
(250,), 2, 251
(250,), 3, 501
(250,), 4, 751
julia> slide(x, 250) do x1, blknum, firstframe
x1 .= blknum
end
julia> x[1], x[251], x[501], x[751]
(1.0, 2.0, 3.0, 4.0)
SignalAnalysis.slide
— Methodslide(f::Function, ::Type{Array{T}}, noutput::Int, s::AbstractVector, nframes, overlap=0, args...; showprogress=true) where T
Slides a window over a signal, processing each window, and collecting the results of type Array{T}
of length noutput
. If the total number of frames in the signal is not an integral multiple of nframes
, the last incomplete block of samples remains unprocessed.
Examples:
julia> x = signal(ones(1000), 8kHz);
julia> slide(Array{Float64}, 2, x, 250) do x1, blknum, firstframe
[sum(x1), prod(x1)]
end
4×2 Array{Float64,2}:
250.0 1.0
250.0 1.0
250.0 1.0
250.0 1.0
SignalAnalysis.slide
— Methodslide(f::Function, ::Type{T}, s::AbstractVector, nframes, overlap=0, args...; showprogress=true) where T
Slides a window over a signal, processing each window, and collecting the results of type T
. If the total number of frames in the signal is not an integral multiple of nframes
, the last incomplete block of samples remains unprocessed.
Examples:
julia> x = signal(ones(1000), 8kHz);
julia> slide(Float32, x, 250) do x1, blknum, firstframe
sum(x1)*blknum
end
4-element Array{Float32,1}:
250.0
500.0
750.0
1000.0
julia> slide(Tuple{Int,Float64}, x, 250) do x1, blknum, firstframe
(blknum, sum(x1)*blknum)
end
4-element Array{Tuple{Int64,Float64},1}:
(1, 250.0)
(2, 500.0)
(3, 750.0)
(4, 1000.0)
SignalAnalysis.toframe
— Methodtoframe(t, s)
Converts time to signal frame number.
Examples:
julia> x = signal(randn(2000), 8kHz);
julia> toframe(0.2s, x)
1601
julia> toframe([0.2s, 201ms], x)
2-element Array{Int64,1}:
1601
1609
julia> toframe(0.2:0.01:0.3, x)
11-element Array{Int64,1}:
1601
1681
1761
⋮
SignalAnalysis.@rate
— Macro@rate fs expr
Creates a signal from expr
with frame rate fs
. It provides syntactic sugar on the signal
method.
Example:
julia> x = @rate 44.1kHz randn(44100)
44100-frame, 1-channel SampleBuf{Float64, 1}
1.0s sampled at 44100.0Hz
SignalAnalysis.@samerateas
— Macro@samerateas n x expr
Creates a signal from expr
with the a frame rate n
times that of signal x
.
Examples:
julia> x = @rate 44.1kHz randn(44100)
44100-frame, 1-channel SampleBuf{Float64, 1}
1.0s sampled at 44100.0Hz
julia> y = @samerateas 1//2 x x[1:2:end]
22050-frame, 1-channel SampleBuf{Float64, 1}
1.0s sampled at 22050.0Hz
julia> using DSP
julia> y = @samerateas 2//3 x resample(x, 2//3)
29400-frame, 1-channel SampleBuf{Float64, 1}
1.0s sampled at 29400.0Hz
SignalAnalysis.@samerateas
— Macro@samerateas x expr
Creates a signal from expr
with the same frame rate as signal x
. This is useful to preserve frame rate metadata across functions that do not return a signal.
Examples:
julia> x = @rate 44.1kHz randn(44100)
44100-frame, 1-channel SampleBuf{Float64, 1}
1.0s sampled at 44100.0Hz
julia> using DSP
julia> y = filt([1.0,0.5], x) # frame rate stripped by DSP.filt
44100-element Array{Float64,1}:
⋮
julia> y = @samerateas x filt([1.0,0.5], x)
44100-frame, 1-channel SampleBuf{Float64, 1}
1.0s sampled at 44100.0Hz
Base.getindex
— Methodx[a:b,∘]
Generates a view of specified row range a:b
of a vector or matrix x
.