/** * Utilities for applying effects to audio selections */ import type { Selection } from '@/types/selection'; import { getAudioContext } from '../context'; /** * Extract a region from an audio buffer */ export function extractRegion( buffer: AudioBuffer, startTime: number, endTime: number ): AudioBuffer { const audioContext = getAudioContext(); const sampleRate = buffer.sampleRate; const numberOfChannels = buffer.numberOfChannels; const startSample = Math.floor(startTime * sampleRate); const endSample = Math.floor(endTime * sampleRate); const length = endSample - startSample; const regionBuffer = audioContext.createBuffer( numberOfChannels, length, sampleRate ); for (let channel = 0; channel < numberOfChannels; channel++) { const sourceData = buffer.getChannelData(channel); const targetData = regionBuffer.getChannelData(channel); for (let i = 0; i < length; i++) { targetData[i] = sourceData[startSample + i]; } } return regionBuffer; } /** * Replace a region in an audio buffer with processed audio */ export function replaceRegion( originalBuffer: AudioBuffer, processedRegion: AudioBuffer, startTime: number ): AudioBuffer { const audioContext = getAudioContext(); const sampleRate = originalBuffer.sampleRate; const numberOfChannels = originalBuffer.numberOfChannels; // Create new buffer with same length as original const newBuffer = audioContext.createBuffer( numberOfChannels, originalBuffer.length, sampleRate ); const startSample = Math.floor(startTime * sampleRate); for (let channel = 0; channel < numberOfChannels; channel++) { const originalData = originalBuffer.getChannelData(channel); const processedData = processedRegion.getChannelData(channel); const newData = newBuffer.getChannelData(channel); // Copy everything from original for (let i = 0; i < originalBuffer.length; i++) { newData[i] = originalData[i]; } // Replace the selected region with processed data for (let i = 0; i < processedRegion.length; i++) { if (startSample + i < newBuffer.length) { newData[startSample + i] = processedData[i]; } } } return newBuffer; } /** * Apply an effect function to a selection, or entire buffer if no selection */ export function applyEffectToSelection( buffer: AudioBuffer, selection: Selection | null, effectFn: (buffer: AudioBuffer) => AudioBuffer ): AudioBuffer { if (!selection || selection.start === selection.end) { // No selection, apply to entire buffer return effectFn(buffer); } // Extract the selected region const region = extractRegion(buffer, selection.start, selection.end); // Apply effect to the region const processedRegion = effectFn(region); // Replace the region in the original buffer return replaceRegion(buffer, processedRegion, selection.start); } /** * Apply an async effect function to a selection, or entire buffer if no selection */ export async function applyAsyncEffectToSelection( buffer: AudioBuffer, selection: Selection | null, effectFn: (buffer: AudioBuffer) => Promise ): Promise { if (!selection || selection.start === selection.end) { // No selection, apply to entire buffer return await effectFn(buffer); } // Extract the selected region const region = extractRegion(buffer, selection.start, selection.end); // Apply effect to the region const processedRegion = await effectFn(region); // Replace the region in the original buffer return replaceRegion(buffer, processedRegion, selection.start); }