diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index a5bfffd4..6bfabc58 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -24,6 +24,7 @@ import { code2hash, hash2code } from './helpers.mjs'; import { isTauri } from '../tauri.mjs'; import { useWidgets } from '@strudel.cycles/react/src/hooks/useWidgets.mjs'; import { writeText } from '@tauri-apps/api/clipboard'; +import { defaultAudioDeviceName, getAudioDevices, setAudioDevice } from './panel/AudioDeviceSelector'; const { latestCode } = settingsMap.get(); @@ -132,6 +133,7 @@ export function Repl({ embedded = false }) { panelPosition, isZen, activePattern, + audioDeviceName, } = useSettings(); const paintOptions = useMemo(() => ({ fontFamily }), [fontFamily]); @@ -171,8 +173,9 @@ export function Repl({ embedded = false }) { paintOptions, }); - // init code + //on first load... useEffect(() => { + // init code initCode().then((decoded) => { let msg; if (decoded) { @@ -188,6 +191,16 @@ export function Repl({ embedded = false }) { logger(`Welcome to Strudel! ${msg} Press play or hit ctrl+enter to run it!`, 'highlight'); setPending(false); }); + // Initialize user audio device if it has been saved to settings + if (audioDeviceName !== defaultAudioDeviceName) { + getAudioDevices().then((devices) => { + const deviceID = devices.get(audioDeviceName); + if (deviceID == null) { + return; + } + setAudioDevice(deviceID); + }); + } }, []); // keyboard shortcuts diff --git a/website/src/repl/panel/AudioDeviceSelector.jsx b/website/src/repl/panel/AudioDeviceSelector.jsx index 603c4537..d39e58d2 100644 --- a/website/src/repl/panel/AudioDeviceSelector.jsx +++ b/website/src/repl/panel/AudioDeviceSelector.jsx @@ -1,56 +1,44 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState } from 'react'; import { getAudioContext, initializeAudioOutput } from '@strudel.cycles/webaudio'; import { SelectInput } from './SelectInput'; const initdevices = new Map(); -const defaultDeviceName = 'System Standard'; +export const defaultAudioDeviceName = 'System Standard'; + +export const getAudioDevices = async () => { + await navigator.mediaDevices.getUserMedia({ audio: true }); + let mediaDevices = await navigator.mediaDevices.enumerateDevices(); + mediaDevices = mediaDevices.filter((device) => device.kind === 'audiooutput' && device.deviceId !== 'default'); + const devicesMap = new Map(); + devicesMap.set(defaultAudioDeviceName, ''); + mediaDevices.forEach((device) => { + devicesMap.set(device.label, device.deviceId); + }); + return devicesMap; +}; + +export const setAudioDevice = async (id) => { + const isValidID = (id ?? '').length > 0; + // reset the audio context and dont set the sink id if it is invalid AKA System Standard selection + const audioCtx = getAudioContext(!isValidID); + if (isValidID) { + await audioCtx.setSinkId(id); + } + initializeAudioOutput(); +}; + // Allows the user to select an audio interface for Strudel to play through export function AudioDeviceSelector({ audioDeviceName, onChange }) { const [devices, setDevices] = useState(initdevices); const devicesInitialized = devices.size > 0; - const setAudioDevice = useCallback(async (id) => { - const isValidID = (id ?? '').length > 0; - // reset the audio context and dont set the sink id if it is invalid AKA System Standard selection - const audioCtx = getAudioContext(!isValidID); - if (isValidID) { - await audioCtx.setSinkId(id); - } - initializeAudioOutput(); - }); - const initializedevices = useCallback(async () => { - await navigator.mediaDevices.getUserMedia({ audio: true }); - let mediaDevices = await navigator.mediaDevices.enumerateDevices(); - mediaDevices = mediaDevices.filter((device) => device.kind === 'audiooutput' && device.deviceId !== 'default'); - const devicesMap = new Map(); - devicesMap.set(defaultDeviceName, ''); - mediaDevices.forEach((device) => { - devicesMap.set(device.label, device.deviceId); - }); - setDevices(devicesMap); - return devicesMap; - }, []); - - // on first load, check if there is a cached audio device name in settings and initialize it - useEffect(() => { - if (!audioDeviceName.length || devicesInitialized) { - return; - } - initializedevices().then((devices) => { - const deviceID = devices.get(audioDeviceName); - if (deviceID == null) { - onChange(defaultDeviceName); - return; - } - setAudioDevice(deviceID); - }); - }, []); - const onClick = () => { if (devicesInitialized) { return; } - initializedevices(); + getAudioDevices().then((devices) => { + setDevices(devices); + }); }; const onDeviceChange = (deviceName) => { if (!devicesInitialized) { @@ -64,13 +52,5 @@ export function AudioDeviceSelector({ audioDeviceName, onChange }) { Array.from(devices.keys()).forEach((deviceName) => { options.set(deviceName, deviceName); }); - return ( - - ); + return ; } diff --git a/website/src/repl/panel/SelectInput.jsx b/website/src/repl/panel/SelectInput.jsx index 3c3fb84e..1bbb6d84 100644 --- a/website/src/repl/panel/SelectInput.jsx +++ b/website/src/repl/panel/SelectInput.jsx @@ -8,6 +8,7 @@ export function SelectInput({ value, options, onChange, onClick }) { value={value ?? ''} onChange={(e) => onChange(e.target.value)} > + {options.size == 0 && } {Array.from(options.keys()).map((id) => (