13

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 or not?

avasin
  • 9,186
  • 18
  • 80
  • 127
  • 2
    Support for FLAC landed in chrome recently. The only way to know where it works is by checking manually. [Here's a site to help with that.](http://hpr.dogphilosophy.net/test/) – 0xcaff Jan 01 '16 at 19:32
  • [Added native support for FLAC](https://www.mozilla.org/en-US/firefox/51.0/releasenotes/) in Firefox 51.0 – cespon Jan 24 '17 at 17:14

2 Answers2

7

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'} ) );
}
Community
  • 1
  • 1
Adria
  • 8,651
  • 4
  • 37
  • 30
  • http://codepen.io/enjikaka/pen/avVKGz - I get alot of white noise. Do you know why? – Jeremy Karlsson Oct 19 '15 at 18:23
  • Sorry it's been a year since I touched this, and I no longer have samples. Needless to say the audio data has to be raw 16bit PCM data, with accurate sample rate, and channel count. It's quite brittle. Anything slightly wrong and you'll hear corrupted audio. – Adria Dec 31 '15 at 04:50
4

A simple Google search led me to these sites:

Aurora and FLAC.js — audio codecs using the Web Audio API

Introducing FLAC.js: A Pure JavaScript FLAC Decoder

Believe it or not, it wasn't so hard.

Almost forgot:
Check HTML5Test to compare browsers performance/compatibility with the <audio> tag and it's siblings.

Scott Stensland
  • 26,870
  • 12
  • 93
  • 104
trinaldi
  • 2,872
  • 2
  • 32
  • 37
  • Wow! I thought it is not possible to make audio in js from binary data! It will not support IE, but Chrome/Safari/Firefox are supported. Good reason to ban all IE browser requests. Thanks a lot! – avasin Dec 13 '13 at 00:41
  • Unfortunately, IE is a very "orthodox" browser! Good thing people are using it less frequently. If my answer fulfill your question, mark it as answer ;) – trinaldi Dec 13 '13 at 00:45
  • Have fun! It looks promising. – trinaldi Dec 13 '13 at 00:58
  • @avasin Did you manage to figure out how to use FLAC.js? I don't see the API specification or any guide anywhere. – RaminS Aug 16 '16 at 20:45
  • @Tico But Aurora is *a framework that makes writing audio decoders in JavaScript easier*. It's not the decoder itself. FLAC.js is the decoder. I don't want to write my own decoder, I just want to use FLAC.js. – RaminS Aug 16 '16 at 22:04