0

我正在尝试为我创建的终端应用程序创建一个 GUI,该应用程序监听您的麦克风以确定您在吉他上演奏的音符。我想使用 PyQt5,这是我第一次使用它(或者特别是任何 GUI 框架)。

我可以很好地创建 UI,但是我用于由 QTimer 调用的笔记检测的主要功能使应用程序非常缓慢且无响应。我认为我在如何设置方面做得非常错误,但我自己找不到最好的方法。

这是我的代码:

import sys 
from PyQt5.QtWidgets import QApplication, QWidget 
from PyQt5.QtWidgets import QGridLayout, QLabel 
from PyQt5.QtGui import QFont 
from PyQt5.QtCore import QTimer, QTime, Qt 
import pyaudio
import numpy as np
from aubio import pitch
from helpers import pitch2note

# set up globals
CHUNK = 1024
FORMAT = pyaudio.paFloat32
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5000

tolerance = 0.8
downsample = 1
win_s = 4096 // downsample # fft size
hop_s = 1024  // downsample # hop size
pitch_o = pitch("yinfft", win_s, hop_s, RATE)
pitch_o.set_unit("Hz")
pitch_o.set_silence(-40)
pitch_o.set_tolerance(tolerance)

class Window(QWidget): 
  
    def __init__(self): 
        super().__init__() 
        self.setupUI()

    def setupUI(self):
        # UI design is very much in progress
        # not final but it shouldn't matter for this case
  
        self.setGeometry(100, 100, 800, 400) 
        self.noteLabel = QLabel('Note played:')
        layout = QGridLayout()
  
        font = QFont('Arial', 60, QFont.Bold) 
        self.note = QLabel() # the variable I will modify later
        self.note.setAlignment(Qt.AlignCenter) 
  
        # setting font to the label 
        self.note.setFont(font) 
  
        # adding widgets to the layout 
        layout.addWidget(self.noteLabel, 1, 0) 
        layout.addWidget(self.note, 1, 1)

  
        # setting the layout to main window 
        self.setLayout(layout) 
  
        # creating a timer object 
        timer = QTimer(self) 
  
        # adding action to timer 
        # this is what might be wrong
        timer.timeout.connect(self.getNote) 
  
        # update the timer every 33ms (30fps?) 
        # or is it too much?
        timer.start(33) 

    def getNote(self):
         # main function handling note detection

        p = pyaudio.PyAudio()

        stream = p.open(format=FORMAT,
                        channels=CHANNELS,
                        rate=RATE,
                        input=True,
                        frames_per_buffer=CHUNK)

        frames = []
        notes_list = []

        for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
            buffer = stream.read(CHUNK)
            frames.append(buffer)

            signal = np.frombuffer(buffer, dtype=np.float32)

            pitch_of_note = pitch_o(signal)[0]
            if pitch_of_note != 0:
                note_played = pitch2note(pitch_of_note)
                notes_list.append(note_played[:-1]) 
                # we append only note and not number (eg. E and not E2)

                if len(notes_list) == 10:
                    # We listen for 10 signals and then select the most frequent note in the list
                    # This is because when you pluck a note the frequency can deviate as you pluck it strongly
                    most_likely_note = max(notes_list, key=notes_list.count)

                    stream.stop_stream()
                    stream.close()
                    p.terminate()
                    
                    self.label.setText(most_likely_note)
                    return most_likely_note

  
  
# create pyqt5 app 
App = QApplication(sys.argv) 
  
# create the instance of our Window 
window = Window() 
  
# showing all the widgets 
window.show() 
  
# start the app 
App.exit(App.exec_())

这是帮助文件:

from math import log2

A4 = 440
C0 = A4*pow(2, -4.75)
NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]

def pitch2note(freq):
    h = round(12*log2(freq/C0))
    octave = h // 12
    n = h % 12
    return NOTE_NAMES[n] + str(octave)
4

1 回答 1

0

我建议在这里使用 QThread,因为您不希望 UI 在录制时阻塞。 https://doc.qt.io/qtforpython/PySide2/QtCore/QThread.html

或者,您也可以查看 PyAudio 的回调模式,它也可以非阻塞 https://people.csail.mit.edu/hubert/pyaudio/docs/

于 2020-08-20T10:23:10.660 回答