Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to convert Float32Array into mp3 file #55

Open
codGirl opened this issue Aug 16, 2018 · 8 comments
Open

How to convert Float32Array into mp3 file #55

codGirl opened this issue Aug 16, 2018 · 8 comments

Comments

@codGirl
Copy link

codGirl commented Aug 16, 2018

Can someone tell me how to encode a mp3 file out of Float32Array in typescript (Angular 6) without using lame? (i tried using Lame but it displayed errors, for files generated when installing Lame, when compiling the project.)

please help.

@geeee
Copy link
Collaborator

geeee commented Aug 16, 2018

What kind of data is in your array? You need to use encoder (i.e. Lame) if you have uncompressed audio data there.
You don't need to compile anything to use lamejs.lame.min.js is already compiled. Just include it in your project and it's good to go.

@codGirl
Copy link
Author

codGirl commented Aug 16, 2018

The data is in an Float32Array, uncompressed. I used lame but when i tried to run the application i get an error for files creted when installing lame (haven't modified them )

@ronoyama7
Copy link

ronoyama7 commented Sep 2, 2018

FloatArray2Int16 (floatbuffer) {
    var int16Buffer = new Int16Array(floatbuffer.length);
    for (var i = 0, len = floatbuffer.length; i < len; i++) {
        if (floatbuffer[i] < 0) {
            int16Buffer[i] = 0x8000 * floatbuffer[i];
        } else {
            int16Buffer[i] = 0x7FFF * floatbuffer[i];
        }
    }
    return int16Buffer;
  }

you can change float32array to int16array.
and call encodeMono function like this

let blob = this.encodeMono(channels, sampleRate, int16Buffer);

encodeMono function is same as here.
https://github.com/zhuker/lamejs/blob/master/example.html

@zero41120
Copy link

Here's my suggestion for converting any Web Audio API-supported media to MP3 using the AudioContext to decode the source media.

This approach is versatile and can also be applied to various video formats as needed.

const fetchAudio = async (url: string) => {
  const response = await fetch(url, {
    mode: 'cors',
    referrerPolicy: 'no-referrer-when-downgrade',
  });
  const arrayBuffer = await response.arrayBuffer();
  const audioContext = new AudioContext();
  const buffer = await audioContext.decodeAudioData(arrayBuffer);
  return buffer;
};
// Do something to get the url and setup your s3
fetchAudio(url).then((data) => {
    const binary = audioToMp3Binary(data);
    s3.send(new PutObjectCommand({
        Bucket: bucketName,
        Key: `test/${fileName}`,
        Body: binary, // Uint8Array
    }));
});

The AudioBuffer encapsulates the channel data within a Float32Array, alongside the sampleRate attribute for reference.

import lamejs from 'lamejs';
import MPEGMode from 'lamejs/src/js/MPEGMode';
import Lame from 'lamejs/src/js/Lame';
import BitStream from 'lamejs/src/js/BitStream';

(window as any).MPEGMode = MPEGMode;
(window as any).Lame = Lame;
(window as any).BitStream = BitStream;

export function audioToMp3Binary(audioData: AudioBuffer, debug = true) {
    const encoder = new lamejs.Mp3Encoder(1, audioData.sampleRate, 128);
    const leftChannel = audioData.getChannelData(0);
    // Interpolate to -32768 to 32767, which is signed int16
    const interpolated = leftChannel.map((n) =>
        Math.max(-32768, Math.min(32768, n * (n < 0 ? 32768 : 32767)))
    );
    const int16arr = new Int16Array(leftChannel.length);
    // Type coercion, float -> int
    interpolated.forEach((n, i) => (int16arr[i] = n));

    const mp3Data: Int8Array[] = [];
    const sampleBlockSize = 1152;
    for (let i = 0; i < int16arr.length; i += sampleBlockSize) {
        const sampleChunk = int16arr.subarray(i, i + sampleBlockSize);
        const mp3buf = encoder.encodeBuffer(sampleChunk);
        if (mp3buf.length > 0) mp3Data.push(mp3buf);
    }
    mp3Data.push(new Int8Array(encoder.flush())); // Fill gaps in mp3buf
    if (debug) debugDownload(mp3Data);
    return int8ArrayToUint8Array(mp3Data);
}

For debugging purposes, you can download the file using a Blob with the MIME type audio/mp3.

function debugDownload(mp3Data: Int8Array[]) {
    const blob = new Blob(mp3Data, { type: 'audio/mp3' });
    const mp3Url = URL.createObjectURL(blob);
    const link = Object.assign(document.createElement('a'), {
        href: mp3Url,
        download: 'output.mp3',
    });
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}

Finally, should you require the raw binary data, such as for uploading to an S3 bucket, the conversion process would be as follows:

function int8ArrayToUint8Array(int8s: Int8Array[]) {
    const totalLength = int8s.reduce((acc, value) => acc + value.length, 0);
    const result = new Uint8Array(totalLength);
    let offset = 0;
    for (const int8 of int8s) {
        result.set(int8, offset);
        offset += int8.length;
    }
    return result;
}

@guest271314
Copy link

@zero41120 Getting silence using your solution in an AudioWorklet. What is the significance of MPEGMode, Lame, and BitStream?

@zero41120
Copy link

@zero41120 Getting silence using your solution in an AudioWorklet. What is the significance of MPEGMode, Lame, and BitStream?

#86 (comment)

@guest271314
Copy link

@zero41120 That still doesn't tell me what the significance of MPEGMode, Lame, and BitStream are.

@zero41120
Copy link

@zero41120 That still doesn't tell me what the significance of MPEGMode, Lame, and BitStream are.

The three variables has no significance for developer perspective and exist solely to satisfy TypeScript's requirements for referencing them as they would in JavaScript, due to their global nature on window. LameJS, not being TypeScript-based and without @types definitions, necessitates their presence to prevent TypeScript calling them undefined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants