Pygame MIDI Playback in PaaS from Dockerfile
Question:
The Problem:
I am currently deploying a Python Flask App using a Dockerfile on a PaaS (Heroku like). The User Story is as follows:
- User Clicks ‘Generate MIDI’
- MIDI is generated and User can (on the same page):
-
- ‘Download MIDI’ (currently works perfectly locally & PaaS deployment)
-
- ‘Playback MIDI; (only works on local Flask deployment)
I had some NotImplemented
: no such file or dir… errors which have gone away since I added some install lines to my Dockerfile. Alas, I am stuck at this ALSA error:
pygame.error: ALSA: Couldn't open audio device: No such file or directory
I know for certain the file is a good directory as downloading proves the file exists and references the source. Locally, you can also see the file generate.
The Context:
Here is my Dockerfile:
# syntax=docker/dockerfile:1
FROM python:3.9-slim-buster
WORKDIR /appname
COPY requirements.txt requirements.txt
# Added to avoid NotImplemented errors
RUN apt-get update && apt-get install -yq
libgtk2.0-dev
libasound2
alsa-tools
# Original Run Command
&& pip3 install -r requirements.txt
# && rm -rf /var/lib/apt/lists/* # Doesn't work.
COPY . .
EXPOSE 5000
CMD [ "python3", "-m" , "flask", "--app", "appname", "run", "--host=0.0.0.0"]
Here is a pertinent snippet of my Flask App:
from pygame import mixer
if request.form.get('playback_midi') and MIDI_file:
if 'Playback MIDI' in request.form['playback_midi']:
mixer.init() # <-- Here is where it fails
mixer.music.load("temp_MIDI_File.mid")
mixer.music.play()
The "temp_MIDI_File.mid"
is used so that it is constantly overwritten without unique file names (that is handled elsewhere).
What I’ve Tried:
Currently, I’m working with these references:
Run Apps Using Audio in a Docker Container
Getting pygame running in docker
I’m thinking the ‘Run Apps Using Audio in a Docker Container’ might have the solution in there, I’m just not sure if the same solution could possibly work in a PaaS context (as I’m not sure what audio device would possibly be usable in this cloud hosting scenario; or if I even have privileges to access it). I’m also open to using a different package besides Pygame. Thanks!
Further Research:
Running Sound in a Container
Docker Container Audio
Docker Tips – Play Audio in a Container (2022)
Maybe this is best handled in JS…
Leaving it for now…
I was able to Use the html <audio>
tag to generate audio successfully.
All of the controls work. This leads me to two complicated avenues I don’t quite have time to pursue, but will put a pin in…
- Audio-Deviceless File Render – Using the FluidSynth API, perhaps I could convert the MIDI to a WAV and play the audio using the html
audio
tags. midi2audio
seemed like a pythonic way to approach this, but seems configured on using ALSA just like pygame
. The FluidSynth API is coded in C, so I’m not sure how to bridge this.
- Specify the device to be used by ALSA in the container – Perhaps I could specify –device in the Dockerfile to find the audio device and use a full Ubuntu image to launch it.
Answers:
My Solution:
Fortunately, there is an awesome html MIDI player that solved this issue:
Here is my Flask HTML Template Code:
{% if MIDI_playback %}
<midi-player
src="{{ MIDI_playback }}"
sound-font visualizer="#myPianoRollVisualizer">
</midi-player>
<midi-visualizer type="piano-roll" id="myPianoRollVisualizer"
src="{{ MIDI_playback }}" style="color: white">
</midi-visualizer>
<script src="https://cdn.jsdelivr.net/combine/npm/[email protected],npm/@magenta/[email protected]/es`6/core.js,npm/focus-visible@5,npm/[email protected]">
</script>
An Important Note:
Using the script within this Python Flask App, I ran into this CORS error:
No ‘Access-Control-Allow-Origin’ header is present on the requested
resource. Origin ‘http://localhost:3000’ is therefore not allowed
access. If an opaque response serves your needs, set the request’s
mode to ‘no-cors’ to fetch the resource with CORS disabled
My workaround was to specify a CORS configuration in local and production instances.
The Problem:
I am currently deploying a Python Flask App using a Dockerfile on a PaaS (Heroku like). The User Story is as follows:
- User Clicks ‘Generate MIDI’
- MIDI is generated and User can (on the same page):
-
- ‘Download MIDI’ (currently works perfectly locally & PaaS deployment)
-
- ‘Playback MIDI; (only works on local Flask deployment)
I had some NotImplemented
: no such file or dir… errors which have gone away since I added some install lines to my Dockerfile. Alas, I am stuck at this ALSA error:
pygame.error: ALSA: Couldn't open audio device: No such file or directory
I know for certain the file is a good directory as downloading proves the file exists and references the source. Locally, you can also see the file generate.
The Context:
Here is my Dockerfile:
# syntax=docker/dockerfile:1
FROM python:3.9-slim-buster
WORKDIR /appname
COPY requirements.txt requirements.txt
# Added to avoid NotImplemented errors
RUN apt-get update && apt-get install -yq
libgtk2.0-dev
libasound2
alsa-tools
# Original Run Command
&& pip3 install -r requirements.txt
# && rm -rf /var/lib/apt/lists/* # Doesn't work.
COPY . .
EXPOSE 5000
CMD [ "python3", "-m" , "flask", "--app", "appname", "run", "--host=0.0.0.0"]
Here is a pertinent snippet of my Flask App:
from pygame import mixer
if request.form.get('playback_midi') and MIDI_file:
if 'Playback MIDI' in request.form['playback_midi']:
mixer.init() # <-- Here is where it fails
mixer.music.load("temp_MIDI_File.mid")
mixer.music.play()
The "temp_MIDI_File.mid"
is used so that it is constantly overwritten without unique file names (that is handled elsewhere).
What I’ve Tried:
Currently, I’m working with these references:
Run Apps Using Audio in a Docker Container
Getting pygame running in docker
I’m thinking the ‘Run Apps Using Audio in a Docker Container’ might have the solution in there, I’m just not sure if the same solution could possibly work in a PaaS context (as I’m not sure what audio device would possibly be usable in this cloud hosting scenario; or if I even have privileges to access it). I’m also open to using a different package besides Pygame. Thanks!
Further Research:
Running Sound in a Container
Docker Container Audio
Docker Tips – Play Audio in a Container (2022)
Maybe this is best handled in JS…
Leaving it for now…
I was able to Use the html <audio>
tag to generate audio successfully.
All of the controls work. This leads me to two complicated avenues I don’t quite have time to pursue, but will put a pin in…
- Audio-Deviceless File Render – Using the FluidSynth API, perhaps I could convert the MIDI to a WAV and play the audio using the html
audio
tags.midi2audio
seemed like a pythonic way to approach this, but seems configured on using ALSA just likepygame
. The FluidSynth API is coded in C, so I’m not sure how to bridge this. - Specify the device to be used by ALSA in the container – Perhaps I could specify –device in the Dockerfile to find the audio device and use a full Ubuntu image to launch it.
My Solution:
Fortunately, there is an awesome html MIDI player that solved this issue:
Here is my Flask HTML Template Code:
{% if MIDI_playback %}
<midi-player
src="{{ MIDI_playback }}"
sound-font visualizer="#myPianoRollVisualizer">
</midi-player>
<midi-visualizer type="piano-roll" id="myPianoRollVisualizer"
src="{{ MIDI_playback }}" style="color: white">
</midi-visualizer>
<script src="https://cdn.jsdelivr.net/combine/npm/[email protected],npm/@magenta/[email protected]/es`6/core.js,npm/focus-visible@5,npm/[email protected]">
</script>
An Important Note:
Using the script within this Python Flask App, I ran into this CORS error:
No ‘Access-Control-Allow-Origin’ header is present on the requested
resource. Origin ‘http://localhost:3000’ is therefore not allowed
access. If an opaque response serves your needs, set the request’s
mode to ‘no-cors’ to fetch the resource with CORS disabled
My workaround was to specify a CORS configuration in local and production instances.