简体   繁体   中英

HTML File Input to Audio Buffer in JavaScript

I'm struggling to write just a simple HTML page with a form that takes an audio file as an input and passes it to a JavaScript file for manipulation.

What I Have

index.html

<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  </head>
  <body>
    <main role="main" class="container">
      <form action="#" method="POST" enctype="multipart/form-data" class="audio-input-form">
          <label>Audio File:</label>
          <input accept="audio/*" type="file" name="audio_file"/>
          <input type="submit" value="Get BPM" />
      </form>
    </main>
     <script type="text/javascript" src="app.js"></script>
  </body>
</html>

app.js

// Package from NPM I import
import { analyze } from 'web-audio-beat-detector';

## here I want to be able to get and manipulate the audio data

Desired Result

When the form is submitted, I just want the actual file data to be available in the JavaScript file (I guess as an arraybuffer). I wish to turn it into an AudioBuffer using this tutorial but it's unclear to me how to actually pass the data off.

I tried making the form action a function in my JS file and using an onClick function as well but neither seemed to work. Would I need some form of an XHR? I'm confused as to their purpose, I don't want any kind of server running for this, just an HTML page and a JavaScript file that runs with it in the browser.

I created a little example which doesn't even use a form. If you only have one input you can listen to its onchange event. This works at least for the purpose of this demo.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <input id="audio-file" accept="audio/*" disabled type="file"/>
        <script>
            import('https://dev.jspm.io/web-audio-beat-detector')
                .then(({ default: { analyze }}) => {
                    const $audioFileInput = document.getElementById('audio-file');

                    $audioFileInput.disabled = false;
                    $audioFileInput.onchange = async () => {
                        const file = $audioFileInput.files[0];

                        if (file !== undefined) {
                            $audioFileInput.disabled = true;

                            const arrayBuffer = await file.arrayBuffer();
                            const audioContext = new AudioContext();
                            const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);

                            alert(await analyze(audioBuffer));

                            $audioFileInput.disabled = false;
                        }
                    };
                });
        </script>
    </body>
</html>

I also loaded web-audio-beat-detector via jspm.io but in a real application you probably want to bundle it with something like webpack.

Creating an AudioContext each time you want to decode some audio data as I did it here is very wasteful. If you plan to decode multiple files it would make more sense to reuse one AudioContext.

Please also note that I only tested this in the latest version of Firefox and Chrome. Unfortunately it won't work in Safari though. Making it work in Safari would require much more code which is why I omitted it.

How big is the file? Ideally, you would analyze the first part of the file without requiring the entire file to be uploaded. Not sure how the beat detection algorithm works, but you could create a read buffer to obtain the first N bytes/seconds of the file to be analyzed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM