From 8ea2b64ff798af913dcba64baace8d2536bf0b18 Mon Sep 17 00:00:00 2001 From: Eddy Pedroni Date: Thu, 1 Jan 2026 17:57:27 +0100 Subject: Add Android app wrapper around web interface --- solo-tool-project/src/solo_tool/recorder.py | 75 +++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 solo-tool-project/src/solo_tool/recorder.py (limited to 'solo-tool-project/src/solo_tool/recorder.py') diff --git a/solo-tool-project/src/solo_tool/recorder.py b/solo-tool-project/src/solo_tool/recorder.py new file mode 100644 index 0000000..fd2df02 --- /dev/null +++ b/solo-tool-project/src/solo_tool/recorder.py @@ -0,0 +1,75 @@ +import pyaudio as pa +from pathlib import Path + +class Recording: + def __init__(self, frames: [], channels: int, samplingRate: int, sampleFormat: int): + self._frames = frames + self._channels = channels + self._samplingRate = samplingRate + self._sampleFormat = sampleFormat + + def writeWav(self, file: Path) -> None: + import wave + with wave.open(str(file), "wb") as wf: + wf.setnchannels(self._channels) + wf.setsampwidth(self._sampleFormat) + wf.setframerate(self._samplingRate) + wf.writeframes(b''.join(self._frames)) + + def writeMp3(self, file: Path) -> None: + from pydub import AudioSegment + segment = AudioSegment( + data=b''.join(self._frames), + sample_width=self._sampleFormat, + frame_rate=self._samplingRate, + channels=self._channels + ) + 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 + -- cgit v1.2.3