UNPKG

synaudio-cli

Version:

A command line tool for aligning and synchronizing two or more audio clips based on their content.

137 lines (118 loc) 3.16 kB
import { runCmd } from "./utilities.js"; const copyToChunks = ( inputChunks, inputLength, outputChunkSeconds, outputChunkIntervalSeconds, syncStartRange, syncEndRange, sampleRate, ) => { const chunks = []; const samplesPerChunk = sampleRate * outputChunkSeconds; const samplesToSkip = sampleRate * outputChunkIntervalSeconds; let data = null; let currentPosition = 0; // total bytes read let nextStart = 0; let nextEnd = 0; let dataPosition = 0; for (const chunk of inputChunks) { if (currentPosition >= nextEnd) { // start a new output chunk nextStart = nextEnd + samplesToSkip; nextEnd = Math.min(nextStart + samplesPerChunk, inputLength); if (nextStart >= inputLength) { // next output chunk would start be beyond the input data break; } data = new Float32Array(nextEnd - nextStart); chunks.push({ start: nextStart, end: nextEnd, syncStart: Math.round( chunks.length * outputChunkIntervalSeconds - syncStartRange, ) * sampleRate, syncEnd: Math.round( chunks.length * outputChunkIntervalSeconds + syncEndRange, ) * sampleRate, data, }); dataPosition = 0; } const chunkStart = nextStart - currentPosition; const chunkEnd = nextEnd - currentPosition; if (chunkStart < chunk.length) { const chunkLength = chunk.length; const startOffset = Math.max(0, chunkStart); const endOffset = Math.min(chunkLength, chunkEnd); const chunkToCopy = chunk.subarray(startOffset, endOffset); data.set(chunkToCopy, dataPosition); dataPosition += chunkToCopy.length; } currentPosition += chunk.length; } return chunks; }; const concatenate = (inputChunks, inputLength) => { let currentPosition = 0; const data = new Float32Array(inputLength); for (const chunk of inputChunks) { data.set(chunk, currentPosition); currentPosition += chunk.length; } return data; }; export const decodeAndSave = async ( inputFile, sampleRate, rectify, chunkSize = 0, chunkInterval = 0, syncStartRange, syncEndRange, ) => { const { promise, stdout } = runCmd("ffmpeg", [ "-v", "error", "-i", inputFile, "-map", "0:a:0", // only support one audio file "-ac", "1", "-ar", sampleRate, "-f", "f32le", "-acodec", "pcm_f32le", "-", ]); const outputChunks = []; let outputSamples = 0; stdout.on("data", (data) => { const decoded = new Float32Array(data.buffer); // rectify to avoid comparison issues with phase if (rectify) { for (let i = 0; i < decoded.length; i++) { decoded[i] = decoded[i] < 0 ? -decoded[i] : decoded[i]; } } outputChunks.push(decoded); outputSamples += decoded.length; }); await promise; return chunkSize === 0 ? concatenate(outputChunks, outputSamples) : copyToChunks( outputChunks, outputSamples, chunkSize, chunkInterval, syncStartRange, syncEndRange, sampleRate, ); };