from pathlib import Path import pyaudio as pa from pydub import AudioSegment, effects class Recording: def __init__(self, frames: list, channels: int, samplingRate: int, sampleFormat: int): self._segment = AudioSegment( data=b''.join(frames), sample_width=sampleFormat, frame_rate=samplingRate, channels=channels ) self._postProcess() def _postProcess(self) -> None: self._segment = effects.normalize(self._segment) def writeWav(self, file: Path) -> None: self._segment.export(str(file), format="wav") def writeMp3(self, file: Path) -> None: self._segment.export(str(file), format="mp3", bitrate="320k") class Recorder: def __init__(self, bufferSize: int, samplingRate: int): self._bufferSize = bufferSize self._samplingRate = samplingRate self._sampleFormat = pa.paInt16 self._channels = 2 self._pa = pa.PyAudio() self._stream = None self._frames = [] def __del__(self): if self.recording: self._stream.stop_stream() self._stream.close() def _callback(self, inData, frameCount, timeInfo, statusFlags): if statusFlags != pa.paNoError: print(f"Recorder callback got status {hex(statusFlags)}, aborting") return (None, pa.paAbort) self._frames.append(inData) return (None, pa.paContinue) def startRecording(self) -> None: if self.recording: return self._frames.clear() self._stream = self._pa.open(format=self._sampleFormat, channels=self._channels, rate=self._samplingRate, frames_per_buffer=self._bufferSize, input=True, stream_callback=self._callback) def stopRecording(self) -> Recording: if not self.recording: return None self._stream.stop_stream() self._stream.close() self._stream = None return Recording(self._frames, self._channels, self._samplingRate, self._pa.get_sample_size(self._sampleFormat)) @property def recording(self) -> bool: return self._stream is not None