-
-
Save boylea/1a0b5442171f9afbf372 to your computer and use it in GitHub Desktop.
| """ | |
| Tested on Linux with python 3.7 | |
| Must have portaudio installed (e.g. dnf install portaudio-devel) | |
| pip install pyqtgraph pyaudio PyQt5 | |
| """ | |
| import numpy as np | |
| import pyqtgraph as pg | |
| import pyaudio | |
| from PyQt5 import QtCore, QtGui | |
| FS = 44100 #Hz | |
| CHUNKSZ = 1024 #samples | |
| class MicrophoneRecorder(): | |
| def __init__(self, signal): | |
| self.signal = signal | |
| self.p = pyaudio.PyAudio() | |
| self.stream = self.p.open(format=pyaudio.paInt16, | |
| channels=1, | |
| rate=FS, | |
| input=True, | |
| frames_per_buffer=CHUNKSZ) | |
| def read(self): | |
| data = self.stream.read(CHUNKSZ, exception_on_overflow=False) | |
| y = np.fromstring(data, 'int16') | |
| self.signal.emit(y) | |
| def close(self): | |
| self.stream.stop_stream() | |
| self.stream.close() | |
| self.p.terminate() | |
| class SpectrogramWidget(pg.PlotWidget): | |
| read_collected = QtCore.pyqtSignal(np.ndarray) | |
| def __init__(self): | |
| super(SpectrogramWidget, self).__init__() | |
| self.img = pg.ImageItem() | |
| self.addItem(self.img) | |
| self.img_array = np.zeros((1000, int(CHUNKSZ/2+1))) | |
| # bipolar colormap | |
| pos = np.array([0., 1., 0.5, 0.25, 0.75]) | |
| color = np.array([[0,255,255,255], [255,255,0,255], [0,0,0,255], (0, 0, 255, 255), (255, 0, 0, 255)], dtype=np.ubyte) | |
| cmap = pg.ColorMap(pos, color) | |
| lut = cmap.getLookupTable(0.0, 1.0, 256) | |
| # set colormap | |
| self.img.setLookupTable(lut) | |
| self.img.setLevels([-50,40]) | |
| # setup the correct scaling for y-axis | |
| freq = np.arange((CHUNKSZ/2)+1)/(float(CHUNKSZ)/FS) | |
| yscale = 1.0/(self.img_array.shape[1]/freq[-1]) | |
| self.img.scale((1./FS)*CHUNKSZ, yscale) | |
| self.setLabel('left', 'Frequency', units='Hz') | |
| # prepare window for later use | |
| self.win = np.hanning(CHUNKSZ) | |
| self.show() | |
| def update(self, chunk): | |
| # normalized, windowed frequencies in data chunk | |
| spec = np.fft.rfft(chunk*self.win) / CHUNKSZ | |
| # get magnitude | |
| psd = abs(spec) | |
| # convert to dB scale | |
| psd = 20 * np.log10(psd) | |
| # roll down one and replace leading edge with new data | |
| self.img_array = np.roll(self.img_array, -1, 0) | |
| self.img_array[-1:] = psd | |
| self.img.setImage(self.img_array, autoLevels=False) | |
| if __name__ == '__main__': | |
| app = QtGui.QApplication([]) | |
| w = SpectrogramWidget() | |
| w.read_collected.connect(w.update) | |
| mic = MicrophoneRecorder(w.read_collected) | |
| # time (seconds) between reads | |
| interval = FS/CHUNKSZ | |
| t = QtCore.QTimer() | |
| t.timeout.connect(mic.read) | |
| t.start(1000/interval) #QTimer takes ms | |
| app.exec_() | |
| mic.close() |
Does this still work? I installed pyqt5 but I get this error when I try to run it:
AttributeError: module 'PyQt5.QtGui' has no attribute 'QApplication'. Did you mean: 'QGuiApplication'?
Does this still work? I installed pyqt5 but I get this error when I try to run it:
AttributeError: module 'PyQt5.QtGui' has no attribute 'QApplication'. Did you mean: 'QGuiApplication'?
Yes, using 'QGuiApplication' fixed that error. I had a separate issue, getting a message:
Unable to locate theme engine in module_path: "adwaita"
...which cleared after installing gnome-themes-standard
If you find Qt5 is out of date, pip installing pyside2 will fix that, but it is a large download.
I tried QGuiApplication, but it didn't work. What got it working for me was switching to:
app = QtWidgets.QApplication([])
I'm having trouble with the scaling. Is this a Pyqt version problem?
File "livespec.py", line 142, in __init__
self.img.setScale((1./FS)*CHUNKSZ, yscale)
TypeError: setScale(self, scale: float): too many arguments
I also had this problem. I had to switch to using:
self.img.setTransform(QtGui.QTransform.fromScale((1./samplerate)*samplesize, yscale))
I'm not sure if it does the exact same thing but it seems to work.
Awesome tool. I am wondering if there is a way to an interactive color bar. Any help would be appreciated. Thanks
I discovered today after upgrading to matplotlib 3.7.1 to get colormaps like Turbo that I needed to update line 1084 in /usr/lib/python3/dist-packages/pyqtgraph/functions.py with the following
levels = levels.astype(float)instead of
levels = levels.astype(np.float)as it has been depreciated
looks great and works again

The photo is live audio from Dallas Love Field showing realtime spectrographs of aircraft taking off and their distinct audio signatures.