Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cant get length of m4a audio file on webpage

I am trying to make a nodejs website that will return the length in seconds of any audio file a user chooses. So far I have it working with mp3, wav, and flac files. But it doesn't work for all .m4a or .aif files

The code for my HTML page with javascript is below:

choose audio file to get length:
<input style="cursor: pointer;" type="file" id="file" multiple="multiple" />

<script>

    //when files are selected:
    $("#file").change(async function (e) {
        console.log('file(s) selected')
        //get files 
        var files = e.currentTarget.files;
        //get number of files
        var numberOfFiles = files.length;
        //for each file
        for (i = 0; i < numberOfFiles; i++) {
            console.log(`songs[${i}].type=`, files[i].type)
            //get file length
            let songLength = await getSongLength(files[i]);
            console.log('songLength=', songLength)
        }

    });

    //recieve audio file, return length
    function getSongLength(song) {
        return new Promise(function (resolve, reject) {
            console.log('getSongLength() begin setup')
            //create objectURL and audio object for ssong
            objectURL = URL.createObjectURL(song);
            mySound = new Audio([objectURL])
            console.log('getSongLength() end setup')
            //when song metadata is loaded:
            mySound.addEventListener("canplaythrough", function (e) {
                console.log('getSongLength() canplaythrough')
                var seconds = e.currentTarget.duration;
                resolve(seconds)
            });

        });
    }

</script>

I gathered 6 different files for testing, and after running them on my above code have found out the following results:

  • aif: not working
  • flac: working
  • m4a_file: not working
  • m4a_file_from_comments_below:not working
  • mp3: working
  • wav: working

my test files for download: https://easyupload.io/m/la9xro

It seems like when I input my m4a file sample_M4A_file that it hangs inside the getSongLength() function and never enters the .addEventListener("canplaythrough" function, is there any alternative I can use to consistently get the duration in seconds for every audio file?

enter image description here

like image 976
Martin Avatar asked Oct 21 '20 03:10

Martin


1 Answers

It's because your browser doesn't support Apple Lossless codecs. If you try in Safari, it will work.
If you attach an error event to your media element, you'll see it fires.

There is no real universal solution, apart from using an heavy machinery like mediainfo.js (2.4MB+) which should support most formats.

const input = document.querySelector( "input" );
input.onchange = async (evt) => {
  const mediainfo = await new Promise( (res) => MediaInfo(null, res) );

  const file = input.files[ 0 ];
  const getSize = () => file.size;
  const readChunk = async (chunkSize, offset) =>
    new Uint8Array( await file.slice(offset, offset + chunkSize).arrayBuffer() );

  const info = await mediainfo.analyzeData(getSize, readChunk);
  // assumes we are only interested in audio duration
  const audio_track = info.media.track.find( (track) => track[ "@type" ] === "Audio" );
  console.log( audio_track.Duration );
};
<script src="https://unpkg.com/[email protected]/dist/mediainfo.min.js"></script>
<input type="file">

Ps: when using the media element solution, there is no need to wait for canplaythrough, just wait for loadedmetadata, and if you get Infinity, try this hack.

like image 154
Kaiido Avatar answered Sep 28 '22 20:09

Kaiido