/** * AudioBuffer manipulation utilities */ import { getAudioContext } from './context'; /** * Extract a portion of an AudioBuffer */ export function extractBufferSegment( buffer: AudioBuffer, startTime: number, endTime: number ): AudioBuffer { const audioContext = getAudioContext(); const startSample = Math.floor(startTime * buffer.sampleRate); const endSample = Math.floor(endTime * buffer.sampleRate); const length = endSample - startSample; const segment = audioContext.createBuffer( buffer.numberOfChannels, length, buffer.sampleRate ); for (let channel = 0; channel < buffer.numberOfChannels; channel++) { const sourceData = buffer.getChannelData(channel); const targetData = segment.getChannelData(channel); for (let i = 0; i < length; i++) { targetData[i] = sourceData[startSample + i]; } } return segment; } /** * Delete a portion of an AudioBuffer */ export function deleteBufferSegment( buffer: AudioBuffer, startTime: number, endTime: number ): AudioBuffer { const audioContext = getAudioContext(); const startSample = Math.floor(startTime * buffer.sampleRate); const endSample = Math.floor(endTime * buffer.sampleRate); const beforeLength = startSample; const afterLength = buffer.length - endSample; const newLength = beforeLength + afterLength; const newBuffer = audioContext.createBuffer( buffer.numberOfChannels, newLength, buffer.sampleRate ); for (let channel = 0; channel < buffer.numberOfChannels; channel++) { const sourceData = buffer.getChannelData(channel); const targetData = newBuffer.getChannelData(channel); // Copy before segment for (let i = 0; i < beforeLength; i++) { targetData[i] = sourceData[i]; } // Copy after segment for (let i = 0; i < afterLength; i++) { targetData[beforeLength + i] = sourceData[endSample + i]; } } return newBuffer; } /** * Insert an AudioBuffer at a specific position */ export function insertBufferSegment( buffer: AudioBuffer, insertBuffer: AudioBuffer, insertTime: number ): AudioBuffer { const audioContext = getAudioContext(); const insertSample = Math.floor(insertTime * buffer.sampleRate); const newLength = buffer.length + insertBuffer.length; const newBuffer = audioContext.createBuffer( buffer.numberOfChannels, newLength, buffer.sampleRate ); for (let channel = 0; channel < buffer.numberOfChannels; channel++) { const sourceData = buffer.getChannelData(channel); const insertData = insertBuffer.getChannelData( Math.min(channel, insertBuffer.numberOfChannels - 1) ); const targetData = newBuffer.getChannelData(channel); // Copy before insert point for (let i = 0; i < insertSample; i++) { targetData[i] = sourceData[i]; } // Copy insert buffer for (let i = 0; i < insertBuffer.length; i++) { targetData[insertSample + i] = insertData[i]; } // Copy after insert point for (let i = insertSample; i < buffer.length; i++) { targetData[insertBuffer.length + i] = sourceData[i]; } } return newBuffer; } /** * Trim buffer to selection */ export function trimBuffer( buffer: AudioBuffer, startTime: number, endTime: number ): AudioBuffer { return extractBufferSegment(buffer, startTime, endTime); } /** * Concatenate two audio buffers */ export function concatenateBuffers( buffer1: AudioBuffer, buffer2: AudioBuffer ): AudioBuffer { const audioContext = getAudioContext(); const newLength = buffer1.length + buffer2.length; const channels = Math.max(buffer1.numberOfChannels, buffer2.numberOfChannels); const newBuffer = audioContext.createBuffer( channels, newLength, buffer1.sampleRate ); for (let channel = 0; channel < channels; channel++) { const targetData = newBuffer.getChannelData(channel); // Copy first buffer if (channel < buffer1.numberOfChannels) { const data1 = buffer1.getChannelData(channel); targetData.set(data1, 0); } // Copy second buffer if (channel < buffer2.numberOfChannels) { const data2 = buffer2.getChannelData(channel); targetData.set(data2, buffer1.length); } } return newBuffer; }