Skip to content

Commit

Permalink
🐛 fix: Fix stream merge
Browse files Browse the repository at this point in the history
  • Loading branch information
canisminor1990 committed Nov 11, 2023
1 parent 9403552 commit e4398be
Show file tree
Hide file tree
Showing 18 changed files with 162 additions and 136 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"lucide-react": "latest",
"microsoft-cognitiveservices-speech-sdk": "^1",
"query-string": "^8",
"react-error-boundary": "^4.0.11",
"react-layout-kit": "^1",
"ssml-document": "^1",
"swr": "^2",
Expand Down
5 changes: 2 additions & 3 deletions src/AudioPlayer/demos/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default () => {
{
allowPause: false,
showSlider: true,
showTime: true,
timeRender: {
options: ['text', 'tag'],
value: 'text',
Expand All @@ -22,11 +21,11 @@ export default () => {
{ store },
);

const audio = useAudioPlayer(url);
const { isLoading, ...audio } = useAudioPlayer(url);

return (
<StoryBook levaStore={store}>
<AudioPlayer audio={audio} {...options} />
<AudioPlayer audio={audio} isLoading={isLoading} {...options} />
</StoryBook>
);
};
66 changes: 31 additions & 35 deletions src/AudioPlayer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ActionIcon, ActionIconProps, Tag } from '@lobehub/ui';
import { Slider } from 'antd';
import { Download, Pause, Play, StopCircle } from 'lucide-react';
import { ActionIcon, ActionIconProps, Icon, Tag } from '@lobehub/ui';
import { Dropdown, Slider } from 'antd';
import { Download, PauseCircle, Play, StopCircle } from 'lucide-react';
import React, { memo, useMemo } from 'react';
import { Flexbox } from 'react-layout-kit';

Expand All @@ -22,9 +22,8 @@ export interface AudioPlayerProps {
audio: AudioProps;
buttonSize?: ActionIconProps['size'];
className?: string;
showDownload?: boolean;
isLoading?: boolean;
showSlider?: boolean;
showTime?: boolean;
style?: React.CSSProperties;
timeRender?: 'tag' | 'text';
timeStyle?: React.CSSProperties;
Expand All @@ -33,17 +32,16 @@ export interface AudioPlayerProps {

const AudioPlayer = memo<AudioPlayerProps>(
({
isLoading,
style,
timeStyle,
buttonSize,
className,
audio,
allowPause,
allowPause = true,
timeType = 'left',
showTime = true,
showSlider = true,
timeRender = 'text',
showDownload = true,
}) => {
const { isPlaying, play, stop, pause, duration, setTime, currentTime, download } = audio;

Expand All @@ -62,35 +60,41 @@ const AudioPlayer = memo<AudioPlayerProps>(
className={className}
gap={8}
horizontal
style={{ paddingRight: showDownload ? 0 : 8, width: '100%', ...style }}
style={{ paddingRight: 8, width: '100%', ...style }}
>
{allowPause ? (
<ActionIcon
icon={isPlaying ? Pause : Play}
onClick={isPlaying ? pause : play}
size={buttonSize}
style={{ flex: 'none' }}
/>
) : (
<ActionIcon
icon={isPlaying ? StopCircle : Play}
onClick={isPlaying ? stop : play}
size={buttonSize}
style={{ flex: 'none' }}
/>
)}
<ActionIcon
icon={isPlaying ? (allowPause ? PauseCircle : StopCircle) : Play}
loading={isLoading}
onClick={isPlaying ? (allowPause ? pause : stop) : play}
size={buttonSize || { blockSize: 32, fontSize: 16 }}
style={{ flex: 'none' }}
/>
{showSlider && (
<Slider
disabled={duration === 0}
max={duration}
min={0}
onChange={(e) => setTime(e)}
step={0.01}
style={{ flex: 1 }}
tooltip={{ formatter: secondsToMinutesAndSeconds as any }}
value={currentTime}
/>
)}
{showTime && (
<Time style={{ flex: 'none', ...timeStyle }}>
<Dropdown
disabled={duration === 0}
menu={{
items: [
{
key: 'download',
label: <Icon icon={Download} size={{ fontSize: 16 }} />,
onClick: download,
},
],
}}
placement="top"
>
<Time style={{ cursor: 'pointer', flex: 'none', ...timeStyle }}>
{timeType === 'left' && formatedLeftTime}
{timeType === 'current' && formatedCurrentTime}
{timeType === 'combine' && (
Expand All @@ -100,15 +104,7 @@ const AudioPlayer = memo<AudioPlayerProps>(
</span>
)}
</Time>
)}
{showDownload && (
<ActionIcon
icon={Download}
onClick={download}
size={buttonSize}
style={{ flex: 'none' }}
/>
)}
</Dropdown>
</Flexbox>
);
},
Expand Down
38 changes: 38 additions & 0 deletions src/AudioVisualizer/Visualizer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useAudioVisualizer } from '@lobehub/tts';
import { useTheme } from 'antd-style';
import { RefObject, memo } from 'react';

export interface VisualizerProps {
borderRadius?: number;
color?: string;
count?: number;
gap?: number;
maxHeight?: number;
minHeight?: number;
width?: number;
}

const Visualizer = memo<VisualizerProps & { audioRef: RefObject<HTMLAudioElement> }>(
({ audioRef, count = 4, width = 48, color, ...barStyle }) => {
const maxHeight = barStyle?.maxHeight || width * 3;
const minHeight = barStyle?.minHeight || width;
const borderRadius = barStyle?.borderRadius || width / 2;
const theme = useTheme();
const bars = useAudioVisualizer(audioRef, { count });

return bars.map((bar, index) => (
<div
key={index}
style={{
background: color || theme.colorPrimary,
borderRadius,
height: minHeight + (bar / 255) * (maxHeight - minHeight),
transition: 'height 50ms cubic-bezier(.2,-0.5,.8,1.5)',
width,
}}
/>
));
},
);

export default Visualizer;
6 changes: 3 additions & 3 deletions src/AudioVisualizer/demos/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ export default () => {

return (
<StoryBook levaStore={store}>
<Flexbox gap={8}>
<AudioPlayer audio={audio} />
{!isLoading && audio.duration > 0 && <AudioVisualizer audioRef={ref} barStyle={barStyle} />}
<Flexbox align={'center'} gap={8}>
<AudioPlayer audio={audio} isLoading={isLoading} style={{ width: '100%' }} />
<AudioVisualizer audioRef={ref} barStyle={barStyle} isLoading={isLoading} />
</Flexbox>
</StoryBook>
);
Expand Down
74 changes: 39 additions & 35 deletions src/AudioVisualizer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,49 @@
import { useTheme } from 'antd-style';
import React, { RefObject, memo } from 'react';
import { Icon } from '@lobehub/ui';
import { Loader2 } from 'lucide-react';
import { CSSProperties, RefObject, memo } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { Flexbox } from 'react-layout-kit';

import { useAudioVisualizer } from '@/hooks/useAudioVisualizer';
import Visualizer, { VisualizerProps } from '@/AudioVisualizer/Visualizer';

export interface AudioVisualizerProps {
audioRef: RefObject<HTMLAudioElement>;
barStyle?: {
borderRadius?: number;
count?: number;
gap?: number;
maxHeight?: number;
minHeight?: number;
width?: number;
};
barStyle?: VisualizerProps;
className?: string;
color?: string;
isLoading?: boolean;
style?: CSSProperties;
}

const AudioVisualizer = memo<AudioVisualizerProps>(({ color, audioRef, barStyle }) => {
const { count, width, gap } = { count: 4, gap: 4, width: 48, ...barStyle };
const maxHeight = barStyle?.maxHeight || width * 3;
const minHeight = barStyle?.minHeight || width;
const borderRadius = barStyle?.borderRadius || width / 2;
const theme = useTheme();
const bars = useAudioVisualizer(audioRef, { count });
return (
<Flexbox align={'center'} gap={gap} horizontal style={{ height: maxHeight }}>
{bars.map((bar, index) => (
<div
key={index}
style={{
background: color || theme.colorPrimary,
borderRadius,
height: minHeight + (bar / 255) * (maxHeight - minHeight),
transition: 'height 50ms cubic-bezier(.2,-0.5,.8,1.5)',
width,
}}
/>
))}
</Flexbox>
);
});
const AudioVisualizer = memo<AudioVisualizerProps>(
({ audioRef, isLoading, barStyle, style, className }) => {
const { count, width, gap } = { count: 4, gap: 4, width: 48, ...barStyle };
const maxHeight = barStyle?.maxHeight || width * 3;
const containerStyle: CSSProperties = {
fontSize: 24,
height: maxHeight,
minWidth: (width + gap) * count,
...style,
};
return (
<ErrorBoundary fallback={<div className={className} style={containerStyle}></div>}>
<Flexbox
align={'center'}
className={className}
gap={gap}
horizontal
justify={'center'}
style={containerStyle}
>
{isLoading ? (
<Icon icon={Loader2} spin />
) : (
<Visualizer audioRef={audioRef} {...barStyle} />
)}
</Flexbox>
</ErrorBoundary>
);
},
);

export default AudioVisualizer;
8 changes: 7 additions & 1 deletion src/hooks/useAudioPlayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ export const useAudioPlayer = (src: string): AudioPlayerHook => {
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const [isGlobalLoading, setIsGlobalLoading] = useState(true);

const { isLoading } = useSWR(src, async () => {
setIsGlobalLoading(true);
const data = await fetch(src);
const arrayBuffer = await data.arrayBuffer();
const audioBuffer = await arrayBufferConvert(arrayBuffer);
Expand All @@ -33,6 +35,7 @@ export const useAudioPlayer = (src: string): AudioPlayerHook => {
if (!audioRef.current) return;
const onLoadedMetadata = () => {
setDuration(audioRef.current.duration);
setIsGlobalLoading(false);
};
const onTimeUpdate = () => {
setCurrentTime(audioRef.current.currentTime);
Expand All @@ -46,6 +49,8 @@ export const useAudioPlayer = (src: string): AudioPlayerHook => {
audioRef.current.currentTime = 0;
setCurrentTime(0);
};

audioRef.current.addEventListener('ended', onEnded);
audioRef.current.addEventListener('error', onError);
audioRef.current.addEventListener('loadedmetadata', onLoadedMetadata);
audioRef.current.addEventListener('timeupdate', onTimeUpdate);
Expand All @@ -57,6 +62,7 @@ export const useAudioPlayer = (src: string): AudioPlayerHook => {
audioRef.current.removeEventListener('loadedmetadata', onLoadedMetadata);
audioRef.current.removeEventListener('timeupdate', onTimeUpdate);
audioRef.current.removeEventListener('error', onError);
setIsGlobalLoading(true);
};
}, []);

Expand Down Expand Up @@ -101,7 +107,7 @@ export const useAudioPlayer = (src: string): AudioPlayerHook => {
currentTime,
download: handleDownload,
duration,
isLoading,
isLoading: isLoading || isGlobalLoading,
isPlaying,
pause: handlePause,
play: handlePlay,
Expand Down
42 changes: 15 additions & 27 deletions src/hooks/useAudioVisualizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const useAudioVisualizer = (
const analyserRef = useRef<AnalyserNode | null>(null);
const dataArrayRef = useRef<Uint8Array | null>(null);
const animationFrameIdRef = useRef<number | null>(null);
const audioSourceRef = useRef<MediaElementAudioSourceNode | null>(null);
const [init, setInit] = useState(false);

const renderFrame = throttle(() => {
Expand All @@ -40,37 +41,24 @@ export const useAudioVisualizer = (
useEffect(() => {
if (!audioRef.current) return;

// 初始化AudioContext
const audioContext = new AudioContext();
audioContextRef.current = audioContext;

// 确保AudioContext处于活动状态
if (audioContext.state === 'suspended') {
audioContext.resume().then(() => {
console.log('Playback resumed successfully');
});
try {
audioContextRef.current = new AudioContext();
analyserRef.current = audioContextRef.current.createAnalyser();
analyserRef.current.fftSize = 256;
const bufferLength = analyserRef.current.frequencyBinCount;
dataArrayRef.current = new Uint8Array(bufferLength);
audioSourceRef.current = audioContextRef.current.createMediaElementSource(audioRef.current);
audioSourceRef.current.connect(analyserRef.current);
analyserRef.current.connect(audioContextRef.current.destination);
} catch (error) {
console.error(error);
}

const analyser = audioContext.createAnalyser();
analyserRef.current = analyser;
analyser.fftSize = 256;
const bufferLength = analyser.frequencyBinCount;
dataArrayRef.current = new Uint8Array(bufferLength);

// 创建音频源
const audioSource = audioContext.createMediaElementSource(audioRef.current);
audioSource.connect(analyser);
analyser.connect(audioContext.destination);
setInit(true);
return () => {
audioSource.disconnect();
analyser.disconnect();

// 关闭AudioContext
if (audioContextRef.current) {
audioContextRef.current.close();
}

audioSourceRef.current?.disconnect();
analyserRef.current?.disconnect();
audioContextRef.current?.close();
setInit(false);
};
}, []);
Expand Down
Loading

0 comments on commit e4398be

Please sign in to comment.