Giter VIP home page Giter VIP logo

Comments (19)

calipoop avatar calipoop commented on July 18, 2024 10

Thanks @shanewho ! I actually just solved the problem too, hi-5 for both of us!

To anyone else interested, it seems we're required to convert Float32Array to Int16Array. To do that, I used a function I found on StackOverflow ( http://stackoverflow.com/a/29342909/2434324 ). This function looks very similar to the answer you posted.

var data = buffer.getChannelData(0);
var len = data.length, i = 0;
var dataAsInt16Array = new Int16Array(len);

while(i < len){
     dataAsInt16Array[i] = convert(data[i++]);
}
function convert(n) {
     var v = n < 0 ? n * 32768 : n * 32767;       // convert in range [-32768, 32767]
     return Math.max(-32768, Math.min(32768, v)); // clamp
}

var samples = dataAsInt16Array;

Thanks again for all your help - and for workin on a Saturday! 😆

from lamejs.

shanewho avatar shanewho commented on July 18, 2024 5

OHHHH I forgot about that. I just found this code hidden in a function I didn't realize was getting called and I had this exact same problem when I tried this. Those are values from -1 to +1 but lamejs expects them to be much higher. So basically you have the right data it is just really really quiet. Try something like this:

        var lo = this.leftBuffer; //the decoded data range: -1 +1
        var ro = this.rightBuffer;
        var l = new Float32Array(lo.length); //The transformed data, this is what you will pass to lame instead
        var r = new Float32Array(ro.length);

        //Convert to required format
        for(var i=0;i<lo.length;i++) {
            l[i] = lo[i]*32767.5;
            r[i] = ro[i]*32767.5;
        }

Basically loop through all of your decoded wave data (the small numbers) and make them larger by the same amount. I bet that will do it.

from lamejs.

javismiles avatar javismiles commented on July 18, 2024 1

Thank you everybody for this great post, following the advice in this thread i made it work with the code you see here below, now it records and encodes in real time from mic and then uploads and writes to server an mp3 successfully, yay, however i have two issues and a question:

a) the very beginning of recording gets distorted and a bit cut, the last second of recording also gets cut, so if i record 8 seconds, the first half second and last half second get cut, and also the first second has breaks-cuts in it, the intermediate 6 seconds are ok

b) sound seems to sound somehow lower frequency than the voice recorded, is that an EQ issue or an encoder issue or is there a problem in my code?

c) final thing, at the moment the encoding is not happening in a web worker, how could i modify this code to make the encoding happen in the background in a web worker to improve performance etc?

thank you so so so much and here below is my code:

navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;

liblame = new lamejs(); var mymp3Encoder=new liblame.Mp3Encoder(1,44100,320);

function upload(blobOrFile) {
var form = new FormData(); form.append("blobbie", blobOrFile);
var xhr = new XMLHttpRequest(); xhr.open('POST', './upload.php', true);xhr.send(form);

xhr.onreadystatechange = function(e) {
if (xhr.readyState == 4) { if (xhr.status==200) { cap=xhr.responseText; }}};
xhr.onload = function(e) {};
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {a = (e.loaded / e.total) * 100;
console.log (e.loaded+"/"+e.total+"/"+a);}};
}

function record() {
thetext=$("#statusbit");
navigator.getUserMedia({audio: true}, function (stream) {
var stopRecording = beginRecording(stream);
var start = +new Date();
thetext.text('Recording... 3.0');
// Run a function every 100 ms to update the UI
var timer = setInterval(function () {
var duration = new Date() - start;
if (duration < 8000) {
thetext.text('Recording... ' + ((8000 - duration) / 1000).toFixed(1));
} else {
var buffers = stopRecording();
clearInterval(timer);
console.log ("Get MP3");
mp3Buffer=mymp3Encoder.flush();
if (mp3Buffer.length>0) buffers.push(mp3Buffer);
var blob=new Blob(buffers, {type: 'audio/mpeg'});
upload(blob);

                }
            }, 100);
        }, function (error) {thetext.text('Error! Try again.');});

}

    function beginRecording(stream) {
        // Set up Web Audio API to process data from the media stream (microphone).
        var context = new AudioContext();
        var microphone = context.createMediaStreamSource(stream);
        var processor = context.createScriptProcessor(0, 1, 1);
        var buffers = [];
        processor.onaudioprocess = function (event) {
            var data = event.inputBuffer.getChannelData(0);
            var lo = data; //the decoded data range: -1 +1
            var l = new Float32Array(lo.length); 
            for(var i=0;i<lo.length;i++) {l[i] = lo[i]*32767.5;}
            var buffer = mymp3Encoder.encodeBuffer(l);
            buffers.push(buffer)
        };
        // Begin retrieving microphone data.
        microphone.connect(processor);
        processor.connect(context.destination);
        // Return a function which will stop recording and return all MP3 data.
        return function stopRecording() {
            console.log ("FINISHED RECORDING");
            // Clean up the Web Audio API resources.
            if (context.close) context.close();
            microphone.disconnect();
            processor.disconnect();
            processor.onaudioprocess = null;
            // Return the buffers array. Note that there may be more buffers pending here.
            console.log ("Finished cleaning");
            return buffers;
        };
    }

from lamejs.

javismiles avatar javismiles commented on July 18, 2024 1

ok now i know why, it was simple,
rate of encoder has to match rate of audio context, so with code below all works well always

var context = new AudioContext();
var conrate=context.sampleRate;
console.log("mic sample rate:"+conrate);
liblame = new lamejs(); var mymp3Encoder=new liblame.Mp3Encoder(1,conrate,128);

new version is here
http://torchprinciple.com/tests/rec1/alexrec.html

now i just wish to put the encoding process in a web worker which im not sure how to do

from lamejs.

shanewho avatar shanewho commented on July 18, 2024

This has worked for me (getting samples from getChannelData(0)). This isn't a full sample but basically what I do is this:

var sampleRate = audioBuffer.sampleRate;
var numberOfChannels = audioBuffer.numberOfChannels;
var ld = audioBuffer.getChannelData(0);
var rd = numberOfChannels > 1 ? audioBuffer.getChannelData(1) : null;
//this code has a bug, assumes 2 channel
function lameEncode(l, r, sampleRate, progress) {
        var liblame = new lamejs();
        var mp3Encoder = new liblame.Mp3Encoder(2, sampleRate, 128); //2 channel hard coded :-/

        var blockSize = 1152;
        var blocks = [];
        var mp3Buffer;

        var length = l.length;
        for (var i = 0; i < length; i += blockSize) {
            progress((i/length)*100);
            var lc = l.subarray(i, i + blockSize);
            var rc = r.subarray(i, i + blockSize);
            mp3Buffer = mp3Encoder.encodeBuffer(lc, rc);
            if (mp3Buffer.length > 0) blocks.push(mp3Buffer);
        }
        mp3Buffer = mp3Encoder.flush();   
        if (mp3Buffer.length > 0) blocks.push(mp3Buffer);
        progress(100);
        return new Blob(blocks, {type: 'audio/mpeg'});
    }

from lamejs.

calipoop avatar calipoop commented on July 18, 2024

Thanks @shanewho for all the assistance on this. I tried your code, but it looks like the same thing happens - I get an mp3 of silence. But the play length seems right! It looks like a valid mp3 file: 48000 Hz, 128 kb/s, mono mp3 - It's just that it's silent...

I'm trying to figure out the difference between what I'm using and the example code. Probably the only thing I can see is different Sample Rate (my machine uses 48000 and so the audioBuffer I created is 48000) The example uses a source wav with 44100 sample rate. Could that be the issue? Still debugging. Here's your code I plugged into mine, for a mono audioBuffer:

        var sampleRate = buffer.sampleRate;
    var numberOfChannels = buffer.numberOfChannels;
    var ld = buffer.getChannelData(0);
    var rd = numberOfChannels > 1 ? buffer.getChannelData(1) : null;
    blob = lameEncode(ld, rd, sampleRate);
    forceDownload();

    function lameEncode(l, r, sampleRate) {
        var liblame = new lamejs();
        var mp3Encoder = new liblame.Mp3Encoder(numberOfChannels, sampleRate, 128); //2 channel hard coded :-/

        var blockSize = 1152;
        var blocks = [];
        var mp3Buffer;

        var length = l.length;
        for (var i = 0; i < length; i += blockSize) {
            var lc = l.subarray(i, i + blockSize);
            //var rc = r.subarray(i, i + blockSize);
            mp3Buffer = mp3Encoder.encodeBuffer(lc);
            if (mp3Buffer.length > 0) blocks.push(mp3Buffer);
        }
        mp3Buffer = mp3Encoder.flush();   
        if (mp3Buffer.length > 0) blocks.push(mp3Buffer);
        return new Blob(blocks, {type: 'audio/mpeg'});
    }     

    function forceDownload(){
        console.log('forceDownload');
        //var blob = new Blob(mp3Data, {type:'audio/mpeg'});
        var url = (window.URL || window.webkitURL).createObjectURL(blob);
        var link = window.document.createElement('a');
        link.href = url;
        link.download = 'output.mp3';
        var click = document.createEvent("Event");
        click.initEvent("click", true, true);
        link.dispatchEvent(click);
    }

from lamejs.

shanewho avatar shanewho commented on July 18, 2024

With the code you just posted (mixed with mine), you are only passing the left buffer to encode so you'd have problems if numberOfChannels wasn't equal to 1. But I doubt that's the issue. Check your input samples to see if they look like what you'd expect. It might be that it is actually encoding the audio but it is really quiet. I think the floats should go from -1 to +1, so if they are all really close to 0, you might have an issue of just really quiet sound. Other than that, I have no idea, sorry!

from lamejs.

calipoop avatar calipoop commented on July 18, 2024

ok @shanewho & @zhuker, I've created a test case that should make it obvious (though not to me) what's wrong. The following code works for fn2, but not for fn1. Everything else about the code is the same for both cases. fn1 uses Web Audio API: AudioContext.decodeAudioData() and fn2 uses a function built into lamejs, as well as explicitly creating an Int16Array. When I compare the mp3Data for both, both arrays are the same length, but different slightly different values. Any thoughts?

    <script src='lame.all.js'></script>
    <script>
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        var context = new AudioContext();
        var lib = new lamejs();
        var mp3Data = [];
        var xhr = new XMLHttpRequest();
        xhr.open("get", "testdata/Left.wav", true);
        xhr.responseType = "arraybuffer";
        xhr.addEventListener('load', fn1); /////////////////////////////////
        //xhr.addEventListener('load', fn2); ////////////////////////////////
        xhr.send();

        function fn1(e){
            //THIS FUNCTION USES WEB AUDIO API: AudioContext.decodeAudioData();
            context.decodeAudioData(e.target.response, function(buffer){
                encodeAudio(buffer.getChannelData(0), buffer.numberOfChannels, buffer.sampleRate);
            });
        }

        function fn2(e){
            //THIS FUNCTION USES LAMEJS FUNCTION TO READ WAV HEADER AND CREATE INT16ARRAY;
            var buffer = e.target.response;
            var wav = lib.WavHeader.readHeader(new DataView(buffer));
            var samples = new Int16Array(buffer, wav.dataOffset, wav.dataLen / 2);
            encodeAudio(samples, wav.channels, wav.sampleRate);
        }

        function encodeAudio(samples, channels, sampleRate){
            var kbps = 128;
            var mp3encoder = new lib.Mp3Encoder(channels, sampleRate, kbps);
            var sampleBlockSize = 1152;
            var sampleChunk;

            for(var i = 0; i < samples.length; i += sampleBlockSize){
                sampleChunk = samples.subarray(i, i + sampleBlockSize);
                var mp3buf = mp3encoder.encodeBuffer(sampleChunk);
                if(mp3buf.length > 0){
                    mp3Data.push(mp3buf);
                }
            }
            mp3buf = mp3encoder.flush();

            if(mp3buf.length > 0){
                mp3Data.push(mp3buf);
                forceDownload();
                console.debug(mp3Data);
            }
        }

        function forceDownload(){
            var blob = new Blob(mp3Data, {type:'audio/mpeg'});
            var url = (window.URL || window.webkitURL).createObjectURL(blob);
            var link = window.document.createElement('a');
            link.href = url;
            link.download = 'output.mp3';

            var click = document.createEvent("Event");
            click.initEvent("click", true, true);
            link.dispatchEvent(click);
        }

    </script>

from lamejs.

calipoop avatar calipoop commented on July 18, 2024

With the script above (the same wav source), it seems that LAMEJS's samples look like this:

[0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 0, 0, 1, 1, 0, -1, -1, -1, -1, 0, 0, 0, 2, 3, 3, 2, 2, 2, 2, 2, 1, 0, -2, -2, -2, -3, -5, -5, -6, -5, -4, -2, 0, 2, 2, 2, 1, -1, -5, -7, -8, -7, -7, -7, -8, -5, -1, 1, 1, 2, 2, 0, -2, -5, -6, -6, -3, 1…]

while buffer.getChannelData(0)'s samples look like this:

[0, 0, 0, 0, 0, 0, 0, 0, 0, -0.000030517578125, -0.000030517578125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.000030518509447574615, 0.000030518509447574615, 0.000030518509447574615, 0.000030518509447574615, 0.000030518509447574615, 0.000030518509447574615, 0.000030518509447574615, 0.00006103701889514923, 0.000030518509447574615, 0, 0, 0, 0.000030518509447574615, 0.000030518509447574615, 0, -0.000030517578125, -0.000030517578125, -0.000030517578125, -0.000030517578125, 0, 0, 0, 0.00006103701889514923, 0.00009155552834272385, 0.00009155552834272385, 0.00006103701889514923, 0.00006103701889514923, 0.00006103701889514923, 0.00006103701889514923, 0.00006103701889514923, 0.000030518509447574615, 0, -0.00006103515625, -0.00006103515625, -0.00006103515625, -0.000091552734375, -0.000152587890625, -0.000152587890625, -0.00018310546875, -0.000152587890625, -0.0001220703125, -0.00006103515625, 0, 0.00006103701889514923, 0.00006103701889514923, 0.00006103701889514923, 0.000030518509447574615, -0.000030517578125, -0.000152587890625, -0.000213623046875, -0.000244140625, -0.000213623046875, -0.000213623046875, -0.000213623046875, -0.000244140625, -0.000152587890625, -0.000030517578125, 0.000030518509447574615, 0.000030518509447574615, 0.00006103701889514923, 0.00006103701889514923, 0, -0.00006103515625, -0.000152587890625, -0.00018310546875, -0.00018310546875, -0.000091552734375, 0.000030518509447574615…]

Why, and how can this be fixed? Presumably it involves LAMEJS's Int16Array vs audioBuffer's Float32Array? @shanewho You mentioned getChannelData() worked for you - how were you able to overcome the difference above? Why didn't @zhuker use decodeAudioData() to read the loaded wav? So many questions! 😖

from lamejs.

shanewho avatar shanewho commented on July 18, 2024

👍

FWIW, you can use a Float32Array in -32768 32767 (not just an Int16Array) and keep the decimals if needed/wanted. Probably won't notice any difference converting to int16 but might lose some minor fidelity in the rounding.

from lamejs.

zhuker avatar zhuker commented on July 18, 2024

good times @shanewho, thanks for your input
@calipoop problem solved? should i close the issue?

from lamejs.

calipoop avatar calipoop commented on July 18, 2024

Closed. Thanks all!

from lamejs.

javismiles avatar javismiles commented on July 18, 2024

i just confirmed that my code is recording the sound between 1 and 2 semitones lower in frequency than what it should..... just recorded some piano notes, and what comes out is between 1 and 2 semitones lower... why is that happening? code is above, best

from lamejs.

shanewho avatar shanewho commented on July 18, 2024

My only guess, and it's a weak one because I don't see anything in your code that tells me this is happening.. Is it possible that you are somehow encoding the same samples multiple times? i.e., when you stretch a wave it lowers the pitch. For example when a 10 second audio is played over 20 seconds it would be lower pitch. You might check the expected length of your input audio and see if it matches the length of the output. Other than that, good luck!

from lamejs.

javismiles avatar javismiles commented on July 18, 2024

Shane, thank you very much for the reply, mmm, i don´t see how that could be happening because the length of the recorded audio seems to be the same, look i have put the code online here:
http://torchprinciple.com/tests/rec1/alexrec.html
and it generates a download url every time in case any of you wants to test it by yourselves very quickly,
thank you again for any tips :)
http://torchprinciple.com/tests/rec1/alexrec.html

from lamejs.

javismiles avatar javismiles commented on July 18, 2024

i just made a test with a different library here:
http://torchprinciple.com/tests/rec2/
it comes from this library: https://github.com/nusofthq/Recordmp3js
and the recorded sound is perfect in frequency, no issues at all. So obviously something is wrong in my code. That other library works perfect but it has two things missing : conversion to mp3 is not in real time so it´s much slower to produce the result and also the minified lame encoder is 6 or 7 times bigger than yours here.

Ideally a combination of both would be great :) I love your library because your minified lame encoder is so so small and because real time encoding with your library is so so fast, now i just wish i can have the sound come out as stable as in http://torchprinciple.com/tests/rec2/

:)

from lamejs.

javismiles avatar javismiles commented on July 18, 2024

All right i found the reason, here
var mymp3Encoder=new liblame.Mp3Encoder(1,48000,128);
changing 44100 to 48000, solved the problem and now the frequency is all correct,

interesting right? why is it that with 48000 works perfect and with 44100 goes slower?

apart from that all the rest works well except that the very beginning and end of clip i kind of have to find the way stretch them more

but anyway, great, so the last thing i would like to now is, at the moment the encoding is not using a web worker, how could i pass the encoding to a web worker?

best wishes and thank u

from lamejs.

javismiles avatar javismiles commented on July 18, 2024

now problem is that
on laptop all is working, but on mobile firefox is recording empty mp3 and chrome also having problems, so on mobile there are problems, why would that happen?

from lamejs.

javismiles avatar javismiles commented on July 18, 2024

it works on mobile firefox (it was an issue with the position of audiocontext declaration) but on mobile chrome and opera the buffers come always empty with zeroes 0 0 0 0 ,
any idea why?
on laptop and mobile firefox buffers come with info all well

from lamejs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.