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.slideFunction
slide(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)
source

The function f is called for each block with 3 arguments:

  • x: windowed view of the original array
  • blknum: block number
  • firstframe: 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.slideMethod
slide(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)
source

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.signalMethod
signal(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.

source
SignalAnalysis.slideFunction
slide(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)
source
SignalAnalysis.slideMethod
slide(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
source
SignalAnalysis.slideMethod
slide(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)
source
SignalAnalysis.toframeMethod
toframe(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
   ⋮
source
SignalAnalysis.@rateMacro
@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
source
SignalAnalysis.@samerateasMacro
@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
source
SignalAnalysis.@samerateasMacro
@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
source
Base.getindexMethod
x[a:b,∘]

Generates a view of specified row range a:b of a vector or matrix x.

source