Inbuilt and custom measurements on CF and FM segments

By default, a baic set of information/measurements is given for each recognised CF/FM segment in the input audio, its start, stop and duration.

Let’s begin by making a synthetic CF-FM call which looks a lot like a horseshoe/leaf nosed bat’s call

import matplotlib.pyplot as plt
import scipy.signal as signal
from itsfm.simulate_calls import make_cffm_call
from itsfm.view_horseshoebat_call import visualise_call
from itsfm.user_interface import segment_and_measure_call

Lets now create a sound that’s got only one CF and one FM component in it. Horseshoe/leaf nosed bats emit these kinds of calls too.

fs = 44100
call_props = {'cf':(8000, 0.01),
'upfm':(8000,0.002), # not that the 'upfm' frequency starts at the CF frequency!
'downfm':(100,0.003)}

cffm_call, freq_profile = make_cffm_call(call_props, fs)
cffm_call *= signal.tukey(cffm_call.size, 0.1)

w,s = visualise_call(cffm_call, fs, fft_size=64)

Now, segment and measure using the ‘peak pecentage’ method

output = segment_and_measure_call(cffm_call, fs,
                                  segment_method = 'peak_percentage',
                                  peak_percentage=0.95,
                                  window_size=44)
segment_info, call_parts, results, _ = output

If everything went well, the output should give us one CF and one FM component. The parameters may need to be tweaked based on the sampling rate and the amount of frequency modulation in the calls. This is true especially for sounds with a ‘curvature’ in the frequency profile, because sometimes the frequency change may be gradual and then become sudden, eg in the transition between CF and FM in this example call.

Another important aspect to notice is that the window size has been set to 44 samples. This corresponds to a short window of ~0.1ms. This short window size is used to compare the relative CF and FM emphasised dB rms profiles (see ‘The peak percentage method’).

print(results)

What if we want more than just the duration of each component? There are inbuilt functions such which allow the measurement of the rms, peak-amplitude, peak frequency and terminal frequency of each segment. Let’s get the peak frequency and peak amplitude for all segments

from itsfm.measurement_functions import measure_peak_frequency, measure_peak_amplitude

added_measures = [measure_peak_amplitude, measure_peak_frequency]

output = segment_and_measure_call(cffm_call, fs,
                                  peak_percentage=0.95,
                                  window_size=44,
                                  measurements=added_measures)

segment_info, call_parts, results, _ = output

print(results)

Now, what if this is not what we’re looking for and we needed to get, say, the dB peak amplitude? This calls for a custom measurement function. Each measurement function follows a particular pattern of three inputs and one output. See the measurement_function documentation or call it through the help

from itsfm import measurement_functions as measure_funcs
help(measure_funcs)

Let’s also take a look at the source code for one of the measurement functions we just used above measure_peak_amplitude:

import inspect
print(inspect.getsource(measure_peak_amplitude))

The output needs to be a dictionary with the measurement names and values in the keys and items respectively.

So, now let’s get the dB peak value of our audio segments

import numpy as np
from itsfm.signal_processing import dB

def measure_dBpeak(audio, fs, segment, **kwargs):
    relevant_audio = audio[segment]
    dB_peak_value = dB(np.max(np.abs(relevant_audio)))
    return {'dB_peak': dB_peak_value}


output = segment_and_measure_call(cffm_call, fs,
                                  peak_percentage=0.95,
                                  window_size=44,
                                  measurements=[measure_dBpeak])

segment_info, call_parts, results, _ = output

print(results)

So, looking at the dB peak value tells us that both CF and FM components are pretty strong, and of comparable levels. Both are close to 0 dB (re 1), which means they’re pretty close to the maximum signal value.

Just like the measure_dBpeak, we can chain a series of inbuilt or custom measurement functions in a list - and the outputs will all appear as a wide-formate Pandas DataFrame.

Total running time of the script: ( 0 minutes 0.000 seconds)

Gallery generated by Sphinx-Gallery