feat(core): audio playback rate (#11702)

fix AF-2470
This commit is contained in:
pengx17
2025-04-16 07:49:12 +00:00
parent 30184817da
commit 51316217af
9 changed files with 135 additions and 8 deletions

View File

@@ -149,6 +149,13 @@ export const timeDisplay = style({
},
});
export const playbackRateDisplay = style({
fontSize: cssVar('fontXs'),
fontWeight: 500,
color: cssVarV2('text/secondary'),
cursor: 'pointer',
});
export const miniRoot = style({
position: 'relative',
display: 'flex',

View File

@@ -12,6 +12,7 @@ const AudioWrapper = () => {
const [seekTime, setSeekTime] = useState(0);
const [duration, setDuration] = useState(0);
const [loading, setLoading] = useState(false);
const [playbackRate, setPlaybackRate] = useState(1.0);
const audioRef = useRef<HTMLAudioElement>(null);
const audioUrlRef = useRef<string | null>(null);
@@ -151,6 +152,13 @@ const AudioWrapper = () => {
[playbackState]
);
const handlePlaybackRateChange = useCallback((rate: number) => {
if (audioRef.current) {
audioRef.current.playbackRate = rate;
setPlaybackRate(rate);
}
}, []);
useEffect(() => {
const audio = audioRef.current;
if (!audio || !audioFile) return;
@@ -298,6 +306,8 @@ const AudioWrapper = () => {
onPause={handlePause}
onStop={handleStop}
onSeek={handleSeek}
playbackRate={playbackRate}
onPlaybackRateChange={handlePlaybackRateChange}
/>
<AudioPlayer
name={audioFile.name}
@@ -311,6 +321,8 @@ const AudioWrapper = () => {
onPause={handlePause}
onStop={handleStop}
onSeek={handleSeek}
playbackRate={playbackRate}
onPlaybackRateChange={handlePlaybackRateChange}
/>
</>
)}

View File

@@ -8,8 +8,9 @@ import bytes from 'bytes';
import { clamp } from 'lodash-es';
import { type MouseEventHandler, type ReactNode, useCallback } from 'react';
import { IconButton } from '../button';
import { Button, IconButton } from '../button';
import { AnimatedPlayIcon } from '../lottie';
import { Menu, MenuItem } from '../menu';
import * as styles from './audio-player.css';
import { AudioWaveform } from './audio-waveform';
@@ -40,8 +41,15 @@ export interface AudioPlayerProps {
onPause: MouseEventHandler;
onStop: MouseEventHandler;
onSeek: (newTime: number) => void;
// Playback rate
playbackRate: number;
onPlaybackRateChange: (rate: number) => void;
}
// Playback rate options
const playbackRates = [0.5, 0.75, 1, 1.5, 1.75, 2, 3];
export const AudioPlayer = ({
name,
size,
@@ -55,6 +63,8 @@ export const AudioPlayer = ({
onPause,
onSeek,
onClick,
playbackRate,
onPlaybackRateChange,
}: AudioPlayerProps) => {
// Handle progress bar click
const handleProgressClick = useCallback(
@@ -80,6 +90,13 @@ export const AudioPlayer = ({
[loading, playbackState, onPause, onPlay]
);
const handlePlaybackRateChange = useCallback(
(rate: number) => {
onPlaybackRateChange(rate);
},
[onPlaybackRateChange]
);
// Calculate progress percentage
const progressPercentage = duration > 0 ? seekTime / duration : 0;
return (
@@ -97,6 +114,27 @@ export const AudioPlayer = ({
</div>
</div>
<div className={styles.upperRight}>
<Menu
rootOptions={{ modal: false }}
children={
<Button variant="plain" className={styles.playbackRateDisplay}>
{playbackRate}x
</Button>
}
items={
<>
{playbackRates.map(rate => (
<MenuItem
key={rate}
selected={rate === playbackRate}
onClick={() => handlePlaybackRateChange(rate)}
>
{rate}x
</MenuItem>
))}
</>
}
/>
{notesEntry}
<AnimatedPlayIcon
onClick={handlePlayToggle}