# -*- coding: utf-8 -*- """ Created on Sat Jan 22 20:06:20 2022 @author: timof """ import math as m import numpy as np from scipy.fftpack import * import time as t import sys import struct from pyqtgraph import * import pyqtgraph.opengl as gl from pyqtgraph.Qt import QtCore, QtGui import pyaudio as pa class Signal(object): def __init__(self, RATE = 44100, CHUNK = 1024, device = 'standard'): # pyaudio stuff self.p = pa.PyAudio() self.FORMAT = pa.paInt16 self.CHANNELS = 1 self.RATE = RATE self.CHUNK = CHUNK self.running = True if device=='standard': self.DEVICE = 1 else: for i in range(self.p.get_device_count()): if self.p.get_device_info_by_index(i)['name']==device: self.DEVICE = i print(self.DEVICE) #'Stereomix (Realtek HD Audio Stereo input)': # #'Microsoft Sound Mapper - Input': # #'Mikrofon (Realtek HD Audio Mic input)': self.stream = self.p.open( format=self.FORMAT, input_device_index=self.DEVICE, channels=self.CHANNELS, rate=self.RATE, input=True, output=True, frames_per_buffer=self.CHUNK, ) self.close = self.p.terminate class QTVisual(object): def __init__(self, RATE = 44100, CHUNK = 1024, device = 'standard' ): # pyqtgraph stuff setConfigOptions(antialias=True) self.traces = dict() self.app = QtGui.QApplication(sys.argv) self.win = GraphicsLayoutWidget(title='Audiogasm') self.win.setWindowTitle('Audiogasm') #self.win.setGeometry(0, 0, 1920, 1080) self.win.showMaximized() self.data_win = GraphicsLayoutWidget(title='Spectrum Analyzer') self.data_win.setWindowTitle('Spectrum Analyzer') #self.win.setGeometry(0, 0, 1920, 1080) self.data_win.showMaximized() xlabels = [(-1,'-1'),(-.5,'-0.5'),(0, '0'), (.5, '0.5'), (1, '1')] xaxis = pg.AxisItem(orientation='bottom') xaxis.setTicks([xlabels]) ylabels = [(-1,'-1'),(-.5,'-0.5'),(0, '0'), (.5, '0.5'), (1, '1')] yaxis = pg.AxisItem(orientation='left') yaxis.setTicks([ylabels]) wf_xlabels = [(0, '0'), (2048, '2048'), (4096, '4096')] wf_xaxis = pg.AxisItem(orientation='bottom') wf_xaxis.setTicks([wf_xlabels]) wf_ylabels = [(-127, '-128'), (0, '0'), (127, '128')] wf_yaxis = pg.AxisItem(orientation='left') wf_yaxis.setTicks([wf_ylabels]) sp_xlabels = [ (np.log10(10), '10'), (np.log10(100), '100'), (np.log10(440), '440'), (np.log10(1000), '1000'), (np.log10(22050), '22050') ] sp_xaxis = pg.AxisItem(orientation='bottom') sp_xaxis.setTicks([sp_xlabels]) self.waveform = self.data_win.addPlot( title='WAVEFORM', row=1, col=1, axisItems={'bottom': wf_xaxis, 'left': wf_yaxis}, ) self.spectrum = self.data_win.addPlot( title='SPECTRUM', row=2, col=1, axisItems={'bottom': sp_xaxis}, ) self.visual = self.win.addPlot(title='VISUAL', axisItems={'bottom': xaxis, 'left': yaxis}) self.RATE = RATE self.CHUNK = CHUNK self.device = device self.signal = Signal(RATE=self.RATE,CHUNK=self.CHUNK,device=self.device) self.CHUNK = self.signal.CHUNK self.tone = [[0,0]]*8 self.x = np.arange(0, 2 * self.CHUNK, 2) self.f = np.linspace(0, int(self.RATE / 2), int(self.CHUNK / 2)) self.l = np.linspace(0, 2*m.pi) self.xx = np.cos(self.l) self.yy = np.sin(self.l) def __start(self): if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): QtGui.QApplication.instance().exec_() def __plot_visual(self, name, data_x, data_y, freq=None): if name in self.traces: self.traces[name].setData(data_x, data_y) else: if name == 'waveform': self.traces[name] = self.waveform.plot(pen='c', width=3) self.waveform.setYRange(-128, 128, padding=0) self.waveform.setXRange(0, 2 * self.CHUNK, padding=0.005) if name == 'spectrum': self.traces[name] = self.spectrum.plot(pen='m', width=3) self.spectrum.setLogMode(x=True, y=False) self.spectrum.setYRange(0, 2, padding=0) self.spectrum.setXRange( np.log10(20), np.log10(self.RATE / 2), padding=0.005) if 'tone' in name: self.color = self.__get_color(freq) self.traces[name] = self.visual.plot(pen=mkPen(self.color, width=10)) self.visual.setYRange(-1, 1, padding=0) self.visual.setXRange(-1, 1, padding=0) def __get_color(self, frequency, gamma=0.8): lightfrequency = 0.5*np.log10(frequency/2)/2 wavelength = 300/lightfrequency '''steps of 10Hz: 22 means 220Hz''' '''This converts a given wavelength of light to an approximate RGB color value. The wavelength must be given in nanometers in the range from 380 nm through 750 nm (789 THz through 400 THz). Based on code by Dan Bruton http://www.physics.sfasu.edu/astro/color/spectra.html ''' wavelength = float(wavelength) if wavelength >= 380 and wavelength <= 440: attenuation = 0.3 + 0.7 * (wavelength - 380) / (440 - 380) R = ((-(wavelength - 440) / (440 - 380)) * attenuation) ** gamma G = 0.0 B = (1.0 * attenuation) ** gamma elif wavelength >= 440 and wavelength <= 490: R = 0.0 G = ((wavelength - 440) / (490 - 440)) ** gamma B = 1.0 elif wavelength >= 490 and wavelength <= 510: R = 0.0 G = 1.0 B = (-(wavelength - 510) / (510 - 490)) ** gamma elif wavelength >= 510 and wavelength <= 580: R = ((wavelength - 510) / (580 - 510)) ** gamma G = 1.0 B = 0.0 elif wavelength >= 580 and wavelength <= 645: R = 1.0 G = (-(wavelength - 645) / (645 - 580)) ** gamma B = 0.0 elif wavelength >= 645 and wavelength <= 750: attenuation = 0.3 + 0.7 * (750 - wavelength) / (750 - 645) R = (1.0 * attenuation) ** gamma G = 0.0 B = 0.0 else: R = 0.0 G = 0.0 B = 0.0 R *= 255 G *= 255 B *= 255 return (int(R), int(G), int(B)) def __update(self): audio_data = self.signal.stream.read(self.CHUNK) audio_data = struct.unpack(str(2 * self.CHUNK) + 'B', audio_data) audio_data = np.array(audio_data, dtype='b')[::2] sp_data = fft(np.array(audio_data, dtype='int8')) sp_data = np.abs(sp_data[0:int(self.CHUNK / 2)] ) * 2 / (128 * self.CHUNK) self.__plot_visual(name='waveform', data_x=self.x, data_y=audio_data,) self.__plot_visual(name='spectrum', data_x=self.f, data_y=sp_data) self.time = t.time() for key in range(0,8): self.tone[key][0] = self.xx*sum(sp_data[22*(key+1)-3:22*(key+1)+2])*np.sin(self.time*22*(key+1)/100) self.tone[key][1] = self.yy*sum(sp_data[22*(key+1)-3:22*(key+1)+2])*np.sin(self.time*22*(key+1)/100) self.__plot_visual(name='tone{}'.format(key), data_x=self.tone[key][0], data_y=self.tone[key][1], freq=(key+1)*220) def animation(self): timer = QtCore.QTimer() timer.timeout.connect(self.__update) timer.start(20) self.__start() if __name__ == '__main__': try: audio_app = QTVisual(RATE=48000, CHUNK=512*4, device='Mic Input (Traktor Kontrol S2 M' ) audio_app.animation() finally: audio_app.signal.close()