platypush/platypush/plugins/sound/_streams/_recorder.py

97 lines
2.7 KiB
Python

from typing import Type
import sounddevice as sd
from platypush.message.event.sound import (
SoundRecordingPausedEvent,
SoundRecordingResumedEvent,
SoundRecordingStartedEvent,
SoundRecordingStoppedEvent,
)
from .._converters import RawInputAudioConverter
from .._model import AudioState, StreamType
from ._base import AudioThread
class AudioRecorder(AudioThread):
"""
The ``AudioRecorder`` thread is responsible for recording audio from the
input device, writing it to the converter process and dispatch the
converted audio to the registered consumers.
"""
def __init__(self, *args, output_format: str, **kwargs):
super().__init__(*args, **kwargs)
self.output_format = output_format
@property
def direction(self) -> StreamType:
return StreamType.INPUT
def _audio_callback(self):
# _ = frames
# __ = time
def callback(indata, outdata, _, __, status):
if self.state != AudioState.RUNNING:
return
if status:
self.logger.warning('Recording callback status: %s', status)
if not self._converter:
self.logger.warning(
'The ffmpeg converter process has already terminated'
)
self.notify_stop()
raise sd.CallbackStop
try:
self._converter.write(indata.tobytes())
except AssertionError as e:
self.logger.warning('Audio converter callback error: %s', e)
self.state = AudioState.STOPPED
return
if self.audio_pass_through:
outdata[:] = indata
return callback
@property
def _audio_converter_type(self) -> Type[RawInputAudioConverter]:
return RawInputAudioConverter
@property
def _started_event_type(self) -> Type[SoundRecordingStartedEvent]:
return SoundRecordingStartedEvent
@property
def _stopped_event_type(self) -> Type[SoundRecordingStoppedEvent]:
return SoundRecordingStoppedEvent
@property
def _paused_event_type(self) -> Type[SoundRecordingPausedEvent]:
return SoundRecordingPausedEvent
@property
def _resumed_event_type(self) -> Type[SoundRecordingResumedEvent]:
return SoundRecordingResumedEvent
@property
def _converter_args(self) -> dict:
return {
'output_format': self.output_format,
**super()._converter_args,
}
@property
def _stream_args(self) -> dict:
return {
'callback': self._audio_callback(),
**super()._stream_args,
}
# vim:sw=4:ts=4:et: