0

The Problem:
I am currently deploying a Python Flask App using a Dockerfile on a PaaS (Heroku like). The User Story is as follows:

  1. User Clicks 'Generate MIDI'
  2. MIDI is generated and User can (on the same page):
    1. 'Download MIDI' (currently works perfectly locally & PaaS deployment)
    1. '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...

  1. 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.
  2. 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.
  • There is no point in adding tags to the title of the question. – Rabbid76 Jan 06 '23 at 19:14
  • Do those `pygame.mixer` calls run as part of the server code? I'd expect the Heroku instance doesn't have usable audio hardware, Docker concerns aside. That is, I don't think this setup will work if the browser and server are on different systems, even if containers aren't involved. – David Maze Jan 06 '23 at 22:56
  • Yeah, that's what I'm thinking, too @DavidMaze :( . I have a post in the PaaS forum (DigitalOcean) to see if I have any options. I was hoping I could docker run --device tags and use the server machine / host or something. Maybe I could make something work if create a Droplet / Virtual Machine... – Christopher Burrows Jan 07 '23 at 06:04

1 Answers1

1

My Solution:

Fortunately, there is an awesome html MIDI player that solved this issue:

html-midi-player

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/tone@14.7.58,npm/@magenta/music@1.23.1/es`6/core.js,npm/focus-visible@5,npm/html-midi-player@1.5.0">
    </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.