build the audiorecorder client
This commit is contained in:
parent
27a7fb73d9
commit
6b51e33ff2
130
clients/audiorecorder-pulse-udp/Util/PyAudioRecorder.py
Normal file
130
clients/audiorecorder-pulse-udp/Util/PyAudioRecorder.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#
|
||||||
|
# The idea is to have only one thread accessing the audio input source instead
|
||||||
|
# of every music enabled thread itself. also, different fuctions calculating
|
||||||
|
# frequencys and so on shoud run in own threads to the leds get more updates
|
||||||
|
#
|
||||||
|
# in history one thread was calculating bpm, freqence average and max values in one thread
|
||||||
|
# before updating the leds, what the pi was a bit slow for and you cloud count every update
|
||||||
|
# of the leds
|
||||||
|
|
||||||
|
# i think most of it is from https://github.com/shunfu/python-beat-detector/
|
||||||
|
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
import pyaudio
|
||||||
|
import threading
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class PyAudioRecorder:
|
||||||
|
"""Simple, cross-platform class to record from the default input device."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.RATE = 44100
|
||||||
|
self.BUFFERSIZE = 2**12
|
||||||
|
self.secToRecord = .1
|
||||||
|
self.kill_threads = False
|
||||||
|
self.has_new_audio = False
|
||||||
|
self.setup()
|
||||||
|
|
||||||
|
# since the server only can handly one input (for now) this thread will calculate
|
||||||
|
# some basic things for the musicEffectsThreads, so they can update the leds more often.
|
||||||
|
# it is always posible to catch the fft() function and do your own thing in your musikEffect.
|
||||||
|
|
||||||
|
# calculate bpm, this is the same for all clientThreads
|
||||||
|
self.beats_idx = 0
|
||||||
|
|
||||||
|
self.bpm_list = []
|
||||||
|
self.prev_beat = time.perf_counter()
|
||||||
|
self.low_freq_avg_list = []
|
||||||
|
|
||||||
|
self.lastTime = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
self.recorderClients = []
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
self.buffers_to_record = int(
|
||||||
|
self.RATE * self.secToRecord / self.BUFFERSIZE)
|
||||||
|
if self.buffers_to_record == 0:
|
||||||
|
self.buffers_to_record = 1
|
||||||
|
self.samples_to_record = int(self.BUFFERSIZE * self.buffers_to_record)
|
||||||
|
self.chunks_to_record = int(self.samples_to_record / self.BUFFERSIZE)
|
||||||
|
self.sec_per_point = 1. / self.RATE
|
||||||
|
|
||||||
|
self.p = pyaudio.PyAudio()
|
||||||
|
# start the PyAudio class
|
||||||
|
|
||||||
|
# make sure the default input device is broadcasting the speaker output
|
||||||
|
# there are a few ways to do this
|
||||||
|
# e.g., stereo mix, VB audio cable for windows, soundflower for mac
|
||||||
|
self.in_stream = self.p.open(format=pyaudio.paInt16,
|
||||||
|
channels=1,
|
||||||
|
rate=self.RATE,
|
||||||
|
input=True,
|
||||||
|
frames_per_buffer=self.BUFFERSIZE)
|
||||||
|
print("Using default input device: {:s}".format(
|
||||||
|
self.p.get_default_input_device_info()['name']))
|
||||||
|
|
||||||
|
self.audio = numpy.empty(
|
||||||
|
(self.chunks_to_record * self.BUFFERSIZE), dtype=numpy.int16)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
print("pyAudioRecorder closed")
|
||||||
|
self.kill_threads = True
|
||||||
|
self.p.close(self.in_stream)
|
||||||
|
|
||||||
|
### RECORDING AUDIO ###
|
||||||
|
|
||||||
|
def get_audio(self):
|
||||||
|
"""get a single buffer size worth of audio."""
|
||||||
|
audio_string = self.in_stream.read(self.BUFFERSIZE)
|
||||||
|
return numpy.fromstring(audio_string, dtype=numpy.int16)
|
||||||
|
|
||||||
|
def recordo(self):
|
||||||
|
while not self.kill_threads:
|
||||||
|
for i in range(self.chunks_to_record):
|
||||||
|
self.audio[i*self.BUFFERSIZE:(i+1)
|
||||||
|
* self.BUFFERSIZE] = self.get_audio()
|
||||||
|
self.has_new_audio = True
|
||||||
|
|
||||||
|
def record(self):
|
||||||
|
#while not self.kill_threads:
|
||||||
|
for i in range(self.chunks_to_record):
|
||||||
|
self.audio[i*self.BUFFERSIZE:(i+1)
|
||||||
|
* self.BUFFERSIZE] = self.get_audio()
|
||||||
|
#self.has_new_audio = True
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
print("pyAudioRecorder started")
|
||||||
|
self.t = threading.Thread(target=self.record)
|
||||||
|
self.t.start()
|
||||||
|
|
||||||
|
### MATH ###
|
||||||
|
|
||||||
|
def downsample(self, data, mult):
|
||||||
|
"""Given 1D data, return the binned average."""
|
||||||
|
overhang = len(data) % mult
|
||||||
|
if overhang:
|
||||||
|
data = data[:-overhang]
|
||||||
|
data = numpy.reshape(data, (len(data) / mult, mult))
|
||||||
|
data = numpy.average(data, 1)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def fft(self, data=None, trim_by=20, log_scale=False, div_by=10000):
|
||||||
|
self.record()
|
||||||
|
if not data:
|
||||||
|
data = self.audio.flatten()
|
||||||
|
left, right = numpy.split(numpy.abs(numpy.fft.fft(data)), 2)
|
||||||
|
ys = numpy.add(left, right[::-1])
|
||||||
|
if log_scale:
|
||||||
|
ys = numpy.multiply(20, numpy.log10(ys))
|
||||||
|
xs = numpy.arange(self.BUFFERSIZE/2, dtype=float)
|
||||||
|
if trim_by:
|
||||||
|
i = int((self.BUFFERSIZE/2) / trim_by)
|
||||||
|
ys = ys[:i]
|
||||||
|
xs = xs[:i] * self.RATE / self.BUFFERSIZE
|
||||||
|
if div_by:
|
||||||
|
ys = ys / float(div_by)
|
||||||
|
return [xs,ys]
|
76
clients/audiorecorder-pulse-udp/audiorecorder.py
Normal file
76
clients/audiorecorder-pulse-udp/audiorecorder.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
from Util.PyAudioRecorder import PyAudioRecorder
|
||||||
|
from random import randint
|
||||||
|
import time
|
||||||
|
import socket
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
remoteaddr = ("0.0.0.0",8002)
|
||||||
|
|
||||||
|
# PyAudioRecorder uses the default
|
||||||
|
pyAudioRecorder: PyAudioRecorder = PyAudioRecorder()
|
||||||
|
#pyAudioRecorder.start()
|
||||||
|
print(pyAudioRecorder.p.get_default_input_device_info())
|
||||||
|
lastping = time.time()-1
|
||||||
|
lastupdate = time.time()-1
|
||||||
|
localaddr = ("",randint(6000,7000))
|
||||||
|
|
||||||
|
udpsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
udpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
|
udpsocket.settimeout(.002)
|
||||||
|
|
||||||
|
registered:bool = False
|
||||||
|
|
||||||
|
while True:
|
||||||
|
#print(time.time() - self.lastping)
|
||||||
|
if time.time() - lastping > .5 :
|
||||||
|
udpsocket.sendto("s:ping".encode(), remoteaddr)
|
||||||
|
lastping = time.time()
|
||||||
|
ftt = pyAudioRecorder.fft()
|
||||||
|
#print(ftt)
|
||||||
|
|
||||||
|
|
||||||
|
if time.time() - lastupdate > 0.00833333333 and registered:
|
||||||
|
#if pyAudioRecorder.has_new_audio:
|
||||||
|
# there must be a better solution to do this.
|
||||||
|
# this looks like my silly arduino code, but it works
|
||||||
|
values: str = "d:xs"
|
||||||
|
i = 0
|
||||||
|
j = 0
|
||||||
|
udpsocket.sendto(("dlen:"+str(len(ftt[0]))+":"+str(len(ftt[1]))+"").encode(), remoteaddr)
|
||||||
|
for value in ftt[0]:
|
||||||
|
values = values+":"+str(j)+"|"+str(value)
|
||||||
|
i = i+1
|
||||||
|
j = j+1
|
||||||
|
if i == 20:
|
||||||
|
udpsocket.sendto(values.encode(), remoteaddr)
|
||||||
|
i = 0
|
||||||
|
values= "d:xs"
|
||||||
|
|
||||||
|
|
||||||
|
values = "d:ys"
|
||||||
|
i = 0
|
||||||
|
j = 0
|
||||||
|
for value in ftt[1]:
|
||||||
|
values = values+":"+str(j)+"|"+str(value)
|
||||||
|
i = i+1
|
||||||
|
j = j+1
|
||||||
|
if i == 20:
|
||||||
|
udpsocket.sendto(values.encode(), remoteaddr)
|
||||||
|
i = 0
|
||||||
|
values= "d:ys"
|
||||||
|
|
||||||
|
|
||||||
|
udpsocket.sendto("d:c".encode(), remoteaddr)
|
||||||
|
lastupdate = time.time()
|
||||||
|
|
||||||
|
try:
|
||||||
|
rbytes, address = udpsocket.recvfrom(4096)
|
||||||
|
remoteaddr = address
|
||||||
|
data = rbytes.decode('UTF-8')
|
||||||
|
if data:
|
||||||
|
if data[0] is "s" and data[1] is "r":
|
||||||
|
registered = True
|
||||||
|
udpsocket.sendto(("r:2:"+pyAudioRecorder.p.get_default_input_device_info()['name']+"").encode(), address)
|
||||||
|
except socket.timeout:
|
||||||
|
pass
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user