Reputation: 509
Its possible to merge two audio 'base64data' strings to create an unique audio file?
I have in strings two cyclic audio base64 wavs like this:
data:audio/x-wav;base64,UklGRuIfQVZFZm1R7SH$WP90AhICLwKT...
I guess I am doing something very stupid but I wonder if it is possible.
I am trying to merge the two wavs into one that I can play in an audio HTML element. I want to xtract the base64 data of second to merge to the one and then play it but the navigator return an error.
This is my javascript code:
var audio_Data1 = base64_audio1;
var audio_Data2 = base64_audio1.split(',')[1];
var audio_final = audio_Data1 + audio_Data2;
audioControl.src = audio_final;
audioControl.play();
I appreciate your suggestions Thank you !
EDIT:
I am trying to decode base64 chunks to buffer data and concat the buffers for encode to base64 again.
The problem: The base64 returned is "AA=="
I think "AA==" is 0 bytes. Then i am doing something bad when I concat the buffer:
var myB64Data = myB64WavString.split(',');
var myB64Chunk = myB64Data[1];
var myBuffer1 = base64ToArrayBuffer(myB64Chunk);
var myBuffer2 = ""; //Same process
var myFinalBuffer = [];
myFinalBuffer.push.apply(myFinalBuffer, myBuffer1);
myFinalBuffer.push.apply(myFinalBuffer, myBuffer2);
var myFinalB64 = 'data:audio/x-wav;base64,' + arrayBufferToBase64(myFinalBuffer);
console.log( myFinalB64 );
Its return by console the next: "data:audio/x-wav;base64,AA=="
My javascript working functions for encode/decode:
function base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
function arrayBufferToBase64( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
Upvotes: 2
Views: 2044
Reputation: 5660
To update the answer using the outdated atob
:
const decoded1 = Buffer.from(encodedString1, "base64");
const decoded2 = Buffer.from(encodedString2, "base64");
const encodedCombined = Buffer.concat([decoded1, decoded2]).toString("base64");
Upvotes: 1
Reputation: 15
The above answer didn't work for me, but this simple one did:
Just decode each B64 String
// Decode the String
var decodedString0 = atob(encodedString0);
var decodedString1 = atob(encodedString1);
Merge them together, then encode back,
var decodedStringMerge = decodedString0 + decodedString1
var ecodedStringMergeB64 = btoa(decodedStringMerge)
console.log(ecodedStringMergeB64);
Play the merged audio..
Upvotes: 0
Reputation: 509
I solved it ! (two days after hard and madness work and by copying answer from Convert AudioBuffer to ArrayBuffer / Blob for WAV Download) I hope it helps you save all the work that I had to develop for that ;)
var myB64Data = myB64WavString.split(',');
var myB64Chunk = myB64Data[1];
var myBuffer1 = base64ToArrayBuffer(myB64Chunk);
var myBuffer2 = ""; //Same process
var myFinalBuffer = appendBuffer (myBuffer1, myBuffer2);
var myFinalBuffer = getWavBytes( arrBytesFinal, {
isFloat: false, // floating point or 16-bit integer
numChannels: 2, //1 for mono recordings
sampleRate: 48000, //Depends on your file audio bitrate !! 32000
})
var myFinalB64 = 'data:audio/x-wav;base64,' + arrayBufferToBase64(myFinalBuffer);
console.log( myFinalB64 );
//Then you can asign to an audio HTML control
myAudioControl.src = myFinalB64;
Additional functions:
function appendBuffer(buffer1, buffer2) {
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp;
};
function getWavBytes(buffer, options) {
const type = options.isFloat ? Float32Array : Uint16Array
const numFrames = buffer.byteLength / type.BYTES_PER_ELEMENT
const headerBytes = getWavHeader(Object.assign({}, options, { numFrames }))
const wavBytes = new Uint8Array(headerBytes.length + buffer.byteLength);
// prepend header, then add pcmBytes
wavBytes.set(headerBytes, 0)
wavBytes.set(new Uint8Array(buffer), headerBytes.length)
return wavBytes
}
// adapted from https://gist.github.com/also/900023
// returns Uint8Array of WAV header bytes
function getWavHeader(options) {
const numFrames = options.numFrames
const numChannels = options.numChannels || 2
const sampleRate = options.sampleRate || 44100
const bytesPerSample = options.isFloat? 4 : 2
const format = options.isFloat? 3 : 1
const blockAlign = numChannels * bytesPerSample
const byteRate = sampleRate * blockAlign
const dataSize = numFrames * blockAlign
const buffer = new ArrayBuffer(44)
const dv = new DataView(buffer)
let p = 0
function writeString(s) {
for (let i = 0; i < s.length; i++) {
dv.setUint8(p + i, s.charCodeAt(i))
}
p += s.length
}
function writeUint32(d) {
dv.setUint32(p, d, true)
p += 4
}
function writeUint16(d) {
dv.setUint16(p, d, true)
p += 2
}
writeString('RIFF') // ChunkID
writeUint32(dataSize + 36) // ChunkSize
writeString('WAVE') // Format
writeString('fmt ') // Subchunk1ID
writeUint32(16) // Subchunk1Size
writeUint16(format) // AudioFormat
writeUint16(numChannels) // NumChannels
writeUint32(sampleRate) // SampleRate
writeUint32(byteRate) // ByteRate
writeUint16(blockAlign) // BlockAlign
writeUint16(bytesPerSample * 8) // BitsPerSample
writeString('data') // Subchunk2ID
writeUint32(dataSize) // Subchunk2Size
return new Uint8Array(buffer)
}
Upvotes: 4
Reputation: 1213
To expand on the answer above, here is how it would be done for an array of encoded strings instead of just two.
function joinBuffers(buffers) {
const byteLength = buffers
.map((buffer) => buffer.byteLength)
.reduce((prev, curr) => prev + curr);
var tmp = new Uint8Array(byteLength);
var length = 0;
for (let i = 0; i < buffers.length; i++) {
const buffer = buffers[i];
tmp.set(new Uint8Array(buffer), length);
length += buffer.byteLength;
}
return tmp;
}
Upvotes: 0