refac: voice call tap to interrupt

This commit is contained in:
Timothy J. Baek 2024-06-16 16:50:57 -07:00
parent e183b0e5ff
commit ad2ffc33d8

View File

@ -31,6 +31,7 @@
let loading = false; let loading = false;
let confirmed = false; let confirmed = false;
let interrupted = false; let interrupted = false;
let assistantSpeaking = false;
let emoji = null; let emoji = null;
@ -268,6 +269,14 @@
return; return;
} }
if (assistantSpeaking) {
analyser.maxDecibels = 0;
analyser.minDecibels = -10;
} else {
analyser.minDecibels = MIN_DECIBELS;
analyser.maxDecibels = -30;
}
analyser.getByteTimeDomainData(timeDomainData); analyser.getByteTimeDomainData(timeDomainData);
analyser.getByteFrequencyData(domainData); analyser.getByteFrequencyData(domainData);
@ -379,6 +388,7 @@
}; };
const stopAllAudio = async () => { const stopAllAudio = async () => {
assistantSpeaking = false;
interrupted = true; interrupted = true;
if (chatStreaming) { if (chatStreaming) {
@ -485,6 +495,7 @@
} }
} else if (finishedMessages[id] && messages[id] && messages[id].length === 0) { } else if (finishedMessages[id] && messages[id] && messages[id].length === 0) {
// If the message is finished and there are no more messages to process, break the loop // If the message is finished and there are no more messages to process, break the loop
assistantSpeaking = false;
break; break;
} else { } else {
// No messages to process, sleep for a bit // No messages to process, sleep for a bit
@ -511,6 +522,7 @@
} }
audioAbortController = new AbortController(); audioAbortController = new AbortController();
assistantSpeaking = true;
// Start monitoring and playing audio for the message ID // Start monitoring and playing audio for the message ID
monitorAndPlayAudio(id, audioAbortController.signal); monitorAndPlayAudio(id, audioAbortController.signal);
} }
@ -545,9 +557,9 @@
const chatFinishHandler = async (e) => { const chatFinishHandler = async (e) => {
const { id, content } = e.detail; const { id, content } = e.detail;
// "content" here is the entire message from the assistant // "content" here is the entire message from the assistant
finishedMessages[id] = true;
chatStreaming = false; chatStreaming = false;
finishedMessages[id] = true;
}; };
eventTarget.addEventListener('chat:start', chatStartHandler); eventTarget.addEventListener('chat:start', chatStartHandler);
@ -577,7 +589,15 @@
> >
<div class="max-w-lg w-full h-screen max-h-[100dvh] flex flex-col justify-between p-3 md:p-6"> <div class="max-w-lg w-full h-screen max-h-[100dvh] flex flex-col justify-between p-3 md:p-6">
{#if camera} {#if camera}
<div class="flex justify-center items-center w-full h-20 min-h-20"> <button
type="button"
class="flex justify-center items-center w-full h-20 min-h-20"
on:click={() => {
if (assistantSpeaking) {
stopAllAudio();
}
}}
>
{#if emoji} {#if emoji}
<div <div
class=" transition-all rounded-full" class=" transition-all rounded-full"
@ -640,72 +660,81 @@
/> />
{/if} {/if}
<!-- navbar --> <!-- navbar -->
</div> </button>
{/if} {/if}
<div class="flex justify-center items-center flex-1 h-full w-full max-h-full"> <div class="flex justify-center items-center flex-1 h-full w-full max-h-full">
{#if !camera} {#if !camera}
{#if emoji} <button
<div type="button"
class=" transition-all rounded-full" on:click={() => {
style="font-size:{rmsLevel * 100 > 4 if (assistantSpeaking) {
? '13' stopAllAudio();
: rmsLevel * 100 > 2 }
? '12' }}
: rmsLevel * 100 > 1 >
? '11.5' {#if emoji}
: '11'}rem;width:100%;text-align:center;" <div
> class=" transition-all rounded-full"
{emoji} style="font-size:{rmsLevel * 100 > 4
</div> ? '13'
{:else if loading} : rmsLevel * 100 > 2
<svg ? '12'
class="size-44 text-gray-900 dark:text-gray-400" : rmsLevel * 100 > 1
viewBox="0 0 24 24" ? '11.5'
fill="currentColor" : '11'}rem;width:100%;text-align:center;"
xmlns="http://www.w3.org/2000/svg" >
><style> {emoji}
.spinner_qM83 { </div>
animation: spinner_8HQG 1.05s infinite; {:else if loading}
} <svg
.spinner_oXPr { class="size-44 text-gray-900 dark:text-gray-400"
animation-delay: 0.1s; viewBox="0 0 24 24"
} fill="currentColor"
.spinner_ZTLf { xmlns="http://www.w3.org/2000/svg"
animation-delay: 0.2s; ><style>
} .spinner_qM83 {
@keyframes spinner_8HQG { animation: spinner_8HQG 1.05s infinite;
0%,
57.14% {
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
transform: translate(0);
} }
28.57% { .spinner_oXPr {
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33); animation-delay: 0.1s;
transform: translateY(-6px);
} }
100% { .spinner_ZTLf {
transform: translate(0); animation-delay: 0.2s;
} }
} @keyframes spinner_8HQG {
</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle 0%,
class="spinner_qM83 spinner_oXPr" 57.14% {
cx="12" animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
cy="12" transform: translate(0);
r="3" }
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg 28.57% {
> animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
{:else} transform: translateY(-6px);
<div }
class=" {rmsLevel * 100 > 4 100% {
? ' size-52' transform: translate(0);
: rmsLevel * 100 > 2 }
? 'size-48' }
: rmsLevel * 100 > 1 </style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
? 'size-[11.5rem]' class="spinner_qM83 spinner_oXPr"
: 'size-44'} transition-all bg-black dark:bg-white rounded-full" cx="12"
/> cy="12"
{/if} r="3"
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
>
{:else}
<div
class=" {rmsLevel * 100 > 4
? ' size-52'
: rmsLevel * 100 > 2
? 'size-48'
: rmsLevel * 100 > 1
? 'size-[11.5rem]'
: 'size-44'} transition-all bg-black dark:bg-white rounded-full"
/>
{/if}
</button>
{:else} {:else}
<div <div
class="relative flex video-container w-full max-h-full pt-2 pb-4 md:py-6 px-2 h-full" class="relative flex video-container w-full max-h-full pt-2 pb-4 md:py-6 px-2 h-full"
@ -805,10 +834,19 @@
</div> </div>
<div> <div>
<button type="button"> <button
type="button"
on:click={() => {
if (assistantSpeaking) {
stopAllAudio();
}
}}
>
<div class=" line-clamp-1 text-sm font-medium"> <div class=" line-clamp-1 text-sm font-medium">
{#if loading} {#if loading}
{$i18n.t('Thinking...')} {$i18n.t('Thinking...')}
{:else if assistantSpeaking}
{$i18n.t('Tap to interrupt')}
{:else} {:else}
{$i18n.t('Listening...')} {$i18n.t('Listening...')}
{/if} {/if}