feat: add playback speed control (0.25x - 2x)
Implemented variable playback speed functionality: - Added playbackRate state and ref to useMultiTrackPlayer (0.25x - 2x range) - Applied playback rate to AudioBufferSourceNode.playbackRate - Updated timing calculations to account for playback rate - Real-time playback speed adjustment for active playback - Dropdown UI control in PlaybackControls with preset speeds - Integrated changePlaybackRate function through AudioEditor 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ export interface MultiTrackPlayerState {
|
||||
loopEnabled: boolean;
|
||||
loopStart: number;
|
||||
loopEnd: number;
|
||||
playbackRate: number;
|
||||
}
|
||||
|
||||
export interface TrackLevel {
|
||||
@@ -38,6 +39,7 @@ export function useMultiTrackPlayer(
|
||||
const [loopEnabled, setLoopEnabled] = useState(false);
|
||||
const [loopStart, setLoopStart] = useState(0);
|
||||
const [loopEnd, setLoopEnd] = useState(0);
|
||||
const [playbackRate, setPlaybackRate] = useState(1.0);
|
||||
|
||||
const audioContextRef = useRef<AudioContext | null>(null);
|
||||
const sourceNodesRef = useRef<AudioBufferSourceNode[]>([]);
|
||||
@@ -60,6 +62,7 @@ export function useMultiTrackPlayer(
|
||||
const loopEnabledRef = useRef<boolean>(false);
|
||||
const loopStartRef = useRef<number>(0);
|
||||
const loopEndRef = useRef<number>(0);
|
||||
const playbackRateRef = useRef<number>(1.0);
|
||||
|
||||
// Keep tracksRef in sync with tracks prop
|
||||
useEffect(() => {
|
||||
@@ -73,6 +76,11 @@ export function useMultiTrackPlayer(
|
||||
loopEndRef.current = loopEnd;
|
||||
}, [loopEnabled, loopStart, loopEnd]);
|
||||
|
||||
// Keep playbackRate ref in sync with state
|
||||
useEffect(() => {
|
||||
playbackRateRef.current = playbackRate;
|
||||
}, [playbackRate]);
|
||||
|
||||
// Keep onRecordAutomationRef in sync
|
||||
useEffect(() => {
|
||||
onRecordAutomationRef.current = onRecordAutomation;
|
||||
@@ -313,7 +321,7 @@ export function useMultiTrackPlayer(
|
||||
const updatePlaybackPosition = useCallback(() => {
|
||||
if (!audioContextRef.current) return;
|
||||
|
||||
const elapsed = audioContextRef.current.currentTime - startTimeRef.current;
|
||||
const elapsed = (audioContextRef.current.currentTime - startTimeRef.current) * playbackRateRef.current;
|
||||
const newTime = pausedAtRef.current + elapsed;
|
||||
|
||||
// Check if loop is enabled and we've reached the loop end
|
||||
@@ -346,6 +354,7 @@ export function useMultiTrackPlayer(
|
||||
|
||||
const source = audioContext.createBufferSource();
|
||||
source.buffer = track.audioBuffer;
|
||||
source.playbackRate.value = playbackRateRef.current;
|
||||
|
||||
// Connect to existing nodes (gain, pan, effects are still connected)
|
||||
const trackIndex = tracks.indexOf(track);
|
||||
@@ -465,6 +474,9 @@ export function useMultiTrackPlayer(
|
||||
outputNode.connect(masterGain);
|
||||
console.log('[MultiTrackPlayer] Effect output connected with', effectNodes.length, 'effect nodes');
|
||||
|
||||
// Set playback rate
|
||||
source.playbackRate.value = playbackRateRef.current;
|
||||
|
||||
// Start playback from current position
|
||||
source.start(0, pausedAtRef.current);
|
||||
|
||||
@@ -902,6 +914,17 @@ export function useMultiTrackPlayer(
|
||||
}
|
||||
}, [setLoopPoints]);
|
||||
|
||||
const changePlaybackRate = useCallback((rate: number) => {
|
||||
// Clamp rate between 0.25x and 2x
|
||||
const clampedRate = Math.max(0.25, Math.min(2.0, rate));
|
||||
setPlaybackRate(clampedRate);
|
||||
|
||||
// Update playback rate on all active source nodes
|
||||
sourceNodesRef.current.forEach(source => {
|
||||
source.playbackRate.value = clampedRate;
|
||||
});
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isPlaying,
|
||||
currentTime,
|
||||
@@ -923,5 +946,7 @@ export function useMultiTrackPlayer(
|
||||
toggleLoop,
|
||||
setLoopPoints,
|
||||
setLoopFromSelection,
|
||||
playbackRate,
|
||||
changePlaybackRate,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user