The audiomath.Signal sub-module

This semi-independent submodule provides various basic signal-processing tools that can be used with or without the audiomath.Sound container.

audiomath.Signal.UnwrapDiff(x, base=6.283185307179586, axis=None, startval=None, dtype=None)

Assume X is a wrapped version of an underlying value Y we’re interested in. For example, it’s a 16-bit value that wraps around at 65536, or it’s an angle which wraps back to 0 at 2*pi.

base is the value (65536 or 2*pi in the above examples) such that X = Y % base. The default value of base is 2*pi.

Let dY be the numeric diff of Y in dimension axis, computed from X by unwrapping in order to avoid jumps larger than base/2. Thus, with base=65536, a jump from 65535 to 1 is considered as a step of +2. With base=360, a jump from 10 to 350 is considered as a step of -20.

Y is then reconstructed based on dY and startval (which defaults to the actual initial value(s) of X).

Return value is (dY,Y).

audiomath.Signal.msec2samples(msec, samplingfreq_hz)

Converts milliseconds to the nearest integer number of samples given the specified sampling frequency.

audiomath.Signal.samples2msec(samples, samplingfreq_hz)

Converts samples to milliseconds given the specified sampling frequency.

audiomath.Signal.ApplyWindow(s, func=<function Hann>, axis=0, **kwargs)

If s is a numpy.ndarray, return a windowed copy of the array. If s is an audiomath.Sound object (for example, if this is being used a method of that class), then its internal array s.y will be replaced by a windowed copy.

Windowing means multiplication by the specified window function, along the specified time axis.

func should take a single positional argument: length in samples. Additional **kwargs, if any, are passed through. Suitable examples include numpy.blackman, numpy.kaiser, and friends.

audiomath.Signal.ModulateAmplitude(s, freq_hz=1.0, phase_rad=None, phase_deg=None, amplitude=0.5, dc=0.5, samplingfreq_hz=None, duration_msec=None, duration_samples=None, axis=None, waveform=None, **kwargs)

If s is a numpy.ndarray, return a modulated copy of the array. If s is an audiomath.Sound object (for example, if this is being used a method of that class), then its internal array s.y will be replaced by a modulated copy.

Modulation means multiplication by the specified waveform, along the specified time axis.

Default phase is such that amplitude is 0 at time 0, which corresponds to phase_deg=-90 if waveform follows sine phase (remember: by default the modulator is a raised waveform, because dc=0.5 by default). To change phase, specify either phase_rad or phase_deg.

Uses GenerateWaveform()

audiomath.Signal.GenerateWaveform(container=None, freq_hz=1.0, phase_rad=None, phase_deg=None, amplitude=1.0, dc=0.0, samplingfreq_hz=None, duration_msec=None, duration_samples=None, axis=None, waveform=None, waveform_domain='auto', **kwargs)

Create a signal (or multiple signals, if the input arguments are arrays) which is a function of time (time being defined along the specified axis).

If this is being used as a method of an audiomath.Sound instance, then the container argument is automatically set to that instance. Otherwise (if used as a global function), the container argument is optional—if supplied, it should be a audiomath.Sound object. With a container, the axis argument is set to 0, and the container object’s sampling frequency number of channels and duration (if non-zero) are used as fallback values in case these are not specified elsewhere. The resulting signal is put into container.y and a reference to the container is returned.

Default phase is 0, but may be changed by either phase_deg or phase_rad (or both, as long as the values are consistent).

Default duration is 1000 msec, but may be changed by either duration_samples or duration_msec (or both, as long as the values are consistent).

If duration_samples is specified and samplingfreq_hz is not, then the sampling frequency is chosen such that the duration is 1 second—so then freq_hz can be interpreted as cycles per signal.

The default waveform function is numpy.cos which means that amplitude, phase and frequency arguments can be taken straight from the kind of dictionary returned by fft2ap() for an accurate reconstruction. A waveform function is assumed by default to take an input expressed in radians, unless the first argument in its signature is named cycles, samples, seconds or milliseconds, in which case the input argument is adjusted accordingly to achieve the named units. (To specify the units explicitly as one of these options, pass one of these words as the waveform_domain argument.)

In this module, SineWave(), SquareWave(), TriangleWave() and SawtoothWave() are all functions of cycles (i.e. the product of time and frequency), whereas Click() is a function of milliseconds. Any of these can be passed as the waveform argument.

audiomath.Signal.Click(milliseconds, positivePulseWidth=0.5, negativePulseWidth='symmetric')

A signal function that can be passed as the waveform argument to GenerateWaveform, to generate periodic clicks. Each click is a positive pulse optionally followed by a negative pulse. If negativePulseWidth is None or the string 'symmetric', then the width of the negative pulse is made the same as positivePulseWidth. Otherwise, input arguments are all expressed in milliseconds.

audiomath.Signal.SineWave(cycles, phase_deg=0, maxharm=None, rescale=False)

A sine wave, but with the input expressed in cycles (0 to 1) instead of radians (0 to 2*pi). Otherwise no different from numpy.sin, except that the function signature has maxharm and rescale arguments (which are ignored) for compatibility with SquareWave(), TriangleWave() and SawtoothWave().

This function can be passed as the waveform argument to GenerateWaveform.

audiomath.Signal.SquareWave(cycles, phase_deg=0, maxharm=None, rescale=False, duty=0.5, ramp=0, tol=1e-08)

A square wave with its peaks and troughs in sine phase. If maxharm is an integer, then an anti-aliased approximation to the square wave (containing no components of higher frequency than maxharm times the fundamental) is returned instead. In this case, the rescale flag can be set to ensure that the sampled waveform does not exceed +/- 1.0

This function can be passed as the waveform argument to GenerateWaveform.

audiomath.Signal.TriangleWave(cycles, phase_deg=0, maxharm=None, rescale=False)

A triangle wave with its peaks and troughs in sine phase. If maxharm is an integer, then an anti-aliased approximation to the triangle wave (containing no components of higher frequency than maxharm times the fundamental) is returned instead. The rescale flag, included for compatibility with SawtoothWave() and SquareWave(), has no effect.

This function can be passed as the waveform argument to GenerateWaveform.

audiomath.Signal.SawtoothWave(cycles, phase_deg=0, maxharm=None, rescale=False)

A sawtooth wave with its polarity and zero-crossings in sine phase. If maxharm is an integer, then an anti-aliased approximation to the sawtooth wave (containing no components of higher frequency than maxharm times the fundamental) is returned instead. In this case, the rescale flag can be set to ensure that the waveform does not exceed +/- 1.0

This function can be passed as the waveform argument to GenerateWaveform.

audiomath.Signal.Noise(cycles, distribution='uniform', **kwargs)

This function can be passed as the waveform argument to GenerateWaveform. The distribution argument should be a function, or the name of a function within numpy.random, that takes a size keyword argument dictating the shape of its output array. Additional **kwargs are passed through to the function.

If the function is numpy.random.uniform, then the low argument is set to -1.0 by default instead of the usual 0.0.

If the function is numpy.random.normal, then the scale argument is set to 0.2 by default instead of the usual 1.0.

audiomath.Signal.fftfreqs(nSamples, samplingfreq_hz=1.0)

Return a 1-D numpy.array of length nSamples containing the positive and negative frequency values corresponding to the elements of an nSamples-point FFT. If samplingfreq_hz is not supplied, 1.0 is assumed, so the result has 0.5 as its Nyquist frequency.

audiomath.Signal.fft2ap(X, samplingfreq_hz=2.0, axis=0)

Given discrete Fourier transform(s) X (with frequency along the specified axis), return a dict containing a properly scaled amplitude spectrum, a phase spectrum in degrees and in radians, and a frequency axis (coping with all the fiddly edge conditions).

The inverse of d = fft2ap(X) is X = ap2fft(**d)

audiomath.Signal.ap2fft(amplitude, phase_rad=None, phase_deg=None, samplingfreq_hz=2.0, axis=0, freq_hz=None, fullfreq_hz=None, nSamples=None)

Keyword arguments match the fields of the dict output by fft2ap().

The inverse of d = fft2ap(X) is X = ap2fft(**d)

audiomath.Signal.Reconstruct(ap, **kwargs)

Check the accuracy of fft2ap() and GenerateWaveform() by reconstructing a signal as the sum of cosine waves with amplitudes and phases specified in dict ap, which is of the form output by fft2ap().

audiomath.Signal.Toy(nSamples=11, nCycles=None, amplitude=(1.0, 0.1), phase_deg=0)

Toy sinusoidal signals for testing fft2ap() and ap2fft(). Check both odd and even nSamples.

audiomath.Signal.Shoulder(x, s, complement=False)

Return a (possibly asymmetric) Tukey window function of x. s may have 1, 2, 3 or 4 elements:

  1. raised cosine between x=s[0]-0.5 and x=s[0]+0.5
  2. raised cosine between x=s[0] and x=s[1]
  3. raised cosine rise from s[0] to s[1], and fall from s[1] to s[2]
  4. raised cosine rise from s[0] to s[1], plateau from s[1] to s[2], and fall from s[2] to s[3]
audiomath.Signal.Hilbert(x, N=None, axis=0, band=(), samplingfreq_hz=None, return_dict=False)

Compute the analytic signal, just like scipy.signal.hilbert but with the differences that (a) the computation can be performed along any axis and (b) a limited band of the signal may be considered. The band argument can be a two-, three- or four-element tuple suitable for passing to Shoulder(), specifying the edges of the passband (expressed in Hz if samplingfreq_hz is explicitly supplied, or relative to Nyquist if not).

If return_dict is True, do not return just the complex analytic signal but rather a dict containing its amplitude, phase, and unwrapped phase difference.

audiomath.Signal.Spectrum(x, samplingfreq_hz=None, axis=None)

Runs fft on a signal followed by fft2ap, to return spectral information in “human-readable” format with the annoying corner cases handled.

The inverse operation is InverseSpectrum().

audiomath.Signal.InverseSpectrum(ap, real=True)

Inverse of Spectrum(). Takes the dict output of Spectrum() (or of the underlying fft2ap()) and runs ap2fft() followed by ifft() on it to return a signal. If real is true, which it is by default, discard the imaginary part of the result.

audiomath.Signal.ReweightSpectrum(s, func, *pargs, **kwargs)

Brutally filter the sound s by transforming into the Fourier domain, weighting the amplitude spectrum, and transforming back. The filtering is non-causal and (TODO) will have wrap-around artifacts whereby the two ends of the signal may bleed into each other, so use with caution—it is most suitable for noise or for periodic signals. No group delay is introduced.

Args:
s (numpy.ndarray or audiomath.Sound):
The input sound data. If s is an array, a new array will be returned. If it is a Sound instance, s will get changed in-place (the array s.y will be replaced by a new array) and returned.
func (callable):
A function that takes a numeric array of frequencies, in Hz, as its first (and only required) argument, and outputs a corresponding array of weights.
samplingfreq_hz (float):
Sampling frequencym in Hz. Not needed if s is a Sound instance.
axis (int):
Axis along which time runs. Not needed if s is a Sound instance.

Additional *pargs and **kwargs are passed straight through to func().

audiomath.Signal.Timebase(x, samplingfreq_hz=None, axis=None)

Return a discrete time axis in seconds, against which signal x can be plotted. x may be an audiomath.Sound instance, or you may supply samplingfreq_hz explicitly.

audiomath.Signal.PlotSignal(x, samplingfreq_hz=None, axis=None, **kwargs)

Plot x (an array with time running along the specified axis, or an audiomath.Sound instance) against the appropriate Timebase().

audiomath.Signal.PlotSpectrum(x, samplingfreq_hz=None, axis=None, dB=False, baseValue=None, **kwargs)

Input argument x may be a numpy.array or audiomath.Sound, in which case Spectrum() will be called on it. Or it may be the dict output of a previous Spectrum() or fft2ap() call.