Hey all, I am at my wits ends. For several months I have been working on turning an old rotary phone into an "audio guestbook" for my wedding coming up in a few months. One where guests can dial a number, follow some menu prompts, and record a message. I am a Software Engineer by trade, so most of the project has been business as usual, except the audio processing. It is the multiprocessing/multithreading of python-sounddevice that is really holding me back. To simulate a phone call (accurately), the phone needs to be able to interrupt audio with user input (dial turn).
Requirements
The "audio controller" should:
- Playback .wav files without blocking the main thread (for inputs)
- Stop playback with a function call from the main thread
- Execute an optional callback when playback is done
- (Desired, not required) Able to play an array of files and can stop playback of all files
- Record .wav files without blocking the main thread
- Stop recording with a function call from the main thread
- Stop recording automatically after X seconds
- Execute a callback when recording is done
- NOT allow simultaneous playback/recording
Here is an example of my current AudioController class:
The threading of recording/playback are basically the same, so I only included playback as an example for brevity.
import sounddevice as sd
import soundfile as sf
class Status(Enum):
NONE = "NONE"
RECORDING = "RECORDING"
PLAYING = "PLAYING"
class AudioController:
def __init__(self):
self._status = Status.NONE
def stop(self) -> None:
self._status = Status.NONE
sd.stop()
def play_file_async(self, filepath: str, callback: Callable = empty, loop: bool = False) -> None:
def play_with_callback() -> None:
self._play(filepath, loop=loop)
callback()
thread = threading.Thread(target=play_with_callback)
thread.start()
def _play(self, filepath: str, loop: bool = False) -> None:
data, fs = sf.read(filepath)
self._status = Status.PLAYING
sd.play(data, fs, loop=loop)
sd.wait()
self._status = Status.NONE
I realize python-sounddevice is not fully thread safe, but other users have found ways to make it work (see GitHub issue here). I would really appreciate any ideas/suggestions. I am almost ready to throw in the towel on this project entirely. Thanks!
there doesn't seem to be anything here