117 lines
3.0 KiB
TypeScript
117 lines
3.0 KiB
TypeScript
|
|
/**
|
||
|
|
* Fade in/out effects
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { getAudioContext } from '../context';
|
||
|
|
|
||
|
|
export type FadeType = 'linear' | 'exponential' | 'logarithmic';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Apply fade in to audio buffer
|
||
|
|
* @param buffer - Source audio buffer
|
||
|
|
* @param duration - Fade duration in seconds
|
||
|
|
* @param type - Fade curve type
|
||
|
|
* @returns New audio buffer with fade in applied
|
||
|
|
*/
|
||
|
|
export function applyFadeIn(
|
||
|
|
buffer: AudioBuffer,
|
||
|
|
duration: number,
|
||
|
|
type: FadeType = 'linear'
|
||
|
|
): AudioBuffer {
|
||
|
|
const audioContext = getAudioContext();
|
||
|
|
const fadeSamples = Math.min(
|
||
|
|
Math.floor(duration * buffer.sampleRate),
|
||
|
|
buffer.length
|
||
|
|
);
|
||
|
|
|
||
|
|
const outputBuffer = audioContext.createBuffer(
|
||
|
|
buffer.numberOfChannels,
|
||
|
|
buffer.length,
|
||
|
|
buffer.sampleRate
|
||
|
|
);
|
||
|
|
|
||
|
|
for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
|
||
|
|
const inputData = buffer.getChannelData(channel);
|
||
|
|
const outputData = outputBuffer.getChannelData(channel);
|
||
|
|
|
||
|
|
for (let i = 0; i < buffer.length; i++) {
|
||
|
|
if (i < fadeSamples) {
|
||
|
|
const progress = i / fadeSamples;
|
||
|
|
const gain = calculateFadeGain(progress, type);
|
||
|
|
outputData[i] = inputData[i] * gain;
|
||
|
|
} else {
|
||
|
|
outputData[i] = inputData[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return outputBuffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Apply fade out to audio buffer
|
||
|
|
* @param buffer - Source audio buffer
|
||
|
|
* @param duration - Fade duration in seconds
|
||
|
|
* @param type - Fade curve type
|
||
|
|
* @returns New audio buffer with fade out applied
|
||
|
|
*/
|
||
|
|
export function applyFadeOut(
|
||
|
|
buffer: AudioBuffer,
|
||
|
|
duration: number,
|
||
|
|
type: FadeType = 'linear'
|
||
|
|
): AudioBuffer {
|
||
|
|
const audioContext = getAudioContext();
|
||
|
|
const fadeSamples = Math.min(
|
||
|
|
Math.floor(duration * buffer.sampleRate),
|
||
|
|
buffer.length
|
||
|
|
);
|
||
|
|
const fadeStartSample = buffer.length - fadeSamples;
|
||
|
|
|
||
|
|
const outputBuffer = audioContext.createBuffer(
|
||
|
|
buffer.numberOfChannels,
|
||
|
|
buffer.length,
|
||
|
|
buffer.sampleRate
|
||
|
|
);
|
||
|
|
|
||
|
|
for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
|
||
|
|
const inputData = buffer.getChannelData(channel);
|
||
|
|
const outputData = outputBuffer.getChannelData(channel);
|
||
|
|
|
||
|
|
for (let i = 0; i < buffer.length; i++) {
|
||
|
|
if (i >= fadeStartSample) {
|
||
|
|
const progress = (i - fadeStartSample) / fadeSamples;
|
||
|
|
const gain = calculateFadeGain(1 - progress, type);
|
||
|
|
outputData[i] = inputData[i] * gain;
|
||
|
|
} else {
|
||
|
|
outputData[i] = inputData[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return outputBuffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Calculate fade gain based on progress and curve type
|
||
|
|
* @param progress - Progress from 0 to 1
|
||
|
|
* @param type - Fade curve type
|
||
|
|
* @returns Gain value from 0 to 1
|
||
|
|
*/
|
||
|
|
function calculateFadeGain(progress: number, type: FadeType): number {
|
||
|
|
switch (type) {
|
||
|
|
case 'linear':
|
||
|
|
return progress;
|
||
|
|
|
||
|
|
case 'exponential':
|
||
|
|
// Exponential curve: faster at the start, slower at the end
|
||
|
|
return progress * progress;
|
||
|
|
|
||
|
|
case 'logarithmic':
|
||
|
|
// Logarithmic curve: slower at the start, faster at the end
|
||
|
|
return Math.sqrt(progress);
|
||
|
|
|
||
|
|
default:
|
||
|
|
return progress;
|
||
|
|
}
|
||
|
|
}
|