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()
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
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")