Is there any crossbrowser solution for playing flac? (or is it possible in theory to make one)

前端 未结 2 1009
借酒劲吻你
借酒劲吻你 2021-02-19 19:40

Not interested in silverlight. Flash/javascript/html5 solutions are acceptable.

If you do not know such solutions, could you please say is it possible to make such that

2条回答
  •  难免孤独
    2021-02-19 20:03

    When I had to play FLAC in-browser, my starting point was also the Aurora framework.

    However, the Aurora player is geared around using ScriptProcessorNode to decode chunks of audio on the fly. This didn't pan out for many reasons.

    1. Seeking Flac in Aurora was never implemented.
    2. Stuttering and unacceptable performance in Firefox, even on a mid-range 2014 desktop.
    3. Not feasable to offload decoding to a WebWorker.
    4. Doesn't inter-operate with audio formats the browser does support.
    5. I didn't want to be responsible for re-sampling the sample-rate, seeking, and other low-level audio tasks that Aurora necessarily assimilates.

    Decoding offline: Flac to Wave

    My solution was to decode the Flac to raw 16bit PCM audio, using a stripped down Aurora.js Assset class + dependencies.
    Look in the source for Asset.get( 'format', callback ), Asset.fromFile, and Asset.prototype.decodeToBuffer.

    Next, take the audio data, along with extracted values for sample-rate and channel count, and build a WAVE file. This can be played using an HTML5 audio element, sent though an audio graph using createMediaElementSource, or absolutely anything you can do with natively supported audio formats.

    Note: Replace clz function in decoder.js with the native Math.clz32 to boost performance, and polyfill clz32 for old browsers.

    Disadvantage

    The decoding time. Around 5 seconds at ~100% CPU for an "average" 4min song.

    Advantages

    1. Blob (opposed to arraybuffer) isn't constrained by RAM, and the browser can swap it to disk. Original Flac data can likely be discarded too.
    2. You get seeking for free.
    3. You get sample-rate re-sampling for free.
    4. CPU activity paid for upfront in WebWorker.
    5. Should browsers EVER gain native Flac support, very easy to rip out. It doesn't create a strong dependency on Aurora.

    Here's the function to build the WAVE header, and turn the raw PCM data into something the browser can natively play.

    function createWave( audioData, sampleRate, channelCount )
    {
        const audioFormat  = 1, // 2    PCM = 1
        subChunk1Size= 16,      // 4    PCM = 16
        bitsPerSample= 16,      // 2
        blockAlign   = channelCount * (bitsPerSample >> 3), // 2
        byteRate     = blockAlign * sampleRate,             // 4
        subChunk2Size= blockAlign * audioData.size,         // 4
        chunkSize    = 36 + subChunk2Size,                  // 4
        // Total header size 44 bytes
        header = new DataView( new ArrayBuffer(44) );
    
    
        header.setUint32( 0, 0x52494646 ); // chunkId=RIFF
        header.setUint32( 4, chunkSize, true );
        header.setUint32( 8, 0x57415645 ); // format=WAVE
        header.setUint32( 12, 0x666d7420 ); // subChunk1Id=fmt
        header.setUint32( 16, subChunk1Size, true );
    
        header.setUint16( 20, audioFormat, true );
        header.setUint16( 22, channelCount, true );
    
        header.setUint32( 24, sampleRate, true );
        header.setUint32( 28, byteRate, true );
    
        header.setUint16( 32, blockAlign, true );
        header.setUint16( 34, bitsPerSample, true );
    
        header.setUint32( 36, 0x64617461 ); // subChunk2Id=data
        header.setUint32( 40, subChunk2Size, true );
    
        return URL.createObjectURL( new Blob( [header, audioData], {type: 'audio/wav'} ) );
    }
    

提交回复
热议问题