Merging Audio files with Javascript - javascript

I have an issue that I have spent forever trying to figure out and I can't get it. I want to take two audio files, and merge them into one blob so that they play at the exact same time. I don't know much about how audio works, so i'm kinda shooting in the dark. But my first idea (which I will leave the code below) was to create arrays of the decimal values of the two audio files, then add the values of a certain position together and divide by two, then push all of these new values into an array that would be turned into a blob and then played. This failed however, it played a really horrible squeaking sound.
function mergeAudio(){
var length;
const mergedAudio = []
//audioArray1 and audioArray2 are just arrays of the decimal values of the two audio files
// setting the length of the merged audio
if(audioArray1.length < audioArray2.length){
length = audioArray1.length
}else{
length = audioArray2.length
}
//merging bytes and pushing them to a new array
for(var i = 0; i < length; i++){
var byte = audioArray2[i] + audioArray1[i]
byte = byte / 2
if(byte <= 0){
byte = 0
}
mergedAudio.push(byte)
}
//create Audio and play it
const arrayBuffer = new Uint8Array(mergedAudio)
const audioBlob = new Blob(arrayBuffer);
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
audio.play().then(function(){}).catch(function(error){
console.log(error)
})
})
Like i said, this did not work, and i tried subtracting values from each byte and trying other methods to see what the outcome would be so I could figure out what I was doing wrong, but for some reason every other method I try (besides the one in the code above) leaves an error message:
"DOMException: Failed to load because no supported source was found."
If anyone is savvy with audio or knows why I am getting this error, or if anyone knows another method to merge audio, it would be greatly appreciated!!!
Edit: I am getting my data 5 second .mp3 files and am using the code below to create an array of the data
var audioArray1;
input.addEventListener('change', () =>{
const fileReader = new FileReader()
fileReader.onload = function(event) {
const arrayBuffer = event.target.result
const buffer = new Uint8Array(arrayBuffer)
const array = []
for(var i = 0; i < buffer.length; i++){
array.push(buffer[i])
}
audioArray1 = array
}
//uses audio file that user uploaded and assigns to file reader
fileReader.readAsArrayBuffer(input.files[0]);
})
If I log the array, it shows something like this:
(74925) [73, 68, 51, 4, 0, 0, 0, 0, 0, 35, 84, 83, 83, 69, 0, 0, 0, 15, 0, 0, 3, 76, 97, 118, 102, 53, 55, 46, 53, 54, 46, 49, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 251, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 110, 102, 111, 0, 0, 0, 15, 0, 0, 0, 129, 0, 1, 36, 128, 0, 5, 7, …]

A few things to be aware of:
(1) you need to deal with PCM values, not bytes. The PCM is created by appending bytes. For example, if the format is 16-bit, one of the two bytes will be shifted 8-bits over and the two bytes are then OR'd together to form a single 16-byte value. The order of the two bytes depends on whether the format is little-endian or big-endian.
(2) Once you have the PCM, mixing the tracks together is accomplished via addition.
IDK what playback format you are using. There may be another formatting step. The only time I've played raw PCM was when using the Web-Audio API.

Related

Python equivalent for JavaScript's DataView

I load a byte array from a base-64 encoded string and I'd like to parse it.
However values are encoded in different ways and I'd like to replicate DataView's behavior.
Example:
function parse(data){
view = new DataView(data.buffer);
return {
headerSize : view.getUint8(0),
numberOfPlanes : view.getUint16(1, true),
width: view.getUint16(3, true),
height: view.getUint16(5, true),
offset: view.getUint16(7, true)
};
}
Usage:
data = new Uint8Array([8, 96, 0, 0, 2, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
parse(data)
Returns {headerSize: 8, numberOfPlanes: 96, width: 512, height: 256, offset: 8}
Later on I'll need to use DataView.getFloat32.
Right now I have something like this:
def get_bin(a):
ba = bin(a)[2:]
return "0" * (8 - len(ba)) + ba
def getUInt16(arr, ind):
a = arr[ind]
b = arr[ind + 1]
return int(get_bin(b) + get_bin(a), 2)
def getFloat32(arr, ind):
return bin_to_float("".join(get(i) for i in arr[ind : ind + 4][::-1]))
def bin_to_float(binary):
return struct.unpack("!f", struct.pack("!I", int(binary, 2)))[0]
But a library could be more efficient and versatile
Float example: [111, 62, 163, 36] should yield 7.079574826789837e-17
This should cover enough of your use cases or at least get you to the point where you can make minor changes. Hopefully you can somewhat follow what I am doing but feel free to ask questions.
from functools import reduce
import struct
class DataView:
def __init__(self, array, bytes_per_element=1):
"""
bytes_per_element is the size of each element in bytes.
By default we are assume the array is one byte per element.
"""
self.array = array
self.bytes_per_element = 1
def __get_binary(self, start_index, byte_count, signed=False):
integers = [self.array[start_index + x] for x in range(byte_count)]
bytes = [integer.to_bytes(self.bytes_per_element, byteorder='little', signed=signed) for integer in integers]
return reduce(lambda a, b: a + b, bytes)
def get_uint_16(self, start_index):
bytes_to_read = 2
return int.from_bytes(self.__get_binary(start_index, bytes_to_read), byteorder='little')
def get_uint_8(self, start_index):
bytes_to_read = 1
return int.from_bytes(self.__get_binary(start_index, bytes_to_read), byteorder='little')
def get_float_32(self, start_index):
bytes_to_read = 4
binary = self.__get_binary(start_index, bytes_to_read)
return struct.unpack('<f', binary)[0] # <f for little endian
def parse(byte_array):
d = DataView(byte_array)
return {
"headerSize": d.get_uint_8(0),
"numverOfPlanes": d.get_uint_16(1),
"width": d.get_uint_16(3),
"hieght": d.get_uint_16(5),
"offset": d.get_uint_16(7),
}
result = parse([8, 96, 0, 0, 2, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
import json
print(json.dumps(result, indent=2))
d = DataView([111, 62, 163, 36])
d.get_float_32(0)
Output:
{
"headerSize": 8,
"numverOfPlanes": 96,
"width": 512,
"hieght": 256,
"offset": 8
}
7.079574826789837e-17

Setting unsigned byte to ArrayBuffer

I know that you can do
const buffer = new ArrayBuffer(16);
const dataView = new DataView(buffer);
dataView.setUint8(1, 4)
console.log(dataView.getUint8(1)); // 1
However, I would like to set an unsigned byte before the dataView deceleration line, so would it be possible to do this without having access to dataView hence would if be possible to set an unsigned byte of 4 at the byte offset 1 to the ArrayBuffer instead of using dataView.setUint8(1, 4)?
Or alternatively would it be to convert a DataView to an ArrayBuffer?
I think the important thing you're missing is that a DataView is just a View. So when you do dataView.setUint8(1, 4) you do modify the buffer. The dataView itself does not hold the data, just a reference to the buffer. So your code already does what you want. To get an ArrayBuffer of it just use the original buffer:
const buffer = new ArrayBuffer(16);
const dataView = new DataView(buffer);
dataView.setUint8(1, 4)
console.log(dataView.getUint8(1)); // 4
console.log(new Uint8Array(buffer)) // [ 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]

javascript board game piece placement

I'm writing a board game in java script, and what i'm trying to accomplish is: layout the board(chess/checkers format) Then add pieces to the board based on position. So for example i want to be able to write code for piece a to be moved onto tile 10.
So far in my code i have a loop to create the board but don't a method to properly name the tiles, so that the piece can correctly be placed on the tile.
for (i=0; i<64; i++){
var tile = cc.Sprite.create(res.myTile_png);
this.addChild(tile,0);
x = centerpos.x + ((i % 8) - 3.5) * tile.getBoundingBox().width;
y = centerpos.y + (Math.floor(i / 8) - 3.5) * tile.getBoundingBox().height;
tile.setPosition(x,y);
}
One way to go about doing this would be to assign a unique integer identifier to every distinct piece in the game, and then maintain a matrix of dimensions equal to the # of rows x # of columns on the board, with values of the piece identifiers in the correct address in the matrix that would correspond to their position on the board.
For instance, the starting arrangement of pieces in checkers can be represented by:
[
[ 0, -1, 0, -1, 0, -1, 0, -1 ],
[ -1, 0, -1, 0, -1, 0, -1, 0 ],
[ 0, -1, 0, -1, 0, -1, 0, -1 ],
[ 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 1, 0, 1, 0, 1, 0, 1, 0 ],
[ 0, 1, 0, 1, 0, 1, 0, 1 ],
[ 1, 0, 1, 0, 1, 0, 1, 0 ]
]
with, say, -1 representing white, and 1 representing red pieces on the board.
The tile elements of the board can also be kept in a matrix, so that the two matrices can be iterated over together to place the pieces in corresponding locations.
The unique ids can also then be used as CSS class names, or image file names to be attached to the element representing the piece.

Converting an array of bytes into a float32

I have a JavaScript application which receives a voltage value as an Uint8Array. Here are 2 examples of the received data:
[3, 134, 46, 177, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[3, 127, 46, 170, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
I am trying to convert this Uint8Array to a float value, but I am not sure if it's a float32 or float64, if it's signed or not. What I do know is that this value is around 12.
Can anyone help with a JavaScript snippet to do the conversion?
Thank you in advance.
Each line appears to have two voltages in it. Voltages from devices usually come from ATOD converters that have between 8 and 16 bits of info and are scaled based on the circuitry.
It looks like a 3 (unused and probably some sort of mode flag) followed by two pairs of bytes each one a voltage from a ATOD. I would guess that two bytes such as 177 and 46 should be interpreted as (177+46*256)/1000.0 based on 1 mv. scaling. This produces values just over 11.9 volts. Given the range of the two samples this would fit your expectations.

Sliding values of an array inside intervals

I ve created a array with eleven values . I am trying to slide the values of my array during intervals. What i am trying, every n ms to slide a value to the next position of the array that i ve created. Every inteval i initialize the first value, so i want the slide effect.
var barArray = [0,0,0,0,0,0,0,0,0,0,0];
var interval = 0;
setInterval(function() {
temporal = getNewValue; //getting with a function new value
barArray[0] = temporal;
if(interval == barArray.length)
{
interval = 0;
}
for (var i = 0; barArray.length; i++){
// code missing
}
}, 1000);
I am tried many things without finding a solution.
Output:
1st interval: [76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
2nd interval: [55, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0]
3rd interval: [32,55, 76, 0, 0, 0, 0, 0, 0, 0, 0]
11th interval: [..., 32, 55, 76]
12th [..., 32,55] ect. `
What you have described here is a queue. You input elements at one end and silently drop them at the other end. JavaScript arrays have functions to add and extract elements at both ends of the array (push/pop and shift/unshift).
In the end, a complete solution would be:
var barArray = [0,0,0,0,0,0,0,0,0,0,0];
setInterval(function() {
barArray.unshift(getNewValue());
barArray.pop();
}, 1000);

Categories