pythondockerportaudiopython-sounddevicejustpy

Is it possible to generate sound within a Python web application deployed with Docker on Google Cloud?


I have built a simple web application, which is supposed to test hearing frequency range of the user. It is built in JustPy framework and it uses pysinewave (in turn based on sounddevice) to generate sound continuously. I have deployed it in a Docker container on Google Cloud and the website part works just fine, but the sound-generating part is causing problems. Below is a part of debugger output:

500 Server Error
PortAudioError: Error querying device -1

Traceback

File /usr/local/lib/python3.10/site-packages/sounddevice.py, line 564, in query_devices ‒

561.     device = _get_device_id(device, kind, raise_on_error=True)

562.     info = _lib.Pa_GetDeviceInfo(device)

563.     if not info:

564.         raise PortAudioError('Error querying device {}'.format(device))

565.     assert info.structVersion == 2

566.     name_bytes = _ffi.string(info.name)

567.     try:

File /usr/local/lib/python3.10/site-packages/sounddevice.py, line 2654, in _get_stream_parameters ‒

2651.         samplerate = default.samplerate

2652.

2653.     device = _get_device_id(device, kind, raise_on_error=True)

2654.     info = query_devices(device)

2655.     if channels is None:

2656.         channels = info['max_' + kind + '_channels']

2657.     try:

File /usr/local/lib/python3.10/site-packages/sounddevice.py, line 811, in __init__ ‒

808.                 samplerate = isamplerate

809.         else:

810.             parameters, self._dtype, self._samplesize, samplerate = \

811.                 _get_stream_parameters(kind, device, channels, dtype, latency,

812.                                        extra_settings, samplerate)

813.             self._device = parameters.device

814.             self._channels = parameters.channelCount

File /usr/local/lib/python3.10/site-packages/sounddevice.py, line 1488, in __init__ ‒

1485.         Stream, RawOutputStream

1486.

1487.         

1488.         _StreamBase.__init__(self, kind='output', wrap_callback='array',

1489.                              **_remove_self(locals()))

1490.

1491.     def write(self, data):

File /usr/local/lib/python3.10/site-packages/pysinewave/sinewave.py, line 21, in __init__ ‒

18.                                     samplerate=samplerate)

19.

20.         # Create the output stream

21.         self.output_stream = sd.OutputStream(channels=1, callback= lambda *args: self._callback(*args), 

22.                                 samplerate=samplerate)

23.

24.     def _callback(self, outdata, frames, time, status):

File /app/./soundgen.py, line 7, in __init__ ‒

4. class SoundGen(SineWave):

5.

6.     def __init__(self, freq, vol):

7.         super().__init__(pitch=9, pitch_per_second=10000, decibels=-30, decibels_per_second=10000)

8.         self.set_frequency(freq)

9.         self.set_volume(vol)

10.         self.volume = self.sinewave_generator.amplitude

File /app/./main.py, line 23, in serve ‒

20.     def serve(cls, req):

21.

22.         # sine wave generator

23.         sound = SoundGen(440, 0.15)

24.

25.         freq_dict = cls.gen_freq_dict()

26.

I have installed additional libraries with the following commands in Dockerfile (based on the answer in this thread):

RUN apt-get update
RUN apt-get install libasound-dev libportaudio2 libportaudiocpp0 portaudio19-dev -y

But clearly, this is not enough. Am I missing an important library for this to work, or is this kind of functionality - generating sound in user's browser through a web application hosted on a remote server - just impossible to achieve in this manner?

To reiterate: the application works completely fine when run locally, and the frontend part generated with JustPy (with the sound functionality disabled) also works fine when deployed on Google Cloud.

I would be extremely grateful for suggestions.


Solution

  • It seems to me that you want to deploy a web app that plays audio on the user's device. When you host your app on the cloud, the server that runs your app will be the backend side, where you have used your SoundGen class. When you trigger it to make the sound, it searches for audio devices - that is not available in the cloud server, therefore it throws the exception.

    In the case when you have hosted your app on your computer, the docker runtime was able to access your audio peripherals and play the sound you wanted. What you might want to do instead is to move the sound generation out of the backend code and into the frontend part. On trigger (eg. button click), use only frontend code to play the sound you wanted on the client's device - the browser. Unfortunately, I am unfamiliar with JustPy, so I cannot write an example code for you, but hopefully, this still helped.