import time import wave from pathlib import Path from threading import Thread from pyaudio import PyAudio, paInt16, paContinue, paComplete class AudioRecord(PyAudio): def __init__(self, channels=2): super().__init__() self.chunk = 1024 # 每个缓冲区的帧数 self.format_sample = paInt16 # 采样位数 self.channels = channels # 声道: 1,单声道;2,双声道 self.fps = 44100 # 采样频率 self.input_dict = None self.output_dict = None self.stream = None self.filename = '~test.wav' self.wf = None self.stop_flag = False self.kill = False def callback_input(self, in_data, frame_count, time_info, status): """录制回调函数""" self.wf.writeframes(in_data) if not self.stop_flag: return (in_data, paContinue) else: return (in_data, paComplete) def callback_output(self, in_data, frame_count, time_info, status): """播放回调函数""" data = self.wf.readframes(frame_count) return (data, paContinue) def open_stream(self, name): """打开录制流""" input_device_index = self.get_device_index(name, True) if name else None return self.open(format=self.format_sample, channels=self.channels, rate=self.fps, frames_per_buffer=self.chunk, input=True, input_device_index=input_device_index, stream_callback=self.callback_input ) def audio_record_run(self, name=None): """音频录制""" self.wf = self.save_audio_file(self.filename) self.stream = self.open_stream(name) self.stream.start_stream() while self.stream.is_active(): time.sleep(0.1) self.wf.close() if self.kill: Path(self.filename).unlink() self.duration = self.get_duration() self.terminate_run() def run(self, filename=None, name=None, record=True): """音频录制启动""" if record: if filename: self.filename = filename thread_1 = Thread(target=self.audio_record_run, args=(name,)) else: if not filename: raise Exception('未输入音频文件名,不能播放,请输入后再试!') thread_1 = Thread(target=self.read_audio, args=(filename, name,)) thread_1.start() def read_audio(self, filename, name=None): """音频播放""" output_device_index = self.get_device_index(name, False) if name else None with wave.open(filename, 'rb') as self.wf: # 获取音频长度 self.duration = self.get_duration() self.stream = self.open(format=self.get_format_from_width(self.wf.getsampwidth()), channels=self.wf.getnchannels(), rate=self.wf.getframerate(), output=True, output_device_index=output_device_index, stream_callback=self.callback_output ) self.stream.start_stream() while self.stream.is_active(): time.sleep(0.1) print(self.duration) self.terminate_run() def get_duration(self): """获取音频时长""" return round(self.wf.getnframes() / self.wf.getframerate(), 2) def get_in_out_devices(self): """获取系统输入输出设备""" self.input_dict = {} self.output_dict = {} for i in range(self.get_device_count()): devinfo = self.get_device_info_by_index(i) if not devinfo['hostApi'] and int(devinfo['defaultSampleRate']) == self.fps \ and '映射器' not in devinfo['name']: if devinfo['maxInputChannels']: self.input_dict[devinfo['name'].split(' ')[0]] = i elif devinfo['maxOutputChannels']: self.output_dict[devinfo['name'].split(' ')[0]] = i def get_device_index(self, name, inp=True): """获取选定设备索引""" if inp and self.input_dict: return self.input_dict.get(name, -1) elif not inp and self.output_dict: return self.output_dict.get(name, -1) def save_audio_file(self, filename): """音频文件保存""" wf = wave.open(filename, 'wb') wf.setnchannels(self.channels) wf.setsampwidth(self.get_sample_size(self.format_sample)) wf.setframerate(self.fps) return wf def terminate_run(self): """结束流录制或流播放""" if self.stream is not None: self.stream.stop_stream() self.stream.close() self.stream = None self.wf = None self.terminate()