From 6b51e33ff272d480b4d19edb103524e79d7cf41b Mon Sep 17 00:00:00 2001 From: dezeyer Date: Sat, 11 May 2019 14:22:01 +0000 Subject: [PATCH] build the audiorecorder client --- .../Util/PyAudioRecorder.py | 130 ++++++++++++++++++ .../audiorecorder-pulse-udp/audiorecorder.py | 76 ++++++++++ 2 files changed, 206 insertions(+) create mode 100644 clients/audiorecorder-pulse-udp/Util/PyAudioRecorder.py create mode 100644 clients/audiorecorder-pulse-udp/audiorecorder.py diff --git a/clients/audiorecorder-pulse-udp/Util/PyAudioRecorder.py b/clients/audiorecorder-pulse-udp/Util/PyAudioRecorder.py new file mode 100644 index 0000000..79dff62 --- /dev/null +++ b/clients/audiorecorder-pulse-udp/Util/PyAudioRecorder.py @@ -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] diff --git a/clients/audiorecorder-pulse-udp/audiorecorder.py b/clients/audiorecorder-pulse-udp/audiorecorder.py new file mode 100644 index 0000000..4ac1d80 --- /dev/null +++ b/clients/audiorecorder-pulse-udp/audiorecorder.py @@ -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 +