[bugfix]: Validate audio sample range, catch AudioContext error (#470)

This commit is contained in:
Kendall Garner 2024-01-25 04:36:20 +00:00 committed by GitHub
parent 3bca85b3a8
commit 5f1d0a3b5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 32 additions and 8 deletions

View file

@ -510,7 +510,7 @@
"replayGainPreamp": "{{ReplayGain}} preamp (dB)", "replayGainPreamp": "{{ReplayGain}} preamp (dB)",
"replayGainPreamp_description": "adjust the preamp gain applied to the {{ReplayGain}} values", "replayGainPreamp_description": "adjust the preamp gain applied to the {{ReplayGain}} values",
"sampleRate": "sample rate", "sampleRate": "sample rate",
"sampleRate_description": "select the output sample rate to be used if the sample frequency selected is different from that of the current media", "sampleRate_description": "select the output sample rate to be used if the sample frequency selected is different from that of the current media. a value less than 8000 will use the default frequency",
"savePlayQueue": "save play queue", "savePlayQueue": "save play queue",
"savePlayQueue_description": "save the play queue when the application is closed and restore it when the application is opened", "savePlayQueue_description": "save the play queue when the application is closed and restore it when the application is opened",
"scrobble": "scrobble", "scrobble": "scrobble",

View file

@ -7,10 +7,11 @@ import {
crossfadeHandler, crossfadeHandler,
gaplessHandler, gaplessHandler,
} from '/@/renderer/components/audio-player/utils/list-handlers'; } from '/@/renderer/components/audio-player/utils/list-handlers';
import { useSettingsStore } from '/@/renderer/store/settings.store'; import { useSettingsStore, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import type { CrossfadeStyle } from '/@/renderer/types'; import type { CrossfadeStyle } from '/@/renderer/types';
import { PlaybackStyle, PlayerStatus } from '/@/renderer/types'; import { PlaybackStyle, PlayerStatus } from '/@/renderer/types';
import { useSpeed } from '/@/renderer/store'; import { useSpeed } from '/@/renderer/store';
import { toast } from '/@/renderer/components/toast';
interface AudioPlayerProps extends ReactPlayerProps { interface AudioPlayerProps extends ReactPlayerProps {
crossfadeDuration: number; crossfadeDuration: number;
@ -60,6 +61,7 @@ export const AudioPlayer = forwardRef(
const [isTransitioning, setIsTransitioning] = useState(false); const [isTransitioning, setIsTransitioning] = useState(false);
const audioDeviceId = useSettingsStore((state) => state.playback.audioDeviceId); const audioDeviceId = useSettingsStore((state) => state.playback.audioDeviceId);
const playback = useSettingsStore((state) => state.playback.mpvProperties); const playback = useSettingsStore((state) => state.playback.mpvProperties);
const { resetSampleRate } = useSettingsStoreActions();
const playbackSpeed = useSpeed(); const playbackSpeed = useSpeed();
const [webAudio, setWebAudio] = useState<WebAudio | null>(null); const [webAudio, setWebAudio] = useState<WebAudio | null>(null);
@ -119,10 +121,21 @@ export const AudioPlayer = forwardRef(
useEffect(() => { useEffect(() => {
if ('AudioContext' in window) { if ('AudioContext' in window) {
const context = new AudioContext({ let context: AudioContext;
latencyHint: 'playback',
sampleRate: playback.audioSampleRateHz || undefined, try {
}); context = new AudioContext({
latencyHint: 'playback',
sampleRate: playback.audioSampleRateHz || undefined,
});
} catch (error) {
// In practice, this should never be hit because the UI should validate
// the range. However, the actual supported range is not guaranteed
toast.error({ message: (error as Error).message });
context = new AudioContext({ latencyHint: 'playback' });
resetSampleRate();
}
const gain = context.createGain(); const gain = context.createGain();
gain.connect(context.destination); gain.connect(context.destination);

View file

@ -206,11 +206,16 @@ export const MpvSettings = () => {
{ {
control: ( control: (
<NumberInput <NumberInput
defaultValue={settings.mpvProperties.audioSampleRateHz} defaultValue={settings.mpvProperties.audioSampleRateHz || undefined}
max={192000}
min={0}
placeholder="48000"
rightSection="Hz"
width={100} width={100}
onBlur={(e) => { onBlur={(e) => {
const value = Number(e.currentTarget.value); const value = Number(e.currentTarget.value);
handleSetMpvProperty('audioSampleRateHz', value > 0 ? value : undefined); // Setting a value of `undefined` causes an error for MPV. Use 0 instead
handleSetMpvProperty('audioSampleRateHz', value >= 8000 ? value : value);
}} }}
/> />
), ),

View file

@ -251,6 +251,7 @@ export interface SettingsState {
export interface SettingsSlice extends SettingsState { export interface SettingsSlice extends SettingsState {
actions: { actions: {
reset: () => void; reset: () => void;
resetSampleRate: () => void;
setSettings: (data: Partial<SettingsState>) => void; setSettings: (data: Partial<SettingsState>) => void;
setSidebarItems: (items: SidebarItemType[]) => void; setSidebarItems: (items: SidebarItemType[]) => void;
setTable: (type: TableType, data: DataTableProps) => void; setTable: (type: TableType, data: DataTableProps) => void;
@ -567,6 +568,11 @@ export const useSettingsStore = create<SettingsSlice>()(
set(initialState); set(initialState);
} }
}, },
resetSampleRate: () => {
set((state) => {
state.playback.mpvProperties.audioSampleRateHz = 0;
});
},
setSettings: (data) => { setSettings: (data) => {
set({ ...get(), ...data }); set({ ...get(), ...data });
}, },