feat: add volume control

This commit is contained in:
verbiricha
2023-09-10 14:03:13 +02:00
parent 216311951b
commit fc37cfe92d
5 changed files with 33 additions and 6 deletions

View File

@ -404,6 +404,9 @@
"x82IOl": { "x82IOl": {
"defaultMessage": "Mute" "defaultMessage": "Mute"
}, },
"y867Vs": {
"defaultMessage": "Volume"
},
"yzKwBQ": { "yzKwBQ": {
"defaultMessage": "eg. nsec1xyz" "defaultMessage": "eg. nsec1xyz"
}, },

View File

@ -27,6 +27,7 @@ function ZapAlertConfiguration({ npub, baseUrl }: ZapAlertConfigurationProps) {
const [textToSpeech, setTextToSpeech] = useState<boolean>(false); const [textToSpeech, setTextToSpeech] = useState<boolean>(false);
const [voice, setVoice] = useState<string | null>(null); const [voice, setVoice] = useState<string | null>(null);
const [minSatsForTextToSpeech, setMinSatsForTextToSpeech] = useState<string>("21"); const [minSatsForTextToSpeech, setMinSatsForTextToSpeech] = useState<string>("21");
const [volume, setVolume] = useState<number>(1);
// Google propietary voices are not available on OBS browser // Google propietary voices are not available on OBS browser
const voices = getVoices().filter(v => !v.name.includes("Google")); const voices = getVoices().filter(v => !v.name.includes("Google"));
@ -47,14 +48,15 @@ function ZapAlertConfiguration({ npub, baseUrl }: ZapAlertConfigurationProps) {
const params = toTextToSpeechParams({ const params = toTextToSpeechParams({
voiceURI: voice, voiceURI: voice,
minSats: voice ? Number(minSatsForTextToSpeech) : null, minSats: voice ? Number(minSatsForTextToSpeech) : null,
volume,
}); });
const queryParams = params.toString(); const queryParams = params.toString();
return queryParams.length > 0 ? `?${queryParams}` : ""; return queryParams.length > 0 ? `?${queryParams}` : "";
}, [voice, minSatsForTextToSpeech]); }, [voice, volume, minSatsForTextToSpeech]);
function testVoice() { function testVoice() {
if (selectedVoice) { if (selectedVoice) {
speak(selectedVoice, testText); speak(selectedVoice, testText, volume);
} }
} }
@ -103,6 +105,20 @@ function ZapAlertConfiguration({ npub, baseUrl }: ZapAlertConfigurationProps) {
onChange={ev => setMinSatsForTextToSpeech(ev.target.value)} onChange={ev => setMinSatsForTextToSpeech(ev.target.value)}
/> />
</div> </div>
<div className="paper labeled-input">
<label htmlFor="volume">
<FormattedMessage defaultMessage="Volume" />
</label>
<input
id="volume"
type="number"
min="0"
max="1"
step="0.1"
value={volume}
onChange={ev => setVolume(Number(ev.target.value))}
/>
</div>
<div className="paper labeled-input"> <div className="paper labeled-input">
<label htmlFor="voice-selector"> <label htmlFor="voice-selector">
<FormattedMessage defaultMessage="Voice" /> <FormattedMessage defaultMessage="Voice" />

View File

@ -39,7 +39,7 @@ export function ZapAlerts({ link }: { link: NostrLink }) {
const zaps = useZaps(currentLink, true); const zaps = useZaps(currentLink, true);
const zap = useZapQueue(zaps); const zap = useZapQueue(zaps);
const mutedPubkeys = useMutedPubkeys(host, true); const mutedPubkeys = useMutedPubkeys(host, true);
const { voiceURI, minSats } = useTextToSpeechParams(); const { voiceURI, minSats, volume } = useTextToSpeechParams();
const voices = getVoices(); const voices = getVoices();
const voice = useMemo(() => { const voice = useMemo(() => {
return voices.find(v => v.voiceURI === voiceURI); return voices.find(v => v.voiceURI === voiceURI);
@ -56,7 +56,7 @@ export function ZapAlerts({ link }: { link: NostrLink }) {
if (text.length > 0 && voice) { if (text.length > 0 && voice) {
try { try {
if (zap.amount >= minSats) { if (zap.amount >= minSats) {
speak(voice, text); speak(voice, text, volume);
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -9,18 +9,21 @@ function useQuery() {
interface TextToSpeechConfig { interface TextToSpeechConfig {
voiceURI: string | null; voiceURI: string | null;
minSats: number; minSats: number;
volume: number;
} }
export function useTextToSpeechParams(): TextToSpeechConfig { export function useTextToSpeechParams(): TextToSpeechConfig {
const q = useQuery(); const q = useQuery();
const voiceURI = q.get("voiceURI"); const voiceURI = q.get("voiceURI");
const minSats = Number(q.get("minSats")) ?? 21; const minSats = Number(q.get("minSats")) ?? 21;
return { voiceURI, minSats }; const volume = Number(q.get("volume")) ?? 1;
return { voiceURI, minSats, volume };
} }
interface TextToSpeechConfigParams { interface TextToSpeechConfigParams {
voiceURI: string | null; voiceURI: string | null;
minSats: number | null; minSats: number | null;
volume: number | null;
} }
export function toTextToSpeechParams(config: TextToSpeechConfigParams): URLSearchParams { export function toTextToSpeechParams(config: TextToSpeechConfigParams): URLSearchParams {
@ -31,6 +34,9 @@ export function toTextToSpeechParams(config: TextToSpeechConfigParams): URLSearc
if (config.minSats) { if (config.minSats) {
params.set("minSats", String(config.minSats)); params.set("minSats", String(config.minSats));
} }
if (config.volume) {
params.set("volume", String(config.volume));
}
return params; return params;
} }
@ -41,10 +47,11 @@ export function getVoices() {
return []; return [];
} }
export function speak(voice: SpeechSynthesisVoice, text: string) { export function speak(voice: SpeechSynthesisVoice, text: string, volume: number) {
try { try {
const utterance = new SpeechSynthesisUtterance(text); const utterance = new SpeechSynthesisUtterance(text);
utterance.voice = voice; utterance.voice = voice;
utterance.volume = volume;
utterance.rate = 0.8; utterance.rate = 0.8;
speechSynthesis.speak(utterance); speechSynthesis.speak(utterance);
} catch (e) { } catch (e) {

View File

@ -134,6 +134,7 @@
"wOy57k": "Add stream goal", "wOy57k": "Add stream goal",
"wzWWzV": "Top zappers", "wzWWzV": "Top zappers",
"x82IOl": "Mute", "x82IOl": "Mute",
"y867Vs": "Volume",
"yzKwBQ": "eg. nsec1xyz", "yzKwBQ": "eg. nsec1xyz",
"zVDHAu": "Zap Alert" "zVDHAu": "Zap Alert"
} }