[PyQt] Assign Label Text as number in Hz - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [PyQt] Assign Label Text as number in Hz (/thread-11276.html) Pages:
1
2
|
Assign Label Text as number in Hz - mekha - Jul-01-2018 I'm totally new to this sound-involving programming, so I have this entrusted project to me, and they want my sound visualizer program can also showing frequency detail of the sound (in realtime) in number (Hz). I have to put this number (Hz) info alone like in their own window, not in the same as my sound visualizer graph (using graph in PyQt4 promoted to pyqtgraph). This code below is the sound graph import pyaudio import time import numpy as np import threading def getFFT(data,rate): """Given some data and rate, returns FFTfreq and FFT (half).""" data=data*np.hamming(len(data)) fft=np.fft.fft(data) fft=np.abs(fft) #fft=10*np.log10(fft) freq=np.fft.fftfreq(len(fft),1.0/rate) return freq[:int(len(freq)/2)],fft[:int(len(fft)/2)] class SWHear(): def __init__(self,device=None,rate=None,updatesPerSecond=10): self.p=pyaudio.PyAudio() self.chunk=4096 # gets replaced automatically self.updatesPerSecond=updatesPerSecond self.chunksRead=0 self.device=device self.rate=rate ### SYSTEM TESTS def valid_low_rate(self,device): """set the rate to the lowest supported audio rate.""" for testrate in [44100]: if self.valid_test(device,testrate): return testrate print("SOMETHING'S WRONG! I can't figure out how to use DEV",device) return None def valid_test(self,device,rate=44100): """given a device ID and a rate, return TRUE/False if it's valid.""" try: self.info=self.p.get_device_info_by_index(device) if not self.info["maxInputChannels"]>0: return False stream=self.p.open(format=pyaudio.paInt16,channels=1, input_device_index=device,frames_per_buffer=self.chunk, rate=int(self.info["defaultSampleRate"]),input=True) stream.close() return True except: return False def valid_input_devices(self): """ See which devices can be opened for microphone input. call this when no PyAudio object is loaded. """ mics=[] for device in range(self.p.get_device_count()): if self.valid_test(device): mics.append(device) if len(mics)==0: print("no microphone devices found!") else: print("found %d microphone devices: %s"%(len(mics),mics)) return mics ### SETUP AND SHUTDOWN def initiate(self): """run this after changing settings (like rate) before recording""" if self.device is None: self.device=self.valid_input_devices()[0] #pick the first one if self.rate is None: self.rate=self.valid_low_rate(self.device) self.chunk = int(self.rate/self.updatesPerSecond) # hold one tenth of a second in memory if not self.valid_test(self.device,self.rate): print("guessing a valid microphone device/rate...") self.device=self.valid_input_devices()[0] #pick the first one self.rate=self.valid_low_rate(self.device) self.datax=np.arange(self.chunk)/float(self.rate) msg='recording from "%s" '%self.info["name"] msg+='(device %d) '%self.device msg+='at %d Hz'%self.rate print(msg) def close(self): """gently detach from things.""" print(" -- sending stream termination command...") self.keepRecording=False #the threads should self-close while(self.t.isAlive()): #wait for all threads to close time.sleep(.1) self.stream.stop_stream() self.p.terminate() ### STREAM HANDLING def stream_readchunk(self): """reads some audio and re-launches itself""" try: self.data = np.fromstring(self.stream.read(self.chunk),dtype=np.int16) self.fftx, self.fft = getFFT(self.data,self.rate) except Exception as E: print(" -- exception! terminating...") print(E,"\n"*5) self.keepRecording=False if self.keepRecording: self.stream_thread_new() else: self.stream.close() self.p.terminate() print(" -- stream STOPPED") self.chunksRead+=1 def stream_thread_new(self): self.t=threading.Thread(target=self.stream_readchunk) self.t.start() def stream_start(self): """adds data to self.data until termination signal""" self.initiate() print(" -- starting stream") self.keepRecording=True # set this to False later to terminate stream self.data=None # will fill up with threaded recording data self.fft=None self.dataFiltered=None #same self.stream=self.p.open(format=pyaudio.paInt16,channels=1, rate=self.rate,input=True,frames_per_buffer=self.chunk) self.stream_thread_new() if __name__=="__main__": ear=SWHear(updatesPerSecond=10) # optinoally set sample rate here ear.stream_start() #goes forever lastRead=ear.chunksRead while True: while lastRead==ear.chunksRead: time.sleep(.01) print(ear.chunksRead,len(ear.data)) lastRead=ear.chunksRead print("DONE")So my question is: how to assigned a label text so they can turn into number and keep updating while listening to the sound? oh and code samples are most appreciated FYI I have done this "keep updating label text" before but in Delphy which is totally different. Thanks in advance for any help that I receive! RE: Assign Label Text as number in Hz - Alfalfa - Jul-01-2018 You can use a QTimer for that; def __init__(self): self.timer = QtCore.QTimer(interval=1000) self.timer.timeout.connect(self.update) self.timer.start() def update(self): print("Hello world") RE: Assign Label Text as number in Hz - mekha - Jul-01-2018 (Jul-01-2018, 05:28 PM)Alfalfa Wrote: You can use a QTimer for that; Thank you for your reply, can you tell me more specific where I should put this code? I'm so sorry the codes I've showed is the last update of my friend before I get entrusted with this project I'll show you the rest of the code, maybe you can take a look of it this codes belong to the main window from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: def _fromUtf8(s): return s try: _encoding = QtGui.QApplication.UnicodeUTF8 def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig) class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.resize(800, 620) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) self.frame = QtGui.QFrame(self.centralwidget) self.frame.setGeometry(QtCore.QRect(0, 0, 801, 611)) self.frame.setFrameShape(QtGui.QFrame.NoFrame) self.frame.setFrameShadow(QtGui.QFrame.Plain) self.frame.setObjectName(_fromUtf8("frame")) self.grFFT = PlotWidget(self.frame) self.grFFT.setGeometry(QtCore.QRect(10, 30, 781, 241)) self.grFFT.setObjectName(_fromUtf8("grFFT")) self.grPCM = PlotWidget(self.frame) self.grPCM.setGeometry(QtCore.QRect(10, 300, 781, 241)) self.grPCM.setObjectName(_fromUtf8("grPCM")) self.pbLevel = QtGui.QProgressBar(self.frame) self.pbLevel.setGeometry(QtCore.QRect(10, 550, 791, 23)) self.pbLevel.setMaximum(1000) self.pbLevel.setProperty("value", 123) self.pbLevel.setObjectName(_fromUtf8("pbLevel")) self.label_2 = QtGui.QLabel(self.frame) self.label_2.setGeometry(QtCore.QRect(10, 10, 948, 13)) self.label_2.setObjectName(_fromUtf8("label_2")) self.label_3 = QtGui.QLabel(self.frame) self.label_3.setGeometry(QtCore.QRect(10, 280, 721, 20)) self.label_3.setObjectName(_fromUtf8("label_3")) self.pushButton = QtGui.QPushButton(self.frame) self.pushButton.setGeometry(QtCore.QRect(10, 580, 191, 23)) self.pushButton.setObjectName(_fromUtf8("pushButton")) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None)) self.label_2.setText(_translate("MainWindow", "frequency data (FFT):", None)) self.label_3.setText(_translate("MainWindow", "raw data (PCM):", None)) self.pushButton.setText(_translate("MainWindow", "PushButton", None)) from pyqtgraph import PlotWidgetand this codes is to run the entire above codes (along with the one I posted before) from PyQt4 import QtGui,QtCore import sys import ui_main import numpy as np import pyqtgraph import SWHear class SoundApp(QtGui.QMainWindow, ui_main.Ui_MainWindow): def __init__(self, parent=None): pyqtgraph.setConfigOption('background', 'w') #before loading widget super(SoundApp, self).__init__(parent) self.setupUi(self) self.grFFT.plotItem.showGrid(True, True, 0.7) self.grPCM.plotItem.showGrid(True, True, 0.7) self.maxFFT=0 self.maxPCM=0 self.ear = SWHear.SWHear(rate=44100,updatesPerSecond=20) self.ear.stream_start() def update(self): if not self.ear.data is None and not self.ear.fft is None: pcmMax=np.max(np.abs(self.ear.data)) if pcmMax>self.maxPCM: self.maxPCM=pcmMax self.grPCM.plotItem.setRange(yRange=[-pcmMax,pcmMax]) if np.max(self.ear.fft)>self.maxFFT: self.maxFFT=np.max(np.abs(self.ear.fft)) #self.grFFT.plotItem.setRange(yRange=[0,self.maxFFT]) self.grFFT.plotItem.setRange(yRange=[0,1]) self.pbLevel.setValue(1000*pcmMax/self.maxPCM) pen=pyqtgraph.mkPen(color='b') self.grPCM.plot(self.ear.datax,self.ear.data,pen=pen,clear=True) pen=pyqtgraph.mkPen(color='r') self.grFFT.plot(self.ear.fftx,self.ear.fft/self.maxFFT,pen=pen,clear=True) QtCore.QTimer.singleShot(1, self.update) # QUICKLY repeat if __name__=="__main__": app = QtGui.QApplication(sys.argv) form = SoundApp() form.show() form.update() #start with something app.exec_() print("DONE")it'll be easier if you run it, the all .py codes I put it here in my drive Sound App RE: Assign Label Text as number in Hz - Alfalfa - Jul-03-2018 Hi there, I'm not sure about how you want to use it in your code, but QTimer are very easy. Like I shown you, you can simply init it in the __init__ of your gui loop (SoundApp), then use self.timer to manipulate it if needed. You don't need to init a new singleshot timer recursively like you've done (line 36). Single shots timers are.. for single shot. They fire only once. If you want to repeat an action every few seconds, simply init a QTimer with an interval, then start it and it will loop forever. RE: Assign Label Text as number in Hz - DeaD_EyE - Jul-03-2018 Hello, I can only help a bit with signal processing. Instead of making a fft and throwing the negative frequencies away, you should do a rfft. Here a corrected version and additional the same with a rfft. def getFFT(data, rate): """Given some data and rate, returns FFTfreq and FFT (half).""" window_size = len(data) data *= np.hamming(window_size) fft = np.fft.fft(data).real freq = np.fft.fftfreq(window_size, 1.0 / rate) half_size = slice(None, window_size // 2) return freq[half_size], fft[half_size] def getRFFT(data, rate): """Given some data and rate, returns RFFTfreq and RFFT.""" window_size = len(data) data *= np.hamming(window_size) rfft = np.fft.rfft(data).real freq = np.fft.rfftfreq(window_size, 1.0 / rate) return freq, rfft RE: Assign Label Text as number in Hz - mekha - Jul-04-2018 (Jul-03-2018, 04:17 PM)Alfalfa Wrote: Hi there, Thank you so much!! I'll try it right away when I get home, and see if there's any problem while I processing it (Jul-03-2018, 10:02 PM)DeaD_EyE Wrote: Hello, I can only help a bit with signal processing. Thank you so much! I'll try to changed the FFT then and see the results but I have a question if I want to change the number that written in the axis XY inside the graph to alphabet like ABCD instead 0123, how can I do that? I want to make a marker for some number into alphabet like 198 into G, 440 into A, etc RE: Assign Label Text as number in Hz - DeaD_EyE - Jul-04-2018 I have no clue about music. Based on this Article and this Example with matplotlib, I wrote an example. from itertools import product import matplotlib.pyplot as plt def calc_freq_from_midi(m): if m < 0 or m > 127: raise ValueError('m must be between 0 and 127.') notes = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') octaves = range(-1, 10) notes = product(octaves, notes) notes = [note + str(band) for band, note in notes] freq = 440 * 2 ** ((m - 69)/12) return freq, notes[m] labels = [calc_freq_from_midi(m) for m in range(128)] x_ticks, x_labels = zip(*labels) # it's a transpose frequencies = [110.0, 220.0, 440.0] # A2, A3, A4 magnitude = [2, 3, 4] plt.title('Spectrum') plt.xlabel('Notes') plt.ylabel('Magnitude') plt.xticks(x_ticks, x_labels, rotation='vertical') plt.plot(frequencies, magnitude, 'o') plt.legend(['Signal']) plt.show()Just check if the notes are correct. It should plot A2, A3 and A4 as blue dots. I guess you can also print two different xticks. Just explore the matplotlib examples. If you don't use matplotlib inside of QT, you have to adapt it to the plotting framework you use. RE: Assign Label Text as number in Hz - mekha - Jul-07-2018 (Jul-03-2018, 10:02 PM)DeaD_EyE Wrote: Hello, I can only help a bit with signal processing. When I run the code (I do the changes on FFT inside the SWHear.py into your code) The result is no error at all it runs perfectly but both of the graph is empty, showing empty data, its like couldn't recognize the data of the input somehow RE: Assign Label Text as number in Hz - mekha - Jul-07-2018 (Jul-03-2018, 04:17 PM)Alfalfa Wrote: Hi there, It does looping forever until I terminate the graphs window, then they stop. But if I want to assign this Qtimer to show some frequency data as Label Text in my QtDesigner window, then how I can do that? I do have a reference code on how to calculate the frequency data for marking some notes, and it's helpful to finalizing this project import pyaudio import os import struct import numpy as np import matplotlib.pyplot as plt import time from time import sleep %matplotlib tk CHUNK = 2**14 #2**15 #4096 WIDTH = 2 FORMAT = pyaudio.paInt16 CHANNELS = 2 RATE = 44100 dt = 1.0/RATE ### frequencies of the strings for the violin (tunned in A), in Hz f4 = 195.998 ## G3 f3 = 293.665 ## D4 f2 = 440.000 ## A4 f1 = 659.255 ## E5 n = CHUNK freqs = np.fft.rfftfreq(n, d = dt) def Frequency_of_position(position): """ Returns the frequency (Hz) of the note in from its position (halftones) relative to A4 in an equal tempered scale. Ex: 0 -> 440 Hz (A4), 12 -> 880 Hz (A5).""" return 440.0*(2**(1.0/12.0))**position def Position_to_note(position): "A A# B C C# D D# E F F# G G#" SCALE = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"] LETTER = SCALE[position % 12] NUMBER = str(int((position+57) / 12)) return LETTER+NUMBER pos = np.array(range(-36,48)) vnote_freqs = np.vectorize(Frequency_of_position) note_freqs = vnote_freqs(pos) def get_frequency( spectrum ): return freqs[np.argmax(spectrum)] class Freq_analysis(object): def __init__(self): self.pa = pyaudio.PyAudio() self.stream = self.open_mic_stream() self.plots = self.prepare_figure() #self.fig_and_axes = self.prepare_figure() #self.first_plot = self.plot_first_figure() def stop(self): self.stream.close() def open_mic_stream( self ): device_index = self.find_input_device() stream = self.pa.open( format = FORMAT, channels = CHANNELS, rate = RATE, input = True, input_device_index = device_index, frames_per_buffer = CHUNK) return stream def find_input_device(self): device_index = None for i in range( self.pa.get_device_count() ): devinfo = self.pa.get_device_info_by_index(i) print( "Device %d: %s"%(i,devinfo["name"]) ) for keyword in ["mic","input"]: if keyword in devinfo["name"].lower(): print( "Found an input: device %d - %s"% (i,devinfo["name"]) ) device_index = i return device_index if device_index == None: print( "No preferred input found; using default input device." ) return device_index def prepare_figure(self): plt.ion() fig1 = plt.figure(1, figsize = (16,6)) wide_plot = plt.subplot(2,1,1) plt.vlines([f1,f2,f3,f4],1,1e17, linestyles = 'dashed') plt.xlabel("freq (Hz)") plt.ylabel("S^2 (u. arb.)") plt.xscale('log') plt.yscale('log') plt.xlim([80,4000]) #plt.xlim([600,700]) #plt.xlim([400,500]) plt.ylim([1e0,1e17]) spec_w, = plt.plot([1,1],[1,1], '-',c = 'blue') f4_plot = plt.subplot(2,4,5) plt.vlines(f4,1,1e17, linestyles = 'dashed') plt.xlabel("freq (Hz)") plt.ylabel("S^2 (u. arb.)") plt.yscale('log') plt.xlim([140,260]) plt.ylim([1e0,1e17]) spec_f4, = plt.plot([1,1],[1,1], '-',c = 'blue') f3_plot = plt.subplot(2,4,6) plt.vlines(f3,1,1e17, linestyles = 'dashed') plt.xlabel("freq (Hz)") plt.yscale('log') plt.xlim([220,380]) plt.ylim([1e0,1e17]) spec_f3, = plt.plot([1,1],[1,1], '-',c = 'blue') f2_plot = plt.subplot(2,4,7) plt.vlines(f2,1,1e17, linestyles = 'dashed') plt.xlabel("freq (Hz)") plt.yscale('log') plt.xlim([400,500]) plt.ylim([1e0,1e17]) spec_f2, = plt.plot([1,1],[1,1], '-',c = 'blue') f1_plot = plt.subplot(2,4,8) plt.vlines(f1,1,1e17, linestyles = 'dashed') plt.xlabel("freq (Hz)") plt.yscale('log') plt.xlim([600,700]) plt.ylim([1e0,1e17]) spec_f1, = plt.plot([1,1],[1,1], '-',c = 'blue') plt.draw() #return fig1, wide_plot, f1_plot, f2_plot, f3_plot, f4_plot return spec_w, spec_f1, spec_f2, spec_f3, spec_f4 def PrintFreq(self, S2): dominant = get_frequency( S2 ) dist = np.abs(note_freqs-dominant) closest_pos = pos[np.argmin(dist)] closest_note = Position_to_note(closest_pos) print(dominant, "(",closest_note, "=",Frequency_of_position(closest_pos),")") def listen(self): try: block = self.stream.read(CHUNK) except IOError: # An error occurred. print( "Error recording.") return indata = np.array(struct.unpack("%dh"%(len(block)/2),block)) n = indata.size freqs = np.fft.rfftfreq(n, d = dt) data_rfft = np.fft.rfft(indata) S2 = np.abs(data_rfft)**2 #self.PrintFreq(block) #self.update_fig(block) self.PrintFreq(S2) self.update_fig(freqs, S2) def update_fig(self, freqs, S2): self.plots[0].set_xdata(freqs) self.plots[1].set_xdata(freqs) self.plots[2].set_xdata(freqs) self.plots[3].set_xdata(freqs) self.plots[4].set_xdata(freqs) self.plots[0].set_ydata(S2) self.plots[1].set_ydata(S2) self.plots[2].set_ydata(S2) self.plots[3].set_ydata(S2) self.plots[4].set_ydata(S2) plt.draw() plt.pause(0.001) if __name__ == "__main__": Tuner = Freq_analysis() for i in range(100): Tuner.listen() plt.ioff() plt.show()I already have a satisfying result's but the one last thing left is only to show this data on the QtDesigner window wether it's on the same window with the graphs or standalone RE: Assign Label Text as number in Hz - Alfalfa - Jul-10-2018 (Jul-07-2018, 02:44 AM)mekha Wrote: It does looping forever until I terminate the graphs window, then they stop. I'm not sure to understand, are you asking about how to use the QTimer to refresh the text of a QLabel? If so, simply add something like self.ui.yourLabel.setText("frequency data")into the function connected to your timer.. The purpose of QtDesigner is to organize your widget, but the dynamic content is rather set by using python. |