Home > Software design >  RuntimeError: Error opening <FileStorage: 'audio_file.wav' ('audio/wav')>:
RuntimeError: Error opening <FileStorage: 'audio_file.wav' ('audio/wav')>:

Time:07-20

I am trying to record audio via webbrowser for 6 seconds and send it to flask. The audio file then gets converted into a Spectogramm. Afterwards it should be transformed into a Numpy Array to be processed for a model.

I do not know if the problem is my upload function or something with librosa

I tried several different versions and options but cannot manage.

This is JS code:

function lDUp(){
    var formData = new FormData();

    const startButton = document.getElementById("startLungBtn");
    startButton.addEventListener("click", function() {
      if (!navigator.mediaDevices) {
        console.error("getUserMedia not supported.")
        return;
      }
  
    const constraints = { audio: true };
    navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
        var chunks = [];
        const recorder = new MediaRecorder(stream);
        recorder.ondataavailable = event => {
            // Collect all the chunks of the recording in an array.
            chunks.push(event.data);
        };

        const audioContext = new AudioContext();
        const audioStreamSource = audioContext.createMediaStreamSource(stream);
        


        recorder.onstop = event => {
            console.log("Recording stopped.")
            // Create a blob with all the chunks of the recording.
            let blob = new Blob(chunks, { type: "audio/wav" }); 

            // let blob = new Blob(chunks, { type: recorder.mimeType }); 
            chunks = [];
            startButton.disabled = false;
  
            // Create form data that contain the recording.
            formData.append("audio_file", blob, "audio_file.wav");

            $.ajax({
                
                type: "POST",
                url: "http://127.0.0.1:5000/LungUploader",
                processData: false,
                contentType: false,
                cache: false,
                data: formData,
                success: function(response) {
                console.log(response);
                console.log("This is class: ", response)
                },
                error: function(err) {
                    console.log(err);
                    }
            });   
            

          };
        recorder.onstart = event => {
            console.log("Recording started.");
            startButton.disabled = true;
            // Stop recording when the time is up.
            setTimeout(function() { recorder.stop(); }, 6000);
            
                        
        };
        recorder.start();
    })
    .catch(function(err) {
          console.error(err);
      });
    });
        
    
}

This is code for flask:

@app.route('/LUploader', methods=["GET", 'POST'])
def lUploader():

    if request.method == "POST":

        f = request.files["audio_file.wav"]
        with open('audio.wav', 'wb') as audio:
            f.save(audio)


        print("Checkpoint1")
        y, sr = lb.load(f)

        print("Checkpoint")
        #Plot signal in
        plt.figure(figsize=(10,3))
        src_ft = lb.stft(y)
        src_db = lb.amplitude_to_db(abs(src_ft))
        specshow(src_db, sr=sr, x_axis='time', y_axis='hz')  
        plt.ylim(0, 5000)        
        plt.close()
}

Thanks for help in advance :)

CodePudding user response:

You're trying to get the file by its filename. In order to receive the file within the form, the name of the input field is required. In your case this is "audio_file".

f = request.files['audio_file']

You will then get a file-like object of type werkzeug.datastructures.FileStorage, which can be saved with the save function.

f.save(f.filename)

You should distinguish between the name of the form field and the filename of the file sent. The name of the input field is required to request the file from the form (example: "audio_file"). The file name is the name under which the file is saved and loaded on the server (example: "audio_file.wav").

The code should look something like this.

from flask import (
    Flask, 
    request, 
    # ...
)
from werkzeug.utils import secure_filename
import os

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = os.path.join(
    app.instance_path, 
    'uploads'
)
try: 
    os.makedirs(app.config['UPLOAD_FOLDER'])
except: 
    pass 

# ...

@app.post('/upload')
def upload():
    # Get the FileStorage object with the name of the form's input field.
    file = request.files.get('audio_file')
    if file and file.filename != '': 
        dest = os.path.join(
            app.config['UPLOAD_FOLDER'], 
            secure_filename(file.filename)
        )
        # Save the file on the server.
        f.save(dest)
        
        # Load the file by filename using librosa.
        y,sr = librosa.load(dest)
        
        # ...

Detailed instructions for uploading files can be found here.


I wrote an example application for uploading, playing audio files and displaying the spectogram here. Please note that the audio format used depends on the platform and browser.

Flask
'''
pip install flask librosa numpy scikit-image
'''

from flask import (
    Flask,
    Response,
    abort,
    current_app,
    render_template,
    redirect,
    request,
    send_from_directory,
    stream_with_context,
    url_for
)
from collections import namedtuple
from glob import glob
from mimetypes import add_type, guess_extension, guess_type
from werkzeug.utils import secure_filename
import librosa
import numpy
import os
import skimage.io


add_type('audio/aac', '.m4a', strict=True)

Record = namedtuple('Record', ('filename', 'specimage', 'created_at'))

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
try:
    os.makedirs(os.path.join(
        app.instance_path,
        app.config.get('UPLOAD_FOLDER', 'uploads')
    ))
except:
    pass

@app.route('/')
def audio_index():
    patterns = [
        '*.m4a',
        '*.wav',
        '*.weba'
    ]
    path = os.path.join(
        current_app.instance_path,
        current_app.config.get('UPLOAD_FOLDER', 'uploads')
    )
    records = [
        Record(fn[len(path) 1:], f'{fn[len(path) 1:]}.png', os.path.getctime(fn)) \
        for ptrn in patterns for fn in glob(os.path.join(path, ptrn))
    ]
    return render_template('index.html', records=records)

@app.route('/audio-upload', methods=['POST'])
def audio_upload():
    if 'audio_file' in request.files:
        file = request.files['audio_file']
        extname = guess_extension(file.mimetype)
        if not extname:
            abort(400)

        # WARNING: Check for allowed file extensions.

        i = 1
        while True:
            dst = os.path.join(
                current_app.instance_path,
                current_app.config.get('UPLOAD_FOLDER', 'uploads'),
                secure_filename(f'audio_record_{i}{extname}'))
            if not os.path.exists(dst): break
            i  = 1

        file.save(dst)

        img = os.path.join(
            current_app.instance_path,
            current_app.config.get('UPLOAD_FOLDER', 'uploads'),
            secure_filename(f'{os.path.basename(dst)}.png')
        )
        hop_length = 512
        n_mels = 128
        time_steps = 384
        y, sr = librosa.load(dst)
        spectrogram_image(y, sr=sr, out=img, hop_length=hop_length, n_mels=n_mels)

    return redirect(url_for('audio_index'))

def spectrogram_image(y, sr, out, hop_length, n_mels):
    mels = librosa.feature.melspectrogram(
        y=y, sr=sr, n_mels=n_mels,
        n_fft=hop_length*2, hop_length=hop_length
    )
    mels = numpy.log(mels   1e-9)
    img = scale_minmax(mels, 0, 255).astype(numpy.uint8)
    img = numpy.flip(img, axis=0)
    img = 255-img
    skimage.io.imsave(out, img)

def scale_minmax(X, min=0.0, max=1.0):
    X_std = (X - X.min()) / (X.max() - X.min())
    X_scaled = X_std * (max - min)   min
    return X_scaled

@app.route('/audio/stream/<path:fn>', methods=['GET'])
def audio_download(fn):
    @stream_with_context
    def generator(src):
        CHUNK_SIZE = 1024
        with open(src, 'rb') as fp:
            while True:
                data = fp.read(CHUNK_SIZE)
                if not data: break
                yield data

    src = os.path.join(
        current_app.instance_path,
        current_app.config.get('UPLOAD_FOLDER', 'uploads'),
        fn
    )

    if not os.path.exists(src):
        return abort(404)

    mime,_ = guess_type(src)
    return Response(generator(src), mimetype=mime)

@app.route('/audio/spec/<path:fn>')
def audio_spec(fn):
    path = os.path.join(
        current_app.instance_path,
        current_app.config.get('UPLOAD_FOLDER', 'uploads')
    )
    return send_from_directory(path, fn)
HTML (templates/index.html)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Audio Index</title>
  </head>
  <body>
      <div >
        <div >
          <button  id="toggle-rec-btn">Record</button>
        </div>
        <div >
          {% for record in records|sort(attribute='created_at', reverse=True) -%}
          <div >
            <div >
              <span >{{ record.filename }}</span>
              <audio
                controls
                src="{{ url_for('audio_download', fn=record.filename) }}"
              ></audio>
            </div>
            <div >
              <img src="{{ url_for('audio_spec', fn=record.specimage) }}" />
            </div>
          </div>
          {% endfor -%}
        </div>
    </div>

    <script type="text/javascript">
    (function() {
      const uploadURL = {{ url_for('audio_upload') | tojson }};
      const startButton = document.getElementById("toggle-rec-btn");
      startButton.addEventListener("click", function() {
        if (!navigator.mediaDevices) {
          console.error("getUserMedia not supported.")
          return;
        }

        const constraints = { audio: true };
        navigator.mediaDevices.getUserMedia(constraints)
        .then(function(stream) {
            let chunks = []
            let recorder = new MediaRecorder(stream);
            recorder.ondataavailable = event => {
                chunks.push(event.data);
            };
            recorder.onstop = event => {
              console.log("Recording stopped.")
              let blob = new Blob(chunks, { type: recorder.mimeType });
              chunks = [];
              startButton.disabled = false;

              let formData = new FormData();
              formData.append("audio_file", blob);

              fetch(uploadURL, {
                method: "POST",
                cache: "no-cache",
                body: formData
              }).then(resp => {
                if (resp.status === 200) {
                  window.location.reload(true);
                } else {
                  console.error("Error:", resp)
                }
              }).catch(err => {
                console.error(err);
              });
            };
            recorder.onstart = event => {
              console.log("Recording started.");
              startButton.disabled = true;
              setTimeout(function() { recorder.stop(); }, 10000);
            };
            recorder.start();
        })
        .catch(function(err) {
            console.error(err);
        });
      });
    })();
    </script>
  </body>
</html>
  • Related