@siteed/expo-audio-studio
Version:
Comprehensive audio processing library for React Native and Expo with recording, analysis, visualization, and streaming capabilities across iOS, Android, and web
124 lines • 5.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertPCMToFloat32 = exports.WAV_HEADER_SIZE = void 0;
const react_native_1 = require("react-native");
const getWavFileInfo_1 = require("./getWavFileInfo");
exports.WAV_HEADER_SIZE = 44;
const convertSample = (dataView, offset, bitDepth) => {
switch (bitDepth) {
case 8:
return (dataView.getUint8(offset) - 128) / 128;
case 16:
return dataView.getInt16(offset, true) / 32768;
case 24:
return (((dataView.getUint8(offset) |
(dataView.getUint8(offset + 1) << 8) |
(dataView.getUint8(offset + 2) << 16)) /
8388608) *
2 -
1);
case 32:
return dataView.getFloat32(offset, true);
default:
throw new Error(`Unsupported bit depth: ${bitDepth}`);
}
};
const convertSampleNative = (array, startIndex, bitDepth) => {
switch (bitDepth) {
case 8:
return (array[startIndex] - 128) / 128;
case 16: {
// Handle 16-bit PCM using Uint8Array directly
const low = array[startIndex];
const high = array[startIndex + 1];
const value = (high << 8) | low;
// Convert to signed 16-bit
return (value > 32767 ? value - 65536 : value) / 32768;
}
case 24: {
const byte1 = array[startIndex];
const byte2 = array[startIndex + 1];
const byte3 = array[startIndex + 2];
const value = (byte3 << 16) | (byte2 << 8) | byte1;
return (((value > 8388607 ? value - 16777216 : value) / 8388608) * 2 - 1);
}
case 32: {
// Assuming 32-bit float
const view = new DataView(array.buffer, startIndex, 4);
return view.getFloat32(0, true);
}
default:
throw new Error(`Unsupported bit depth: ${bitDepth}`);
}
};
const convertPCMToFloat32 = async ({ bitDepth, buffer, skipWavHeader = false, logger, }) => {
try {
logger?.debug(`Converting PCM to Float32: bitDepth: ${bitDepth}, buffer.byteLength: ${buffer.byteLength}`);
let headerOffset = 0;
if (!skipWavHeader) {
const wavFileInfo = await (0, getWavFileInfo_1.getWavFileInfo)(buffer);
headerOffset = wavFileInfo.dataChunkOffset;
logger?.debug(`Using WAV header offset: ${headerOffset}`);
}
// Convert ArrayBuffer to Uint8Array for more efficient native handling
const uint8Array = new Uint8Array(buffer);
const dataLength = buffer.byteLength - headerOffset;
const bytesPerSample = bitDepth / 8;
const sampleLength = Math.floor(dataLength / bytesPerSample);
// Create result array using SharedArrayBuffer for better memory handling
let float32Array;
try {
// Try using SharedArrayBuffer first
const sharedBuffer = new SharedArrayBuffer(sampleLength * 4);
float32Array = new Float32Array(sharedBuffer);
}
catch (e) {
// Fallback to regular ArrayBuffer if SharedArrayBuffer is not available
float32Array = new Float32Array(sampleLength);
}
let min = Infinity;
let max = -Infinity;
// Process in smaller chunks
const CHUNK_SIZE = react_native_1.Platform.OS === 'web' ? sampleLength : 4000; // Smaller chunks for native
const numChunks = Math.ceil(sampleLength / CHUNK_SIZE);
for (let chunk = 0; chunk < numChunks; chunk++) {
const startSample = chunk * CHUNK_SIZE;
const endSample = Math.min((chunk + 1) * CHUNK_SIZE, sampleLength);
// Process chunk
for (let i = startSample; i < endSample; i++) {
const startIndex = headerOffset + i * bytesPerSample;
if (startIndex + bytesPerSample <= uint8Array.length) {
const value = react_native_1.Platform.OS === 'web'
? convertSample(new DataView(buffer), startIndex, bitDepth)
: convertSampleNative(uint8Array, startIndex, bitDepth);
if (isFinite(value)) {
float32Array[i] = value;
min = Math.min(min, value);
max = Math.max(max, value);
}
}
}
// Allow garbage collection between chunks on native
if (react_native_1.Platform.OS !== 'web' && chunk < numChunks - 1) {
await new Promise((resolve) => setTimeout(resolve, 0));
}
}
logger?.debug(`Conversion complete. Length: ${float32Array.length}, Range: [${min}, ${max}]`);
// Only log a small sample of values to avoid memory issues
if (logger?.debug) {
const sampleValues = Array.from(float32Array.slice(0, 5));
logger.debug('Sample values:', sampleValues);
}
return {
pcmValues: float32Array,
min,
max,
};
}
catch (error) {
logger?.error(`Error converting PCM to Float32`, error);
throw error;
}
};
exports.convertPCMToFloat32 = convertPCMToFloat32;
//# sourceMappingURL=convertPCMToFloat32.js.map