forked from IRL-CT/Interactive-Lab-Hub
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExampleAudioFFT.py
123 lines (87 loc) · 5.41 KB
/
ExampleAudioFFT.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import pyaudio
import numpy as np
from scipy.fft import rfft, rfftfreq
from scipy.signal.windows import hann
from numpy_ringbuffer import RingBuffer
import queue
import time
## Please change the following number so that it matches to the microphone that you are using.
DEVICE_INDEX = 1
## Compute the audio statistics every `UPDATE_INTERVAL` seconds.
UPDATE_INTERVAL = 1.0
### Things you probably don't need to change
FORMAT=np.float32
SAMPLING_RATE = 44100
CHANNELS=1
def main():
### Setting up all required software elements:
audioQueue = queue.Queue() #In this queue stores the incoming audio data before processing.
pyaudio_instance = pyaudio.PyAudio() #This is the AudioDriver that connects to the microphone for us.
def _callback(in_data, frame_count, time_info, status): # This "callbackfunction" stores the incoming audio data in the `audioQueue`
audioQueue.put(in_data)
return None, pyaudio.paContinue
stream = pyaudio_instance.open(input=True,start=False,format=pyaudio.paFloat32,channels=CHANNELS,rate=SAMPLING_RATE,frames_per_buffer=int(SAMPLING_RATE/2),stream_callback=_callback,input_device_index=DEVICE_INDEX)
# One essential way to keep track of variables overtime is with a ringbuffer.
# As an example the `AudioBuffer` it stores always the last second of audio data.
AudioBuffer = RingBuffer(capacity=SAMPLING_RATE*1, dtype=FORMAT) # 1 second long buffer.
# Another example is the `VolumeHistory` ringbuffer.
VolumeHistory = RingBuffer(capacity=int(20/UPDATE_INTERVAL), dtype=FORMAT) ## This is how you can compute a history to record changes over time
### Here is a good spot to extend other buffers aswell that keeps track of varailbes over a certain period of time.
nextTimeStamp = time.time()
stream.start_stream()
if True:
while True:
frames = audioQueue.get() #Get DataFrom the audioDriver (see _callbackfunction how the data arrives)
if not frames:
continue
framesData = np.frombuffer(frames, dtype=FORMAT)
AudioBuffer.extend(framesData[0::CHANNELS]) #Pick one audio channel and fill the ringbuffer.
if(AudioBuffer.is_full and # Waiting for the ringbuffer to be full at the beginning.
audioQueue.qsize()<2 and # Make sure there is not alot more new data that should be used.
time.time()>nextTimeStamp): # See `UPDATE_INTERVAL` above.
buffer = np.array(AudioBuffer) #Get the last second of audio.
volume = np.rint(np.sqrt(np.mean(buffer**2))*10000) # Compute the rms volume
VolumeHistory.append(volume)
volumneSlow = volume
volumechange = 0.0
if VolumeHistory.is_full:
HalfLength = int(np.round(VolumeHistory.maxlen/2))
vnew = np.array(VolumeHistory)[HalfLength:].mean()
vold = np.array(VolumeHistory)[:VolumeHistory.maxlen-HalfLength].mean()
volumechange =vnew-vold
volumneSlow = np.array(VolumeHistory).mean()
## Computes the Frequency Foruier analysis on the Audio Signal.
N = buffer.shape[0]
window = hann(N)
amplitudes = np.abs(rfft(buffer*window))[25:] #Contains the volume for the different frequency bin.
frequencies = (rfftfreq(N, 1/SAMPLING_RATE)[:N//2])[25:] #Contains the Hz frequency values. for the different frequency bin.
'''
Combining the `amplitudes` and `frequencies` varialbes allows you to understand how loud a certain frequency is.
e.g. If you'd like to know the volume for 500Hz you could do the following.
1. Find the frequency bin in which 500Hz belis closest to with:
FrequencyBin = np.abs(frequencies - 500).argmin()
2. Look up the volume in that bin:
amplitudes[FrequencyBin]
The example below does something similar, just in revers.
It finds the loudest amplitued and its coresponding bin with `argmax()`.
The uses the index to look up the Freqeucny value.
'''
# Threshold Detection
if volume > 85:
print("The volume is greater than 85:", volume)
# Running Average
if len(VolumeHistory) != 0:
print("The volume running average is", np.sum(VolumeHistory) / len(VolumeHistory))
# Peak Detection
if len(VolumeHistory) > 2:
peak_threshold = 5
if (VolumeHistory[-3] - VolumeHistory[-2] < -peak_threshold) and (VolumeHistory[-2] - VolumeHistory[-1] > peak_threshold):
print("Peak detected!")
LoudestFrequency = frequencies[amplitudes.argmax()]
print("Loudest Frequency:",LoudestFrequency)
print("RMS volume:",volumneSlow)
print("Volume Change:",volumechange)
nextTimeStamp = UPDATE_INTERVAL+time.time() # See `UPDATE_INTERVAL` above
if __name__ == '__main__':
main()
print("Something happened with the audio example. Stopping!")