zhuker / lamejs

mp3 encoder in javascript
Other
828 stars 199 forks source link

In browser conversion #4

Open devjayhall opened 9 years ago

devjayhall commented 9 years ago

Firstly this is an awesome library.

I'm trying to do an in browser conversion. Don't know what's wrong here, could you please assist?

see html below

<!DOCTYPE html>
<html>
  <head>
  <style>
  #originalPlayer, #converted {display: none;}
audio {display: block;}
input, button, p {margin: 10px 0;}
  </style>
    <script src="lame.min.js"></script>
    <meta charset="utf-8">

  </head>
  <body>
    <div> 
      <input id="audioIn" type="file" />  
      <audio id="originalPlayer" controls>
        </audio>
    </div> 
    <button id="convertBt" disabled>
      Convert
    </button>
    <div id="converted">
      <p>Converted to 
      <a id="convertedLink" download></a></p>
      <audio id="convertedPlayer" controls>
      </audio>
    </div> 
    <script>

    var file;
var load = function(){
  file = this.files[0];
  var src = URL.createObjectURL(file); 
  originalPlayer.src = src;
  originalPlayer.style.display = 'block';
  convertBt.disabled = false;
};
audioIn.addEventListener('change',load);

var read = function(){
  var fileReader = new FileReader();
  fileReader.addEventListener('load', convert);
  fileReader.readAsArrayBuffer(file);
};
convertBt.addEventListener('click',read);

liblame = new lamejs();
    function encodeMono(channels, sampleRate, samples) {
        mp3enc = new liblame.Mp3Encoder(channels, sampleRate, 128);
        var remaining = samples.length;
        var maxSamples = 1152;
        for (var i = 0; remaining >= maxSamples; i += maxSamples) {
            var mono = samples.subarray(i, i + maxSamples);
            var mp3buf = mp3enc.encodeBuffer(mono);
            if (mp3buf.length > 0) {
                //TODO write to output
            }
            remaining -= maxSamples;
        }
        var mp3buf = mp3enc.flush();

    var url = 'data:audio/mp3;base64,'+encode64(mp3buf);
    convertedPlayer.src = url;
    convertedLink.href = url;

    var name = file.name.substr(0, file.name.lastIndexOf('.'));
    convertedLink.textContent = name + '.mp3';

    converted.style.display = 'block';

        console.log('done encoding');
    }

var convert = function(){
    audioData = this.result;
        wav = liblame.WavHeader.readHeader(new DataView(audioData));
        console.log('wav:', wav);
        samples = new Uint16Array(audioData, wav.dataOffset, wav.dataLen / 2);
        encodeMono(wav.channels, wav.sampleRate, samples);
};
function encode64(buffer) {
  var binary = '',
      bytes = new Uint8Array( buffer ),
      len = bytes.byteLength;

  for (var i = 0; i < len; i++) {
    binary += String.fromCharCode( bytes[ i ] );
  }
  return window.btoa( binary );
}
</script>
  </body>
</html>
zhuker commented 9 years ago

note you have //TODO write to output this is where you actually write mp3
after flush its the last mp3 frame - naturally it would output silence

devjayhall commented 9 years ago

what would be the best way to write to output?

devjayhall commented 9 years ago

i've tried the method below but the audio sounds a bit distorted, assist me please

var output= mp3enc.encodeBuffer(mono);
zhuker commented 9 years ago

what is the application you are trying to write? wav file playback?

devjayhall commented 9 years ago

a wav to mp3 file converter

zhuker commented 9 years ago

is your wav file mono or stereo?

devjayhall commented 9 years ago

so far i've been using Left44100.wav as my test file, but i would like to do stereo

calipoop commented 9 years ago

Is it possible to give us a hint or sample code on how to write the mp3 to disk? Trying just a short mono wav file, encoded to mp3 using lamejs. I successfully reach the end of encoding, but can't force a download with "createObjectURL". I still have much to learn with blobs, etc... Thanks in advance!

shanewho commented 9 years ago

@calipoop If you can successfully get an object url from the blob, just set that url as the href of an tag. Clicking on that will download the mp3. Set the content-type in the blob to something like application/octet-stream instead of media/mpeg to have the browser download instead of playing directly in the browser.

calipoop commented 9 years ago

Thanks @shanewho - I've successfully downloaded files before (using the HTML5 download tag targeting modern browsers). Trouble is I can't get the object url from the blob, or maybe I'm not creating the blob correctly. I've tried multiple versions of this code (I think mp3buf is the final encoded mp3):

var typedArray = new Int8Array(mp3buf);
var blob = new Blob([typedArray.buffer], {type: 'audio/mpeg'});
var url = URL.createObjectURL(blob);

The downloaded result ends up being a short burst of noise, so I must be doing something wrong...

shanewho commented 9 years ago

You can pass the mp3buf directly to the blob (in an array). Try this instead:

var blob = new Blob([mp3buf], {type: 'audio/mpeg'});
var url = URL.createObjectURL(blob);
TransLucida commented 9 years ago

we have this push-to-record script implemented which whoever wrote it used a slightly modified version of this script [http://typedarray.org/from-microphone-to-wav-with-getusermedia-and-web-audio/]. it works just fine but the files it generates are in wav and huge, even in mono. I've been trying to use this library to output mp3 files instead of wav but I'm confused as to which point in that script I should insert the mp3 encoding script. I don't need to keep the wav file. I'm not ashamed to say it's outside my comfort zone and I would very much appreciate any help. thanks in advance.