Większość metod analizy sygnału zakłada, że sygnał przez nie analizowany jest stacjonarny i nie zmienny w czasie. Jednak w przypadku analizy rzeczywistych nagrań dźwięku rzadko mamy styczność z sygnałami, które są stacjonarne przez cały czas ich trwania. Zauważono jednak, że można założyć, że każdy sygnał dla pewnego skończonego, krótkiego odcinka czasu, a nawet jeżeli do końca tak nie jest to wyniki powinny być zbieżne. Istnieje wiele norm i zaleceń w jaki sposób powinno dzielić się sygnał na ramki (te krótkie odcinki czasu), ale to rozległy temat, który nie będzie poruszony na dzisiejszych zajęciach. Poniżej zaprezentowano fragment kodu służący do dzielenia sygnału na okna trwające 20 ms. Każde okno oddalone jest od poprzedniego o 5 ms, czyli każde kolejne okno nakłada się na poprzednie w 3/4, co zapewnia pewną redundancję analizowanych danych. Okno o długości 20 ms pozwala nam rejestrować analizować sygnały o częstotliwościach większych niż 50 Hz, co wynika ze wzorów: \(f=\dfrac{1}{t} \quad t=\dfrac{1}{f}\), czyli \(f=\dfrac{1}{0.020}=\dfrac{100}{2}=50\).

data, fs = sf.read('SM_Lab05\sing_medium2.wav', dtype=np.float32)

window_time=20 # ms
window_hop_time=5 # ms

window_samples=np.round(window_time/1000*fs).astype(int)
window_hops=np.round(window_hop_time/1000*fs).astype(int)

for ix in range(0,data.shape[0]-window_samples+1,window_hops):
    frame=data[ix:ix+window_samples]
    print(ix/fs,frame.shape[0]/fs)

Analiza zawartości sygnału

Teraz zapoznajmy się z dwiema prostymi metodami służącymi do oceny zawartości sygnału. Każda z nich analizuje konkretną rankę sygnału i normalizują wartość przez jej długość. Pamiętajcie jednak, żeby stosować wybrane przez was funkcję okna przed wyliczeniem współczynnika.

Energia ramki

Pierwszy parametr to energia ramki. Wyliczmy to ją jako sumę kwadratów wartości w ramce.

def energy(frame):
    e=np.sum(frame**2)/frame.shape[0]
    return e

Zero Crossing Rate

Drugi parametr to Zero Crossing Rate (ZCR) - określa on nam jak często sygnał zmienia znak/przekracza wartość 0. Poniżej jedna z możliwych jego implementacji.

def zcr(frame):
    s=np.sign(frame)
    z=(np.sum(s[:-1]!=s[1:])/frame.shape[0])
    return z