Ejemplo n.º 1
0
class QtRecorder(Recorder):
    def __init__(self, output_path: str, parent: QWidget):
        super().__init__(output_path)

        from PyQt5.QtMultimedia import QAudioRecorder

        self._recorder = QAudioRecorder(parent)
        audio = self._recorder.audioSettings()
        audio.setSampleRate(44100)
        audio.setChannelCount(1)
        self._recorder.setEncodingSettings(
            audio,
            self._recorder.videoSettings(),
            "audio/x-wav",
        )
        self._recorder.setOutputLocation(QUrl.fromLocalFile(self.output_path))
        self._recorder.setMuted(True)

    def start(self, on_done: Callable[[], None]) -> None:
        self._recorder.record()
        super().start(on_done)

    def stop(self, on_done: Callable[[str], None]):
        self._recorder.stop()
        super().stop(on_done)

    def on_timer(self):
        duration = self._recorder.duration()
        if duration >= 300:
            # disable mute after recording starts to avoid clicks/pops
            if self._recorder.isMuted():
                self._recorder.setMuted(False)
Ejemplo n.º 2
0
class QVoiceRecorder(object):
    def __init__(self):
        pass

    def initAudioInput(self, filepath):

        self.recorder = QAudioRecorder()

        self.settings = QAudioEncoderSettings()
        self.settings.setCodec("audio/vorbis")
        self.recorder.setContainerFormat("ogg")
        #self.settings.setQuality(QtMultimedia.HighQuality)

        self.recorder.setEncodingSettings(self.settings)

        url = QtCore.QUrl.fromLocalFile(
            QtCore.QFileInfo(filepath).absoluteFilePath())
        self.recorder.setOutputLocation(url)

    def start(self):
        self.recorder.record()

    def stop(self):
        self.recorder.pause()
        self.recorder.stop()
        self.recorder.stop()
Ejemplo n.º 3
0
class RecordAudio(object):

    recorder = None

    def __init__(self):
        pass

    def record(self, filename):
        url = QtCore.QUrl.fromLocalFile(
            QtCore.QFileInfo(filename).absoluteFilePath())
        #content = QMediaObject(url)

        #self.recorder = QAudioRecorder()
        #source = QAudioInput()
        #source = QMediaObject()
        self.recorder = QAudioRecorder()

        settings = QAudioEncoderSettings()
        settings.setChannelCount(1)
        settings.setSampleRate(44100)
        settings.setBitRate(32)
        settings.setCodec("audio/vorbis")
        #settings.setEncodingMode(QtMultimedia.ConstantQualityEnconding)

        self.recorder.setContainerFormat("ogg")
        self.recorder.setEncodingSettings(settings)
        self.recorder.setOutputLocation(url)

        #container = None
        #self.recorder.setEncodingSettings(settings, container)

    def record(self):
        self.recorder.record()

    def stop(self):
        self.recorder.stop()
Ejemplo n.º 4
0
class Tuner(QMainWindow):
    def __init__(self, parent=None):
        super(Tuner, self).__init__(parent)
        # 从文件中加载UI定义
        # self.ui = loadUi('Tuner.ui')  #pyqt5
        self.ui = Ui_MainWindow()  # 创建UI对象
        self.ui.setupUi(self)  # 构造UI界面

        # # 播放标准音
        self.mediaPlayer = QMediaPlayer(self)
        self.ui.c1.clicked.connect(self.playaudioc)
        self.ui.d1.clicked.connect(self.playaudiod)
        self.ui.e1.clicked.connect(self.playaudioe)
        self.ui.f1.clicked.connect(self.playaudiof)
        self.ui.g1.clicked.connect(self.playaudiog)
        self.ui.a1.clicked.connect(self.playaudioa)
        self.ui.b1.clicked.connect(self.playaudiob)

        # 录音
        self.fileName = ""
        self.recorder = QAudioRecorder(self)
        self.recorder.stateChanged.connect(self.do_stateChanged)
        self.recorder.durationChanged.connect(self.do_durationChanged)

        self.probe = QAudioProbe(self)  # 探测器
        self.probe.setSource(self.recorder)
        # self.probe.audioBufferProbed.connect(self.do_processBuffer)

        if self.recorder.defaultAudioInput() == "":  # str类型
            return  # 无音频录入设备

        for device in self.recorder.audioInputs():
            self.ui.comboDevices.addItem(device)  # 音频录入设备列表

        for codecName in self.recorder.supportedAudioCodecs():
            self.ui.comboCodec.addItem(codecName)  # 支持的音频编码

        sampleList, isContinuous = self.recorder.supportedAudioSampleRates()
        # isContinuous 是否支持连续的采样率,与C++不一样
        for i in range(len(sampleList)):
            self.ui.comboSampleRate.addItem("%d" % sampleList[i])  # 支持的采样率

        ##  channels
        self.ui.comboChannels.addItem("1")
        self.ui.comboChannels.addItem("2")
        self.ui.comboChannels.addItem("4")

        ##  ==============自定义功能函数============

    def __setRecordParams(self):  ##设置音频输入参数
        selectedFile = self.ui.editOutputFile.text().strip()
        if (selectedFile == ""):
            QMessageBox.critical(self, "错误", "请先设置录音输出文件")
            return False

        if os.path.exists(selectedFile):
            os.remove(selectedFile)  # 删除已有文件
        ##         QMessageBox.critical(self,"错误","录音输出文件被占用,无法删除")
        ##         return False

        recordFile = QUrl.fromLocalFile(selectedFile)
        self.recorder.setOutputLocation(recordFile)  # 设置输出文件

        recordDevice = self.ui.comboDevices.currentText()
        self.recorder.setAudioInput(recordDevice)  # 设置录入设备

        settings = QAudioEncoderSettings()  # 音频编码设置
        settings.setCodec(self.ui.comboCodec.currentText())  # 编码

        sampRate = int(self.ui.comboSampleRate.currentText())
        settings.setSampleRate(sampRate)  # 采样率

        channelCount = int(self.ui.comboChannels.currentText())
        settings.setChannelCount(channelCount)  # 通道数

        settings.setEncodingMode(QMultimedia.ConstantBitRateEncoding)  # 固定比特率

        self.recorder.setAudioSettings(settings)  # 音频设置
        return True

    def pitch_estimation(self, wavpath):
        if os.path.exists(wavpath):
            y, sr = librosa.load(wavpath)
            f0, voiced_flag, voiced_probs = librosa.pyin(
                y,
                fmin=librosa.note_to_hz('B3'),
                fmax=librosa.note_to_hz('C5'))
            f0 = f0[~np.isnan(f0)]
            times = librosa.times_like(f0)
            level = optimize.curve_fit(lambda x, b: b, times,
                                       np.nan_to_num(f0))[0]
            pitch = np.around(level[0], decimals=3).astype(float)
        return pitch
        ##  ==========由connectSlotsByName() 自动连接的槽函数==================

    @pyqtSlot()
    def on_btnGetFile_clicked(self):  ##"录音输出文件"按钮
        curPath = os.getcwd()  # 获取系统当前目录
        dlgTitle = "选择输出文件"
        filt = "wav文件(*.wav)"
        self.fileName, flt, = QFileDialog.getSaveFileName(
            self, dlgTitle, curPath, filt)
        if (self.fileName != ""):
            self.ui.editOutputFile.setText(self.fileName)

    @pyqtSlot()  ##开始录音
    def on_actRecord_triggered(self):
        success = True
        if (self.recorder.state() == QMediaRecorder.StoppedState):  # 已停止,重新设置
            success = self.__setRecordParams()  # 设置录音参数
        if success:
            self.recorder.record()

    @pyqtSlot()  ##退出
    def on_actQuit_triggered(self):
        sys.exit(app.exec_())

    # @pyqtSlot()  ##暂停
    # def on_actPause_triggered(self):
    #     self.recorder.pause()

    @pyqtSlot()  ##停止
    def on_actStop_triggered(self):
        self.recorder.stop()
        # 录完立马分析并显示Hz
        name = self.fileName.split('/')[-1][:-4]  # 存储的文件名 无.wav
        # func = lambda x, b: b
        if name == 'c1':
            # if os.path.exists(self.fileName):
            #     y, sr = librosa.load(self.fileName)
            #     f0, voiced_flag, voiced_probs = librosa.pyin(y, fmin=librosa.note_to_hz('C4'),
            #                                                  fmax=librosa.note_to_hz('C5'))
            #     f0 = f0[~np.isnan(f0)]
            #     times = librosa.times_like(f0)
            #     level = optimize.curve_fit(lambda x, b: b, times, np.nan_to_num(f0))[0]
            #     pitch = np.around(level[0], decimals=3).astype(float)
            pitch = self.pitch_estimation(self.fileName)
            self.ui.c1m.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.c1m.setText(str(pitch))
            F0 = 261.626
            err = np.around(np.abs(pitch - F0) / F0, decimals=4) * 100
            self.ui.c1e.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.c1e.setText(str(err))
        elif name == 'd1':
            pitch = self.pitch_estimation(self.fileName)
            self.ui.d1m.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.d1m.setText(str(pitch))
            F0 = 293.665
            err = np.around(np.abs(pitch - F0) / F0, decimals=4) * 100
            self.ui.d1e.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.d1e.setText(str(err))
        elif name == 'e1':
            pitch = self.pitch_estimation(self.fileName)
            self.ui.e1m.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.e1m.setText(str(pitch))
            F0 = 329.628
            err = np.around(np.abs(pitch - F0) / F0, decimals=4) * 100
            self.ui.e1e.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.e1e.setText(str(err))
        elif name == 'f1':
            pitch = self.pitch_estimation(self.fileName)
            self.ui.f1m.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.f1m.setText(str(pitch))
            F0 = 349.228
            err = np.around(np.abs(pitch - F0) / F0, decimals=4) * 100
            self.ui.f1e.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.f1e.setText(str(err))
        elif name == 'g1':
            pitch = self.pitch_estimation(self.fileName)
            self.ui.g1m.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.g1m.setText(str(pitch))
            F0 = 391.995
            err = np.around(np.abs(pitch - F0) / F0, decimals=4) * 100
            self.ui.g1e.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.g1e.setText(str(err))
        elif name == 'a1':
            pitch = self.pitch_estimation(self.fileName)
            self.ui.a1m.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.a1m.setText(str(pitch))
            F0 = 440.000
            err = np.around(np.abs(pitch - F0) / F0, decimals=4) * 100
            self.ui.a1e.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.a1e.setText(str(err))
        elif name == 'b1':
            pitch = self.pitch_estimation(self.fileName)
            self.ui.b1m.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.b1m.setText(str(pitch))
            F0 = 493.883
            err = np.around(np.abs(pitch - F0) / F0, decimals=4) * 100
            self.ui.b1e.setStyleSheet("color:rgb(10,10,10,255);"
                                      "font-size:32px;"
                                      "font-weight:600;"
                                      "font-family:Times New Roman;")
            self.ui.b1e.setText(str(err))
        else:
            QMessageBox.critical(self, "文件名错误", "请输入c1-b1中的音名")

    ##  =============自定义槽函数===============================
    def do_stateChanged(self, state):  ##状态变化
        isRecording = (state == QMediaRecorder.RecordingState)  # 正在录制
        self.ui.actRecord.setEnabled(not isRecording)
        # self.ui.actPause.setEnabled(isRecording)
        self.ui.actStop.setEnabled(isRecording)

        isStoped = (state == QMediaRecorder.StoppedState)  # 已停止
        self.ui.btnGetFile.setEnabled(isStoped)
        self.ui.editOutputFile.setEnabled(isStoped)

    def do_durationChanged(self, duration):  ##持续时间长度变化
        self.ui.LabPassTime.setText("已录制 %d 秒" % (duration / 1000))

    #   ============================================================================
    @pyqtSlot()
    def playaudioc(self):
        self.mediaPlayer.setMedia(
            QMediaContent(QUrl.fromLocalFile("data/standard/C4.wav")))
        self.mediaPlayer.play()

    @pyqtSlot()
    def playaudiod(self):
        self.mediaPlayer.setMedia(
            QMediaContent(QUrl.fromLocalFile("data/standard/D4.wav")))
        self.mediaPlayer.play()

    @pyqtSlot()
    def playaudioe(self):
        self.mediaPlayer.setMedia(
            QMediaContent(QUrl.fromLocalFile("data/standard/E4.wav")))
        self.mediaPlayer.play()

    @pyqtSlot()
    def playaudiof(self):
        self.mediaPlayer.setMedia(
            QMediaContent(QUrl.fromLocalFile("data/standard/F4.wav")))
        self.mediaPlayer.play()

    @pyqtSlot()
    def playaudiog(self):
        self.mediaPlayer.setMedia(
            QMediaContent(QUrl.fromLocalFile("data/standard/G4.wav")))
        self.mediaPlayer.play()

    @pyqtSlot()
    def playaudioa(self):
        self.mediaPlayer.setMedia(
            QMediaContent(QUrl.fromLocalFile("data/standard/A4.wav")))
        self.mediaPlayer.play()

    @pyqtSlot()
    def playaudiob(self):
        self.mediaPlayer.setMedia(
            QMediaContent(QUrl.fromLocalFile("data/standard/B4.wav")))
        self.mediaPlayer.play()
Ejemplo n.º 5
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # Controles principales para organizar la ventana.

        self.setupConstants()

        self.widget = QWidget(self)

        # tha main layout
        self.layout = QVBoxLayout()

        # the top box with file selections
        self.input_layout = QHBoxLayout()
        self.output_layout = QHBoxLayout()
        self.bottom_layout = QHBoxLayout()
        self.volume_box = QHBoxLayout()

        # video playback section
        self.video_widget = QVideoWidget(self)
        self.media_player = QMediaPlayer()
        self.media_player.setVideoOutput(self.video_widget)

        # initialize audio recording section
        self.recorder = QAudioRecorder()

        # labels
        self.volume_label = QLabel()
        self.volume_label.setText("Volume")

        # Buttons for the I/O files selection
        self.input_file_button = QPushButton("Video Input", self)
        self.output_file_button = QPushButton("Audio output", self)

        # path/file line edits
        self.input_file_edit = QLineEdit()
        self.output_file_edit = QLineEdit()
        self.play_button = QPushButton("", self)
        self.play_button.setIcon(self.play_normal_icon)
        self.play_button.resize(150, 150)
        self.stop_button = QPushButton("", self)
        self.stop_button.setIcon(self.stop_normal_icon)
        self.record_button = QPushButton("", self)
        self.record_button.setCheckable(True)
        self.record_button.setIcon(self.rec_icon)

        self.seek_slider = QSlider(Qt.Horizontal)

        self.volume_slider = QSlider(Qt.Horizontal)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setValue(self.media_player.volume())

        self.input_layout.addWidget(self.input_file_button)
        self.input_layout.addWidget(self.input_file_edit)

        self.output_layout.addWidget(self.output_file_button)
        self.output_layout.addWidget(self.output_file_edit)

        self.bottom_layout.addWidget(self.play_button)
        self.bottom_layout.addWidget(self.stop_button)
        self.bottom_layout.addWidget(self.record_button)
        self.bottom_layout.addLayout(self.volume_box)

        self.volume_box.addWidget(self.volume_label)
        self.volume_box.addWidget(self.volume_slider)

        self.layout.addWidget(self.video_widget)
        self.layout.addLayout(self.bottom_layout)
        self.layout.addWidget(self.seek_slider)
        self.layout.addLayout(self.input_layout)
        self.layout.addLayout(self.output_layout)

        # Personalizzazione della finestra
        self.setWindowTitle("Wish' Karaoke! :)")
        self.resize(800, 600)
        self.layout.setContentsMargins(10, 10, 10, 10)
        self.bottom_layout.setContentsMargins(0, 0, 0, 0)
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)

        self.setupMenus()
        self.setupUiConnections()

    def setupMenus(self):
        # setup the menus
        self.mainMenu = self.menuBar()

        # File menu and subitems
        self.fileMenu = self.mainMenu.addMenu('File')

        self.exitButton = QAction(self.exit_icon, 'Exit', self)
        self.exitButton.setShortcut('Ctrl+Q')
        self.exitButton.setStatusTip('Exit application')
        self.fileMenu.addAction(self.exitButton)

        # View menu and related items
        self.viewMenu = self.mainMenu.addMenu('View')

        # Fullscreen item
        self.toggleFullscreenButton = QAction(QIcon(""), 'Fullscreen', self)
        self.toggleFullscreenButton.setCheckable(True)
        self.toggleFullscreenButton.setStatusTip('Toggle fullscreen more')
        self.toggleFullscreenButton.setShortcut("CTRL+SHIFT+F")
        self.viewMenu.addAction(self.toggleFullscreenButton)

        # Tools menu and related items
        self.toolsMenu = self.mainMenu.addMenu('Tools')

        # Play/Rec bind toggle
        self.bindPlayRecButton = QAction(QIcon(""), 'Bind Play/Rec', self)
        self.bindPlayRecButton.setCheckable(True)
        self.bindPlayRecButton.setStatusTip('Bind Play and Rec')

        self.toolsMenu.addAction(self.bindPlayRecButton)

    def setupUiConnections(self):
        """
        Put all the UI connections and event catchers here, just to keep the code clean
        :return:
        """
        self.record_button.clicked.connect(self.recButtonState)
        self.seek_slider.sliderMoved.connect(self.media_player.setPosition)
        self.volume_slider.sliderMoved.connect(self.media_player.setVolume)
        self.media_player.positionChanged.connect(self.seek_slider.setValue)
        self.media_player.durationChanged.connect(
            partial(self.seek_slider.setRange, 0))
        self.play_button.clicked.connect(self.play_clicked)
        self.stop_button.clicked.connect(self.stop_clicked)
        self.media_player.stateChanged.connect(self.state_changed)
        #
        self.input_file_button.clicked.connect(self.selectInputFile)
        #
        self.input_file_edit.textChanged.connect(self.setInputMedia)
        #
        self.output_file_button.clicked.connect(self.selectOutputFile)
        self.output_file_edit.textChanged.connect(self.setOutputMedia)

        # menu connections
        # fullscreen
        self.toggleFullscreenButton.toggled.connect(self.toggleFullscreen)
        # quit
        self.exitButton.triggered.connect(self.close)
        # Play/Rec bind
        self.bindPlayRecButton.toggled.connect(self.bind_play_rec)

        # Installing event filter for the video widget
        self.video_widget.installEventFilter(self)

    def bind_play_rec(self):
        """
        toggle the binding between play and rec to start recording as soon as playback starts.


        :return: Nothing
        """
        if not self.bindPlayRecStatus:
            self.bindPlayRecStatus = True
        else:
            self.bindPlayRecStatus = False
        # If binding is active, the REC button is disabled.
        self.record_button.setDisabled(self.bindPlayRecStatus)

    def play_clicked(self):
        """
        Start or resume playback.
        If binding is active, start/pause the audio recording as well
        """
        if (self.media_player.state()
                in (QMediaPlayer.PausedState, QMediaPlayer.StoppedState)):
            self.media_player.play()
            logger.info("(Re)Starting playback")
            if self.bindPlayRecStatus:
                if (self.recorder.state() in (QAudioRecorder.PausedState,
                                              QAudioRecorder.StoppedState)):
                    logger.info(
                        "Rec/Play bind is on! (Re)Starting Recorder as well.")
                    self.recorder.record()
        else:
            self.media_player.pause()
            logger.info("Pausing playback")
            if self.bindPlayRecStatus:
                logger.info("Rec/Play bind is on! Pausing Recorder as well.")
                self.recorder.pause()

    def stop_clicked(self):
        """
        Stopping playback.
        if Play/Rec binding is on, stop also the recorder.
        """
        logger.info("Stopping playback")
        self.media_player.stop()
        if self.bindPlayRecStatus:
            logger.info("Rec/Play bind is on! Stopping Recorder as well.")
            self.recorder.stop()

    def state_changed(self, newstate):
        """
        Update buttons. Not really needed, probably.
        """
        states = {
            QMediaPlayer.PausedState: self.play_normal_icon,
            QMediaPlayer.PlayingState: self.pause_icon,
            QMediaPlayer.StoppedState: self.play_normal_icon
        }
        self.play_button.setIcon(states[newstate])
        # elegant way to enable/disable the stop button
        self.stop_button.setEnabled(newstate != QMediaPlayer.StoppedState)

    def eventFilter(self, obj, event):
        """
        Catch MouseButtonDblClick or CTRL+SHIFT+F to toggle fullscreen

        """
        if (event.type() == QEvent.KeyPress and event.modifiers() & Qt.ShiftModifier \
                    and event.modifiers() & Qt.ControlModifier and event.key() == 70) \
                    or event.type() == QEvent.MouseButtonDblClick:
            obj.setFullScreen(not obj.isFullScreen())
        return False

    def toggleFullscreen(self):
        self.video_widget.setFullScreen(not self.video_widget.isFullScreen())

    def selectInputFile(self):
        """
        Just a small function to open a file dialog
        """

        #self.input_file_edit.setText(QFileDialog.getOpenFileName())

        # encode the resulting filename as UNICODE text
        self.input_filename, _ = QFileDialog.getOpenFileName()
        self.input_file_edit.setText(self.input_filename)

    def setInputMedia(self, filename):
        self.media_player.setMedia(QMediaContent(QUrl.fromLocalFile(filename)))

    def selectOutputFile(self):
        """
        Just a small function to open a file dialog
        """
        self.output_filename, _ = QFileDialog.getSaveFileName()
        self.output_file_edit.setText(self.output_filename)

    def setOutputMedia(self, filename):
        self.recorder.setOutputLocation(QUrl.fromLocalFile(filename))

    def recButtonState(self):
        if self.record_button.isChecked():
            self.doRecord()
        else:
            self.stopRecord()

    def doRecord(self):
        """
        TODO: define this function better, toggled by the Rec button
        :return:
        """
        print("Recording")
        self.recorder.record()

    def stopRecord(self):
        print("Stopping recorder")
        self.recorder.stop()

    def setupConstants(self):
        self.rec_icon = QIcon.fromTheme("media-record", QIcon("icons/rec.png"))
        self.play_normal_icon = QIcon.fromTheme("media-playback-start",
                                                QIcon("icons/Play-Normal.png"))
        self.stop_normal_icon = QIcon.fromTheme("media-playback-stop",
                                                QIcon("icons/Stop-Normal.png"))
        self.exit_icon = QIcon.fromTheme("application-exit",
                                         QIcon("icons/application-exit.png"))
        self.pause_icon = QIcon.fromTheme(
            "media-playback-pause", QIcon("icons/Pause-Disabled-icon.png"))
        self.bindPlayRecStatus = False
Ejemplo n.º 6
0
class Recorder(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_Form()
        self.ui.setupUi(self)

        self.recorder = QAudioRecorder(self)
        self.recorder.stateChanged.connect(self.do_stateChanged)
        self.recorder.durationChanged.connect(self.do_durationChanged)

        self.probe = QAudioProbe(self)
        self.probe.setSource(self.recorder)
        self.probe.audioBufferProbed.connect(self.do_processBuffer)

        if self.recorder.defaultAudioInput() == "":  # str类型
            return  # 无音频录入设备

        for device in self.recorder.audioInputs():
            self.ui.comboDevices.addItem(device)  # 音频录入设备列表

        for codecName in self.recorder.supportedAudioCodecs():
            self.ui.comboCodec.addItem(codecName)  # 支持的音频编码

        sampleList, isContinuous = self.recorder.supportedAudioSampleRates()
        # isContinuous 是否支持连续的采样率,与C++不一样

        for i in range(len(sampleList)):
            self.ui.comboSampleRate.addItem("%d" % sampleList[i])  # 支持的采样率

# channels
        self.ui.comboChannels.addItem("1")
        self.ui.comboChannels.addItem("2")
        self.ui.comboChannels.addItem("4")

# quality
        self.ui.sliderQuality.setRange(0, QMultimedia.VeryHighQuality)
        self.ui.sliderQuality.setValue(QMultimedia.NormalQuality)

# bitrates
        self.ui.comboBitrate.addItem("32000")
        self.ui.comboBitrate.addItem("64000")
        self.ui.comboBitrate.addItem("96000")
        self.ui.comboBitrate.addItem("128000")

        self.__addToolbar()

    def __addToolbar(self):
        layout = QtWidgets.QVBoxLayout()

        Astart = QtWidgets.QToolButton()
        Astart.setText("开始")
        Astart.setDefaultAction(self.ui.actRecordStart)
        Apause = QtWidgets.QToolButton()
        Apause.setText("暂停")
        Apause.setDefaultAction(self.ui.actRecordPause)
        Astop = QtWidgets.QToolButton()
        Astop.setText("停止")
        Astop.setDefaultAction(self.ui.actRecordStop)

        self.toolbar = QtWidgets.QToolBar()
        self.toolbar.addWidget(Astart)
        self.toolbar.addWidget(Apause)
        self.toolbar.addWidget(Astop)

        layout.addWidget(self.toolbar)
        layout.addWidget(self.ui.widget)

        self.setLayout(layout)

    def __setRecordParams(self):
        selectedFile=self.ui.editOutputFile.text().strip()
        if (selectedFile ==""):
            QMessageBox.critical(self,"错误","请先设置录音输出文件")
            return False
        
        if os.path.exists(selectedFile):
            os.remove(selectedFile)#删除已有文件
                 
        recordFile=QUrl.fromLocalFile(selectedFile)
        self.recorder.setOutputLocation(recordFile)  #设置输出文件

        recordDevice=self.ui.comboDevices.currentText()
        self.recorder.setAudioInput(recordDevice)    #设置录入设备

        settings=QAudioEncoderSettings()    #音频编码设置
        settings.setCodec(self.ui.comboCodec.currentText())   #编码

        sampRate=int(self.ui.comboSampleRate.currentText())
        settings.setSampleRate(sampRate)    #采样率

        bitRate=int(self.ui.comboBitrate.currentText())
        settings.setBitRate(bitRate)  #比特率

        channelCount=int(self.ui.comboChannels.currentText())
        settings.setChannelCount(channelCount)    #通道数

        quality=QMultimedia.EncodingQuality(self.ui.sliderQuality.value())
        settings.setQuality(quality)     #品质

        if self.ui.radioQuality.isChecked():  #编码模式为固定品质,自动决定采样率,采样点大小
            settings.setEncodingMode(QMultimedia.ConstantQualityEncoding)
        else:
            settings.setEncodingMode(QMultimedia.ConstantBitRateEncoding)  #固定比特率
        
        self.recorder.setAudioSettings(settings) #音频设置
        return True

##  ==========由connectSlotsByName() 自动连接的槽函数==================    
    @pyqtSlot()
    def on_btnGetFile_clicked(self):    ##"录音输出文件"按钮
        curPath=os.getcwd()        #获取系统当前目录
        dlgTitle="选择输出文件"   
        filt="wav文件(*.wav)"     
        fileName,flt,=QFileDialog.getSaveFileName(self, dlgTitle, curPath, filt)
        if (fileName !=""):
            self.ui.editOutputFile.setText(fileName)
    
    @pyqtSlot()    ##开始录音
    def on_actRecordStart_triggered(self): 
        success=True
        if (self.recorder.state() == QMediaRecorder.StoppedState):  #已停止,重新设置
            success=self.__setRecordParams()  #设置录音参数
        if success:   
            self.recorder.record()

    @pyqtSlot()    ##暂停
    def on_actRecordPause_triggered(self): 
        self.recorder.pause()
    
    @pyqtSlot()
    def on_actRecordStop_triggered(self): 
        self.recorder.stop()

##  =============自定义槽函数===============================       
    def do_stateChanged(self,state):  ##状态变化
        isRecording=(state==QMediaRecorder.RecordingState)  #正在录制
        self.ui.actRecordStart.setEnabled(not isRecording)
        self.ui.actRecordPause.setEnabled(isRecording)
        self.ui.actRecordStop.setEnabled(isRecording)

        isStoped=(state==QMediaRecorder.StoppedState)    #已停止
        self.ui.btnGetFile.setEnabled(isStoped)
        self.ui.editOutputFile.setEnabled(isStoped)

    def do_durationChanged(self,duration):  ##持续时间长度变化
        self.ui.LabPassTime.setText("已录制 %d 秒"%(duration/1000))
    
    def do_processBuffer(self,buffer):     ##解析缓冲区数据
        self.ui.spin_byteCount.setValue(buffer.byteCount())      #缓冲区字节数
        self.ui.spin_duration.setValue(buffer.duration()/1000)   #缓冲区时长
        self.ui.spin_frameCount.setValue(buffer.frameCount())    #缓冲区帧数
        self.ui.spin_sampleCount.setValue(buffer.sampleCount())  #缓冲区采样数
  
        audioFormat=buffer.format()      #缓冲区格式,QAudioBuffer
        self.ui.spin_channelCount.setValue(audioFormat.channelCount()) #通道数
        self.ui.spin_sampleSize.setValue(audioFormat.sampleSize())     #采样大小
        self.ui.spin_sampleRate.setValue(audioFormat.sampleRate())     #采样率
        self.ui.spin_bytesPerFrame.setValue(audioFormat.bytesPerFrame()) #每帧字节数
  
        if (audioFormat.byteOrder()==QAudioFormat.LittleEndian):
           self.ui.edit_byteOrder.setText("LittleEndian")   #字节序
        else:
           self.ui.edit_byteOrder.setText("BigEndian")
  
        self.ui.edit_codec.setText(audioFormat.codec()) #编码格式
  
        if (audioFormat.sampleType()==QAudioFormat.SignedInt): #采样点类型
           self.ui.edit_sampleType.setText("SignedInt")
        elif(audioFormat.sampleType()==QAudioFormat.UnSignedInt):
           self.ui.edit_sampleType.setText("UnSignedInt")
        elif(audioFormat.sampleType()==QAudioFormat.Float):
           self.ui.edit_sampleType.setText("Float")
        else:
           self.ui.edit_sampleType.setText("Unknown")
Ejemplo n.º 7
0
class AudioRecorder(QWidget):
    def __init__(self):
        super().__init__()
        self.initializeUI()

    def initializeUI(self):
        """Initialize the window and display its contents to the screen."""
        self.setFixedSize(360, 540)
        self.setWindowTitle('9.1 - Audio Recorder')

        self.audio_path = ""  # Empty variable for path to audio file

        self.setupWindow()
        self.setupSystemTrayIcon()
        self.show()

    def setupWindow(self):
        """Set up widgets in the main window and the QAudioRecorder instance."""
        # Set up two push buttons (the app's first "screen")
        self.select_path_button = QPushButton("Select Audio Path")
        self.select_path_button.setObjectName("SelectFile")
        self.select_path_button.setFixedWidth(140)
        self.select_path_button.clicked.connect(self.selectAudioPath)

        self.start_button = QPushButton()
        self.start_button.setObjectName("StartButton")
        self.start_button.setEnabled(False)
        self.start_button.setFixedSize(105, 105)
        self.start_button.clicked.connect(self.startRecording)

        # Set up the labels and stop button (the app's second "screen")
        self.recording_label = QLabel("Recording...")
        self.recording_label.setFont(QFont("Helvetica [Cronyx]", 32))
        self.recording_label.setVisible(False)
        self.recording_label.setAlignment(Qt.AlignHCenter)
        self.time_label = QLabel("00:00")
        self.time_label.setFont(QFont("Helvetica [Cronyx]", 18))
        self.time_label.setObjectName("Time")
        self.time_label.setVisible(False)
        self.time_label.setAlignment(Qt.AlignHCenter)

        self.stop_button = QPushButton()
        self.stop_button.setObjectName("StopButton")
        self.stop_button.setFixedSize(65, 65)
        self.stop_button.setVisible(False)
        self.stop_button.clicked.connect(self.stopRecording)

        # Set up the main layout
        self.main_v_box = QVBoxLayout()
        self.main_v_box.setAlignment(Qt.AlignHCenter)
        self.main_v_box.addWidget(self.select_path_button)
        # Force select_path_button to be centered in the window
        self.main_v_box.setAlignment(self.select_path_button, Qt.AlignCenter)
        self.main_v_box.addStretch(3)
        self.main_v_box.addWidget(self.start_button)
        self.main_v_box.setAlignment(self.start_button, Qt.AlignCenter)
        self.main_v_box.addWidget(self.recording_label)
        self.main_v_box.addWidget(self.time_label)
        self.main_v_box.addStretch(3)
        self.main_v_box.addWidget(self.stop_button)
        self.main_v_box.setAlignment(self.stop_button, Qt.AlignCenter)
        self.main_v_box.addStretch(1)

        self.setLayout(self.main_v_box)  # Set the beginning layout

        # Specify audio encoder settings
        audio_settings = QAudioEncoderSettings()
        # Depending upon your platform or the codecs that you have available, you
        # will need to change the codec. For Linux users if you are having issues
        # use "audio/x-vorbis", and then select the .ogg extension when saving
        # the file
        audio_settings.setCodec("audio/wav")
        audio_settings.setQuality(QMultimedia.HighQuality)

        # Create instance of QAudioRecorder for recording audio
        self.audio_recorder = QAudioRecorder()
        # Uncomment to discover possible codecs supported on your platform
        #print(self.audio_recorder.supportedAudioCodecs())
        self.audio_recorder.setEncodingSettings(audio_settings)
        self.audio_recorder.durationChanged.connect(self.displayTime)

    def setupSystemTrayIcon(self):
        """Set up system tray icon and context menu. User can re-open the window if
        it was closed or quit the application using the tray menu."""
        self.tray_icon = QSystemTrayIcon(
            QIcon(":/resources/images/mic_icon.png"))

        # Create the actions and context menu for the tray icon
        tray_menu = QMenu()

        open_act = tray_menu.addAction("Open")
        open_act.triggered.connect(self.show)
        tray_menu.addSeparator()
        quit_act = tray_menu.addAction("Quit")
        quit_act.triggered.connect(QApplication.quit)

        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

    def selectAudioPath(self):
        """Open file dialog and choose the directory for saving the audio file."""
        path, _ = QFileDialog.getSaveFileName(self, "Save Audio File",
                                              os.getenv("HOME"), "WAV (*.wav)")

        if path:
            self.audio_path = path
            self.start_button.setEnabled(True)
        else:
            QMessageBox.information(self, "Error", "No directory selected.",
                                    QMessageBox.Ok)

    def startRecording(self):
        """Set up the audio output file location, reset widget states.
        Also starts the timer and begins recording. """
        self.audio_recorder.setOutputLocation(
            QUrl.fromLocalFile(self.audio_path))

        # Set widget states
        self.select_path_button.setVisible(False)
        self.start_button.setVisible(False)
        self.recording_label.setVisible(True)
        self.time_label.setVisible(True)
        self.time_label.setText("00:00")  # Update the label
        self.stop_button.setVisible(True)

        # Start the timer and begin recording
        self.audio_recorder.record()

    def stopRecording(self):
        """Stop recording, stop the timer, and reset widget states."""
        self.audio_recorder.stop()

        # Reset widget states
        self.select_path_button.setVisible(True)
        self.start_button.setVisible(True)
        self.recording_label.setVisible(False)
        self.time_label.setVisible(False)
        self.stop_button.setVisible(False)

    def displayTime(self, duration):
        """Calculate the time displayed in the time_label widget."""
        minutes, seconds = self.convertTotalTime(duration)
        time_recorded = "{:02d}:{:02d}".format(minutes, seconds)
        self.time_label.setText(time_recorded)

    def convertTotalTime(self, time_in_milli):
        """Convert time from milliseconds."""
        minutes = (time_in_milli / (1000 * 60)) % 60
        seconds = (time_in_milli / 1000) % 60
        return int(minutes), int(seconds)

    def closeEvent(self, event):
        """Display a message in the system tray when the main window has been closed."""
        self.tray_icon.showMessage("Notification",
                                   "Audio Recorder is still running.", 8000)
Ejemplo n.º 8
0
class MainWindow_EXEC():
    def __init__(self):

        #-------------------Init QT Setup---------------------------

        app = QtWidgets.QApplication(sys.argv)

        self.MainWindow = QtWidgets.QMainWindow()
        self.ui = Ui_MainWindow()

        self.ui.setupUi(self.MainWindow)
        app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())

        #------------------Exporting Setup------------------------------

        self.ui.export_midi.clicked.connect(self.openDirectory_midi)
        self.ui.export_midi.setFocusPolicy(QtCore.Qt.NoFocus)
        self.ui.export_audio.clicked.connect(self.openDirectory_audio)
        self.ui.export_audio.setFocusPolicy(QtCore.Qt.NoFocus)

        #------------------Metronome Setup------------------------------

        self.ui.metronome_button.clicked.connect(self.metro_thread)

        #------------------Recording Setup------------------------------

        self.ui.start_stop_rec.clicked.connect(self.start_stop_recording)
        self.ui.play_gui.clicked.connect(self.play)

        # QAudio setup
        self.settings = QAudioEncoderSettings()
        self.settings.setBitRate(16)
        self.settings.setChannelCount(1)
        self.audioRecorder = QAudioRecorder()
        self.audioRecorder.setEncodingSettings(self.settings)
        self.file_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__),
                         resource_path("resources/output.wav")))

        self.url = QUrl.fromLocalFile(self.file_path)
        self.audioRecorder.setOutputLocation(self.url)

        #------------------Audio Terrain Gui Setup------------------------------

        self.terrain = Terrain()
        self.terrain.update()
        self.terrain_widget = self.terrain.getwidget()
        self.layout = QtGui.QGridLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.ui.t_widget.setLayout(self.layout)
        self.layout.addWidget(self.terrain_widget, 0, 0, 1, 1)

        #------------------Audio Trimmer Setup------------------------------

        self.ui.audio_trimmer.clicked.connect(self.trim_audio)

        if os.path.isfile("resources/output.wav"):
            self.y, self.sr = librosa.load(
                resource_path("resources/output.wav"), sr=44100)
        else:
            new_wav = AudioSegment.empty()
            new_wav.export("resources/output.wav", format="wav")
            self.y, self.sr = librosa.load(
                resource_path("resources/output.wav"), sr=44100)

        self.duration = round(
            librosa.core.get_duration(y=self.y, sr=self.sr) * self.sr)
        self.maxv = np.iinfo(np.int16).max

        self.win = pg.GraphicsLayoutWidget()
        self.p = self.win.addPlot()

        #removes X & Y Axis and disables mouse movement
        self.p.showAxis('bottom', show=False)
        self.p.showAxis('left', show=False)
        self.p.setMouseEnabled(x=False, y=False)

        self.region = pg.LinearRegionItem(brush=(100, 100, 100, 60),
                                          bounds=(0, self.duration))
        self.region.setRegion([0, self.duration])

        self.p.addItem(self.region, ignoreBounds=True)
        self.p.plot(self.y, pen="w")

        self.layout.addWidget(self.win)
        self.win.hide()

        #------------------Midi Setup------------------------------

        self.ui.convert_midi.clicked.connect(self.convertMidi)
        self.ui.midi_play.clicked.connect(self.midiplayer_thread)
        self.ui.tempo_slider.valueChanged[int].connect(self.tempo_value)
        self.ui.midi_loop.toggle()

        # default bpm is 120
        self.current_tempo = 120
        self.detected_tempo = 120

        #------------------Drum Kit Selector Setup----------------------

        self.ui.drum_kits.clicked.connect(self.select_drumkit)
        self.drum_number = 0
        self.drum_folders = [
            'Drum_Kit_1', 'Drum_Kit_2', 'Drum_Kit_3', 'Drum_Kit_4'
        ]
        self.drum_current = self.drum_folders[self.drum_number]

        #------------------EXEC Window---------------------------------
        self.MainWindow.show()
        sys.exit(app.exec_())
#---------------------------------------------------------------

#------------------Functions----------------------------------

#------------------Drum Kit Selector------------------------------

    def select_drumkit(self):
        if self.drum_number < 3:
            self.drum_number += 1
            self.drum_current = self.drum_folders[self.drum_number]
            self.ui.drum_kits.setText(self.drum_current.replace("_", " "))
        else:
            self.drum_number = 0
            self.drum_current = self.drum_folders[self.drum_number]
            self.ui.drum_kits.setText(self.drum_current.replace("_", " "))

        #------------------Audio Trimmer------------------------------

    def trim_audio(self):
        # Switch to Trimmer widget
        self.layout.removeWidget(self.terrain_widget)
        self.terrain_widget.hide()
        self.win.show()
        self.trim_values = self.region.getRegion()
        self.updateaudio()
        # Trims signal array with region values
        self.y = self.y[round(self.trim_values[0]):round(self.trim_values[1])]

        # save the new signal values to wav
        librosa.output.write_wav(resource_path("resources/output.wav"),
                                 (self.y * self.maxv).astype(np.int16),
                                 self.sr)
        self.updateplot()

    def updateplot(self):
        # Replot the trimmed wav and update region bounds
        self.duration = round(
            librosa.core.get_duration(y=self.y, sr=self.sr) * self.sr)
        self.p.plot(clear=True)
        self.p.plot(self.y, pen="w")
        self.region = pg.LinearRegionItem(brush=(100, 100, 100, 50),
                                          bounds=(0, self.duration))
        self.p.addItem(self.region, ignoreBounds=True)
        self.region.setRegion([0, self.duration])

    def updateaudio(self):
        self.y, self.sr = librosa.load(resource_path("resources/output.wav"),
                                       sr=44100)

        #------------------Metronome Threading------------------------------

    def metro_thread(self):

        if self.ui.metronome_button.isChecked():
            print('metronome is On')
            self.thread = QThread(
            )  # a new thread to run our background tasks in
            self.worker = Worker(
                self.current_tempo)  # a new worker to perform those tasks
            self.worker.moveToThread(
                self.thread
            )  # move the worker into the thread, do this first before connecting the signals
            self.thread.started.connect(
                self.worker.work
            )  # begin our worker object's loop when the thread starts running
            self.thread.start()

        else:
            print('metronome is Off')
            self.stop_loop()
            self.worker.finished.connect(
                self.loop_finished
            )  # do something in the gui when the worker loop ends
            self.worker.finished.connect(
                self.thread.quit)  # tell the thread it's time to stop running
            self.worker.finished.connect(self.thread.wait)
            self.worker.finished.connect(
                self.worker.deleteLater
            )  # have worker mark itself for deletion
            self.thread.finished.connect(self.thread.deleteLater)

    def stop_loop(self):
        self.worker.working = False

    def loop_finished(self):
        # print('Worker Finished')
        pass

    #---------------------------------------------------------

    #------------------ MIDI ------------------------------

    def tempo_value(self, value):
        self.current_tempo = value

    def convertMidi(self):
        self.ui.convert_midi.setEnabled(False)
        self.thread2 = QThread()
        self.worker2 = ConvertMidi_Worker()
        self.worker2.moveToThread(self.thread2)
        self.thread2.started.connect(self.worker2.work)
        self.thread2.start()
        self.worker2.finished.connect(self.convert_finished)
        self.worker2.finished.connect(self.thread2.quit)
        self.worker2.finished.connect(self.thread2.wait)
        self.worker2.finished.connect(self.worker2.deleteLater)
        self.thread2.finished.connect(self.thread2.deleteLater)

    def convert_finished(self, tempo):
        self.detected_tempo = tempo
        self.ui.tempo_slider.setValue(self.detected_tempo)
        self.ui.convert_midi.clearFocus()
        self.ui.convert_midi.setEnabled(True)
        print('Midi Conversion finished')

    def midiplayer_thread(self):

        if self.ui.midi_play.isChecked() and self.ui.midi_loop.isChecked(
        ) == False:

            self.ui.midi_play.setEnabled(False)
            self.win.hide()
            self.terrain_widget.show()
            self.terrain.animate()
            self.thread3 = QThread()
            self.worker3 = MidiPlayer_Worker(self.current_tempo,
                                             self.drum_current)
            self.worker3.moveToThread(self.thread3)
            self.thread3.started.connect(self.worker3.workonce)

            self.thread3.start()
            self.worker3.finished2.connect(self.midi_loop_finished2)
            self.worker3.finished2.connect(self.thread3.quit)
            self.worker3.finished2.connect(self.thread3.wait)
            self.worker3.finished2.connect(self.worker3.deleteLater)
            self.thread3.finished.connect(self.thread3.deleteLater)

        elif self.ui.midi_play.isChecked() and self.ui.midi_loop.isChecked(
        ) == True:
            self.win.hide()
            self.terrain_widget.show()
            self.start_Midi_Thread()
            self.terrain.animate()

        elif self.ui.midi_play.isChecked() == False:
            self.terrain.stop_animate()
            self.stop_Midi_Thread()

    def start_Midi_Thread(self):
        self.thread3 = QThread()
        self.worker3 = MidiPlayer_Worker(self.current_tempo, self.drum_current)
        self.worker3.moveToThread(self.thread3)
        self.thread3.started.connect(self.worker3.work)
        self.thread3.start()

    def stop_Midi_Thread(self):
        self.worker3.working = False

        self.worker3.stop()
        self.worker3.finished.connect(self.midi_loop_finished)
        self.worker3.finished.connect(self.thread3.quit)
        self.worker3.finished.connect(self.thread3.wait)
        self.worker3.finished.connect(self.worker3.deleteLater)
        self.thread3.finished.connect(self.thread3.deleteLater)
        print('done')

    def midi_loop_finished(self):
        print('Midi loop Finished')

    def midi_loop_finished2(self):
        print('Midi Player Finished')
        self.ui.midi_play.toggle()
        self.ui.midi_play.setEnabled(True)
        self.terrain.stop_animate()

    #---------------------------------------------------------

    #------------------ Recorder & Player ------------------------------

    def start_stop_recording(self):
        if self.ui.start_stop_rec.isChecked():
            self.ui.play_gui.setEnabled(False)
            self.ui.audio_trimmer.setEnabled(False)

            self.win.hide()
            self.terrain_widget.show()

            self.layout.addWidget(self.terrain_widget)
            self.audioRecorder.record()
            self.terrain.update()

            self.terrain.animate()
            print('Recording...')

        else:
            self.ui.play_gui.setEnabled(True)
            self.ui.audio_trimmer.setEnabled(True)

            self.terrain.stop_animate()
            self.audioRecorder.stop()
            self.layout.removeWidget(self.terrain_widget)
            self.terrain_widget.hide()

            self.updateaudio()
            self.win.show()
            self.updateplot()
            print('Stop Recording')

    def play(self):
        if self.ui.play_gui.isChecked():
            self.win.hide()
            self.terrain_widget.show()

            self.player = QSound(resource_path("resources/output.wav"))
            self.terrain.animate()
            self.player.play()
            # if self.player.isFinished():
            # 	self.ui.play_gui.toggle()
            # 	print('done')

        else:
            self.terrain.stop_animate()
            self.player.stop()
            self.player.deleteLater()

    #------------------ Exporting ------------------------------

    def openDirectory_midi(self):
        self.openDirectoryDialog = QtGui.QFileDialog.getExistingDirectory(
            self.MainWindow, "Save Midi File")
        if self.openDirectoryDialog:
            self.saveMidi(self.openDirectoryDialog)
        else:
            pass

    def openDirectory_audio(self):
        self.openDirectoryDialog = QtGui.QFileDialog.getExistingDirectory(
            self.MainWindow, "Save Audio File")
        if self.openDirectoryDialog:
            self.saveAudio(self.openDirectoryDialog)
        else:
            pass

    def saveMidi(self, directory):
        shutil.copy("resources/beatbox.mid", directory)

    def saveAudio(self, directory):
        shutil.copy("resources/output.wav", directory)
Ejemplo n.º 9
0
class App(MainWidget):
    client = Client('localhost', 5000)
    client_thread = ClientThread(client)  # 后台线程

    def __init__(self):
        super(App, self).__init__()
        self.loginButton.clicked.connect(self.showLoginDialog)
        self.client_thread.text_signal.connect(self.showText)  # 显示文本消息
        self.client_thread.usr_signal.connect(self.showUserList)  # 更新在线用户
        self.client_thread.file_signal.connect(self.showFile)  # 显示文件消息
        self.emojis.emoji_signal.connect(self.addEmoji)

        # 通过QListWidget的当前item变化来切换QStackedWidget中的序号
        self.userListWidget.currentRowChanged.connect(self.dialogChanged)

        self.usrList = []  # 保存上一次的在线用户列表
        self.groupList = []  # 群组列表

        self.md5 = hashlib.md5()  # 用于加密密码

        # 录音机
        self.recorder = QAudioRecorder(self)
        settings = QAudioEncoderSettings()
        settings.setChannelCount(2)
        settings.setSampleRate(16000)
        self.recorder.setEncodingSettings(settings)

    # 处理对话用户改变
    def dialogChanged(self, i):
        # 通过QListWidget的当前item变化来切换QStackedWidget中的序号
        self.stackedWidget.setCurrentIndex(i)

        # enable当前chatWidget的所有按钮
        self.stackedWidget.currentWidget().emojiButton.setEnabled(True)
        self.stackedWidget.currentWidget().sendButton.setEnabled(True)
        self.stackedWidget.currentWidget().fileButton.setEnabled(True)
        self.stackedWidget.currentWidget().voiceButton.setEnabled(True)
        try:
            # 设置图标
            self.userListWidget.currentItem().setIcon(
                qta.icon('fa.check-circle', color='lightgreen'))
        except:
            pass

    # 显示登陆对话框
    def showLoginDialog(self):
        # 登录对话框
        self.d = LoginDialog()
        self.dialog = QDialog()
        self.d.setupUi(self.dialog)
        self.dialog.setWindowFlag(QtCore.Qt.FramelessWindowHint)
        self.dialog.show()
        # 点击确认就调用login()进行登录操作
        self.d.buttonBox.accepted.connect(self.login)

    # 登录的相关操作
    def login(self):
        passwd = self.d.lineEdit_2.text()
        if len(passwd) == 0:
            print('密码不能为空!')
            QMessageBox.critical(self, "错误", "密码不能为空!", QMessageBox.Ok)
            return
        try:
            self.client.connect()
        except:
            print('连接服务器失败!')
            QMessageBox.critical(self, "错误", "连接服务器失败!", QMessageBox.Ok)
            return
        self.client_thread.start()  # 启动线程
        self.name = self.d.lineEdit.text()  # 用户名
        self.client.name = self.name

        # md5加密密码
        self.md5.update(passwd.encode('utf-8'))
        hashed_passwd = self.md5.hexdigest()

        data = {'name': self.name, 'passwd': hashed_passwd}
        self.nameLabel.setText('<u>' + self.name)
        self.client.add_msg(msgtype.LOGIN, '', data)  # 登录信息发给服务器验证
        self.stackedWidget.removeWidget(
            self.stackedWidget.findChild(ChatWidget,
                                         name='unlogin'))  # 移除未登录显示的界面
        # 关闭登录按钮
        self.loginButton.close()

        # 为用户建立文件夹
        if not os.path.exists('./temp/%s' % self.client.name):
            os.makedirs('./temp/%s' % self.client.name)

    # 发送文字消息
    def sendText(self):
        text = self.stackedWidget.currentWidget().msgEdit.toPlainText()
        if not len(text):
            return  # 长度为零,不发送
        try:
            to = self.userListWidget.selectedItems()[0].text()
        except:  # 出现异常是因为没有选择对话用户
            return
        if to != self.name:
            data = {'to': to, 'text': text}
            self.client.add_msg(msgtype.TEXT, '', data)
        self.stackedWidget.currentWidget().msgEdit.clear()  # 清空输入框

        # 显示在聊天记录中
        item = QListWidgetItem()
        item.setText(text)
        item.setBackground(QColor('#F19483'))
        item.setTextAlignment(Qt.AlignRight)
        self.stackedWidget.currentWidget().msgList.addItem(item)

    # 显示收到的文字消息
    def showText(self, msg):
        from_user = msg['from']
        text = msg['text']
        item = QListWidgetItem()
        if from_user in self.usrList:  # 一对一
            item.setText(text)
        elif from_user in self.groupList:  # 群聊
            user = msg['user']  # 发言用户
            if user == self.name:
                return
            item.setText('<%s>' % user + text)
        item.setTextAlignment(Qt.AlignLeft)
        item.setBackground(QColor('#F7D420'))
        chat = self.stackedWidget.findChild(
            ChatWidget, name=from_user)  # 找到对应的chatWidget插入新的消息
        chat.msgList.addItem(item)
        chat.msgList.setCurrentRow(chat.msgList.count() - 1)
        if from_user in self.usrList:
            if self.stackedWidget.currentIndex(
            ) != self.usrList.index(from_user) + len(self.groupList):
                # 如果当前没有和该用户对话,就显示未读图标,并弹出通知
                user = self.userListWidget.item(
                    self.usrList.index(from_user) + len(self.groupList))
                user.setIcon(qta.icon('fa.circle', color='orange'))
                self.tray.showMessage(
                    from_user, text, qta.icon('fa5.comment-alt',
                                              color='white'))
        elif from_user in self.groupList:
            if self.stackedWidget.currentIndex() != self.groupList.index(
                    from_user):
                # 如果当前没有看这个群聊,就显示未读图标,并弹出通知
                user = self.userListWidget.item(
                    self.groupList.index(from_user))
                user.setIcon(qta.icon('fa.circle', color='orange'))
                self.tray.showMessage(
                    from_user, text, qta.icon('fa5.comment-alt',
                                              color='white'))

    # 更新在线用户列表和对应的chatWidget
    def showUserList(self, usr_list):
        users = usr_list['users']
        groups = usr_list['groups']

        # 群组
        for i in groups:
            if i not in self.groupList:
                # 添加右侧的chatWidget
                chatWidget = ChatWidget()
                chatWidget.setObjectName(i)
                chatWidget.emojiButton.clicked.connect(self.emojis.show)
                chatWidget.sendButton.clicked.connect(self.sendText)
                chatWidget.fileButton.clicked.connect(self.sendFile)
                chatWidget.voiceButton.pressed.connect(self.startRecord)
                chatWidget.voiceButton.released.connect(self.stopRecord)
                self.stackedWidget.addWidget(chatWidget)

                # 添加到左侧的用户列表
                item = QListWidgetItem(i)
                item.setSizeHint(QSize(16777215, 60))
                item.setTextAlignment(Qt.AlignCenter)
                item.setText(i)
                item.setIcon(qta.icon('fa.check-circle', color='lightgreen'))
                self.userListWidget.addItem(item)

        # 清空用户列表
        for i in range(len(groups), self.userListWidget.count()):
            self.userListWidget.takeItem(len(groups))

        # 用户
        for i in users:
            # 添加到用户列表
            item = QListWidgetItem(i)
            item.setSizeHint(QSize(16777215, 60))
            item.setTextAlignment(Qt.AlignCenter)
            item.setText(i)
            item.setIcon(qta.icon('fa.check-circle', color='lightgreen'))
            self.userListWidget.addItem(item)

        # 对于下线的用户,移除对应的chatWidget
        for i in self.stackedWidget.findChildren(ChatWidget):
            if i.objectName() not in users and i.objectName() not in groups:
                self.stackedWidget.removeWidget(i)

        for i in users:
            if i not in self.usrList:  # 新用户
                # 添加右侧的chatWidget
                chatWidget = ChatWidget()
                chatWidget.setObjectName(i)
                chatWidget.emojiButton.clicked.connect(self.emojis.show)
                chatWidget.sendButton.clicked.connect(self.sendText)
                chatWidget.fileButton.clicked.connect(self.sendFile)
                chatWidget.voiceButton.pressed.connect(self.startRecord)
                chatWidget.voiceButton.released.connect(self.stopRecord)
                self.stackedWidget.addWidget(chatWidget)

        self.usrList = users  # 更新用户列表
        self.groupList = groups  # 更新群组列表

    # 发送文件
    def sendFile(self):
        fileName, filetype = QFileDialog.getOpenFileName(self, "选取文件", "./")
        if len(fileName) == 0:
            return
        to = self.userListWidget.selectedItems()[0].text()  # 发送给的用户
        if to != self.name:
            self.client.add_msg(msgtype.FILE, '', {
                'to': to,
                'filename': fileName
            })

        # 文件显示在聊天记录中
        item = QListWidgetItem()
        item.setSizeHint(QSize(200, 80))
        fileWidget = FileWidget(fileName, 0)
        item.setBackground(QColor('#F19483'))
        fileWidget.fileButton.setObjectName(fileName)
        fileWidget.fileButton.clicked.connect(self.openFile)
        self.stackedWidget.currentWidget().msgList.addItem(item)
        self.stackedWidget.currentWidget().msgList.setItemWidget(
            item, fileWidget)

    # 显示收到的文件
    def showFile(self, msg):
        from_user = msg['from']
        filename = msg['filename']
        chat = self.stackedWidget.findChild(
            ChatWidget, name=from_user)  # 找到对应的chatWidget插入新的消息
        if from_user in self.usrList:  # 一对一
            if self.stackedWidget.currentIndex(
            ) != self.usrList.index(from_user) + len(self.groupList):
                # 如果当前没有和该用户对话,就显示未读图标
                user = self.userListWidget.item(
                    self.usrList.index(from_user) + len(self.groupList))
                user.setIcon(qta.icon('fa.circle', color='orange'))
                self.tray.showMessage(from_user, filename,
                                      qta.icon('fa5.file', color='white'))
        elif from_user in self.groupList:  # 群聊
            item = QListWidgetItem()
            item.setText('<%s>' % msg['user'])
            item.setBackground(QColor('#F7D420'))
            item.setSizeHint(QSize(200, 40))
            chat.msgList.addItem(item)
            if self.stackedWidget.currentIndex() != self.groupList.index(
                    from_user):
                user = self.userListWidget.item(
                    self.groupList.index(from_user))
                user.setIcon(qta.icon('fa.circle', color='orange'))
                self.tray.showMessage(from_user, filename,
                                      qta.icon('fa5.file', color='white'))

        # 文件显示在聊天记录中
        item = QListWidgetItem()
        item.setBackground(QColor('#F7D420'))
        item.setSizeHint(QSize(200, 80))
        fileWidget = FileWidget(filename, 1)
        fileWidget.fileButton.setObjectName('./temp/%s/' % self.name +
                                            filename)
        if filename.endswith('.wav'):  # 语音消息
            fileWidget.fileButton.clicked.connect(self.playVoice)
        else:
            fileWidget.fileButton.clicked.connect(self.openFile)
        chat.msgList.addItem(item)
        chat.msgList.setItemWidget(item, fileWidget)

    # 打开点击的文件
    def openFile(self):
        filename = self.sender().objectName()
        QDesktopServices.openUrl(QUrl.fromLocalFile(filename))

    # 开始录音
    def startRecord(self):
        print('开始录音...')
        self.stackedWidget.currentWidget().voiceButton.setIcon(
            qta.icon('fa.spinner'))  # 设置图标
        # 设置文件名
        self.voiceFileName = os.path.abspath('.') + '\\temp\\%s\\%s.wav' % (
            self.name, time.strftime('%Y%m%d%H%M%S', time.localtime()))
        print(self.voiceFileName)
        self.recorder.setOutputLocation(QUrl.fromLocalFile(self.voiceFileName))
        self.recorder.record()  # 开始录音

    # 停止录音并发送
    def stopRecord(self):
        self.stackedWidget.currentWidget().voiceButton.setIcon(
            qta.icon('fa.microphone'))
        self.recorder.stop()
        print('录音结束')
        to = self.userListWidget.selectedItems()[0].text()
        if to != self.name:
            self.client.add_msg(msgtype.FILE, '', {
                'to': to,
                'filename': self.voiceFileName
            })
        item = QListWidgetItem()
        item.setSizeHint(QSize(200, 80))
        item.setBackground(QColor('#F19483'))
        fileWidget = FileWidget(self.voiceFileName, 0)
        fileWidget.fileButton.setObjectName(self.voiceFileName)
        fileWidget.fileButton.clicked.connect(self.playVoice)
        self.stackedWidget.currentWidget().msgList.addItem(item)
        self.stackedWidget.currentWidget().msgList.setItemWidget(
            item, fileWidget)

    # 播放语音
    def playVoice(self):
        print('playing ' + self.sender().objectName())
        sound = QtMultimedia.QSound(self.sender().objectName())  # 取得文件名
        sound.play()
        loop = QEventLoop()
        loop.exec()

    # 向输入框中添加选中的表情
    def addEmoji(self, emoji):
        self.stackedWidget.currentWidget().msgEdit.setText(
            self.stackedWidget.currentWidget().msgEdit.toPlainText() + emoji)