Search code examples
javascriptblobweb-audio-apiarraybuffer

How to download an array of audio files in one file


I'm trying to make a soundboard webapp where you can add audio files to the website and they will be stored inside of the indexed db to be then fetched and played.

I was thinking of also adding a feature that would let you download the whole pack of audio files as a single file so that other people would be able to import your pack. The data would look something like this:

const data = [
  {
    title: 'audio1',
    description: 'blabla'
    audio: AudioBuffer //or some other buffer
  },{
    title: 'audio2',
    description: 'blablabla'
    audio: AudioBuffer
  }
]

The question is, how would i go at downloading this as a single file? There is probably a way with blobs etc but i have no idea where to look for. Obviously i would also have to decode this back to be used


Solution

  • It's worth avoiding external libraries and keeping your application lean. Consider building a downloadable binary file structured like:

    *************************************************
    |  MARKER  |  META  |  AUDIO  |  AUDIO  |  ...  |  
    *************************************************
    
    // Example 32 MiB audio buffers
    const audio1 =   new Uint8Array(32 * 1024**2 /   Uint8Array.BYTES_PER_ELEMENT)
    const audio2 = new Float32Array(32 * 1024**2 / Float32Array.BYTES_PER_ELEMENT)
    ​
    // Metadata for your file
    const meta = new TextEncoder().encode(JSON.stringify([
      { title: 'audio1', length: audio1.byteLength }, 
      { title: 'audio2', length: audio2.byteLength }, 
    ]))
    ​
    // use 32-bit integer to store byte index where audio begins (metadata ends)
    const marker = new Uint32Array([
      meta.byteLength + 4 // include 4 bytes for this 32-bit integer
    ])
    ​
    function initDownload() {
      const a = document.createElement('a')
      a.href = URL.createObjectURL(new Blob([
        marker,
        meta,
        audio1,
        audio2,
      ], { type: 'application/octet-stream' }))
      a.download = 'saved-audio-project.bin'
      a.click()
    }
    
    function parseFile(buffer) {  // ArrayBuffer of file
      const metaLength = new DataView(buffer).getUint32(0, true)
      let readOffset = 4 // 4-byte marker length
      const audioFiles = JSON.parse(
        new TextDecoder().decode(
          buffer.slice(readOffset, readOffset += metaLength)
        )
      )  
      audioFiles.forEach(audio => audio.data = new Float32Array(
        buffer.slice(readOffset, readOffset += audio.length)
      ))
      return audioFiles
    }