@@ -13,15 +13,19 @@ import { MicrophoneIcon } from '@phosphor-icons/react/dist/ssr';
1313import { useSession } from '@/components/app/session-provider' ;
1414import { AgentControlBar } from '@/components/livekit/agent-control-bar/agent-control-bar' ;
1515import { TrackControl } from '@/components/livekit/agent-control-bar/track-control' ;
16- import { TrackDeviceSelect } from '@/components/livekit/agent-control-bar/track-device-select' ;
17- import { TrackToggle } from '@/components/livekit/agent-control-bar/track-toggle' ;
16+ // import { TrackDeviceSelect } from '@/components/livekit/agent-control-bar/track-device-select';
17+ // import { TrackToggle } from '@/components/livekit/agent-control-bar/track-toggle';
1818import { Alert , AlertDescription , AlertTitle , alertVariants } from '@/components/livekit/alert' ;
1919import { AlertToast } from '@/components/livekit/alert-toast' ;
2020import { BarVisualizer } from '@/components/livekit/audio-visualizer/audio-bar-visualizer/_bar-visualizer' ;
2121import {
2222 AudioBarVisualizer ,
2323 audioBarVisualizerVariants ,
2424} from '@/components/livekit/audio-visualizer/audio-bar-visualizer/audio-bar-visualizer' ;
25+ import {
26+ AudioRadialVisualizer ,
27+ audioRadialVisualizerVariants ,
28+ } from '@/components/livekit/audio-visualizer/audio-radial-visualizer/audio-radial-visualizer' ;
2529import { Button , buttonVariants } from '@/components/livekit/button' ;
2630import { ChatEntry } from '@/components/livekit/chat-entry' ;
2731import {
@@ -40,6 +44,9 @@ type buttonVariantsType = VariantProps<typeof buttonVariants>['variant'];
4044type buttonVariantsSizeType = VariantProps < typeof buttonVariants > [ 'size' ] ;
4145type alertVariantsType = VariantProps < typeof alertVariants > [ 'variant' ] ;
4246type audioBarVisualizerVariantsSizeType = VariantProps < typeof audioBarVisualizerVariants > [ 'size' ] ;
47+ type audioRadialVisualizerVariantsSizeType = VariantProps <
48+ typeof audioRadialVisualizerVariants
49+ > [ 'size' ] ;
4350
4451export function useMicrophone ( ) {
4552 const { startSession } = useSession ( ) ;
@@ -191,10 +198,10 @@ export const COMPONENTS = {
191198 </ Container >
192199 ) ,
193200
194- // Audio visualizer
195- AudioVisualizer : ( ) => {
201+ // Audio bar visualizer
202+ AudioBarVisualizer : ( ) => {
196203 const barCounts = [ '0' , '3' , '5' , '7' , '9' ] ;
197- const sizes = [ 'icon' , 'xs' , ' sm', 'md' , 'lg' , 'xl' ] ;
204+ const sizes = [ 'icon' , 'sm' , 'md' , 'lg' , 'xl' ] ;
198205 const states = [
199206 'disconnected' ,
200207 'connecting' ,
@@ -207,7 +214,7 @@ export const COMPONENTS = {
207214 const { microphoneTrack, localParticipant } = useLocalParticipant ( ) ;
208215 const [ barCount , setBarCount ] = useState < string > ( barCounts [ 0 ] ) ;
209216 const [ size , setSize ] = useState < audioBarVisualizerVariantsSizeType > (
210- sizes [ 3 ] as audioBarVisualizerVariantsSizeType
217+ 'md' as audioBarVisualizerVariantsSizeType
211218 ) ;
212219 const [ state , setState ] = useState < AgentState > ( states [ 0 ] ) ;
213220
@@ -258,7 +265,7 @@ export const COMPONENTS = {
258265 < SelectContent >
259266 { sizes . map ( ( size ) => (
260267 < SelectItem key = { size } value = { size as string } >
261- { size }
268+ { size . toUpperCase ( ) }
262269 </ SelectItem >
263270 ) ) }
264271 </ SelectContent >
@@ -285,7 +292,7 @@ export const COMPONENTS = {
285292 </ div >
286293
287294 < div className = "relative flex flex-col justify-center gap-4" >
288- < div className = "grid h-40 place-items-center" >
295+ < div className = "grid place-items-center py-8 " >
289296 < AudioBarVisualizer
290297 size = { size as audioBarVisualizerVariantsSizeType }
291298 state = { state }
@@ -295,7 +302,7 @@ export const COMPONENTS = {
295302 />
296303 </ div >
297304 < div className = "text-center" > Original BarVisualizer</ div >
298- < div className = "border-border grid h-40 place-items-center space-y-4 rounded-xl border p-4" >
305+ < div className = "border-border grid place-items-center rounded-xl border p-4 py-8 " >
299306 < BarVisualizer
300307 size = { size as audioBarVisualizerVariantsSizeType }
301308 state = { state }
@@ -309,6 +316,114 @@ export const COMPONENTS = {
309316 ) ;
310317 } ,
311318
319+ // Audio bar visualizer
320+ AudioRadialVisualizer : ( ) => {
321+ const barCounts = [ '0' , '4' , '8' , '12' , '16' , '24' ] ;
322+ const sizes = [ 'icon' , 'sm' , 'md' , 'lg' , 'xl' ] ;
323+ const states = [
324+ 'disconnected' ,
325+ 'connecting' ,
326+ 'initializing' ,
327+ 'listening' ,
328+ 'thinking' ,
329+ 'speaking' ,
330+ ] as AgentState [ ] ;
331+
332+ const { microphoneTrack, localParticipant } = useLocalParticipant ( ) ;
333+ const [ barCount , setBarCount ] = useState < string > ( barCounts [ 0 ] ) ;
334+ const [ size , setSize ] = useState < audioRadialVisualizerVariantsSizeType > (
335+ 'md' as audioRadialVisualizerVariantsSizeType
336+ ) ;
337+ const [ state , setState ] = useState < AgentState > ( states [ 0 ] ) ;
338+
339+ const micTrackRef = useMemo < TrackReferenceOrPlaceholder | undefined > ( ( ) => {
340+ return state === 'speaking'
341+ ? ( {
342+ participant : localParticipant ,
343+ source : Track . Source . Microphone ,
344+ publication : microphoneTrack ,
345+ } as TrackReference )
346+ : undefined ;
347+ } , [ state , localParticipant , microphoneTrack ] ) ;
348+
349+ useMicrophone ( ) ;
350+
351+ return (
352+ < Container componentName = "AudioVisualizer" >
353+ < div className = "flex items-center gap-2" >
354+ < div className = "flex-1" >
355+ < label className = "font-mono text-xs uppercase" htmlFor = "state" >
356+ State
357+ </ label >
358+ < Select value = { state } onValueChange = { ( value ) => setState ( value as AgentState ) } >
359+ < SelectTrigger id = "state" className = "w-full" >
360+ < SelectValue placeholder = "Select a state" />
361+ </ SelectTrigger >
362+ < SelectContent >
363+ { states . map ( ( state ) => (
364+ < SelectItem key = { state } value = { state } >
365+ { state }
366+ </ SelectItem >
367+ ) ) }
368+ </ SelectContent >
369+ </ Select >
370+ </ div >
371+
372+ < div className = "flex-1" >
373+ < label className = "font-mono text-xs uppercase" htmlFor = "size" >
374+ Size
375+ </ label >
376+ < Select
377+ value = { size as string }
378+ onValueChange = { ( value ) => setSize ( value as audioRadialVisualizerVariantsSizeType ) }
379+ >
380+ < SelectTrigger id = "size" className = "w-full" >
381+ < SelectValue placeholder = "Select a size" />
382+ </ SelectTrigger >
383+ < SelectContent >
384+ { sizes . map ( ( size ) => (
385+ < SelectItem key = { size } value = { size as string } >
386+ { size . toUpperCase ( ) }
387+ </ SelectItem >
388+ ) ) }
389+ </ SelectContent >
390+ </ Select >
391+ </ div >
392+
393+ < div className = "flex-1" >
394+ < label className = "font-mono text-xs uppercase" htmlFor = "barCount" >
395+ Bar count
396+ </ label >
397+ < Select value = { barCount . toString ( ) } onValueChange = { ( value ) => setBarCount ( value ) } >
398+ < SelectTrigger id = "barCount" className = "w-full" >
399+ < SelectValue placeholder = "Select a bar count" />
400+ </ SelectTrigger >
401+ < SelectContent >
402+ { barCounts . map ( ( barCount ) => (
403+ < SelectItem key = { barCount } value = { barCount . toString ( ) } >
404+ { parseInt ( barCount ) || 'Default' }
405+ </ SelectItem >
406+ ) ) }
407+ </ SelectContent >
408+ </ Select >
409+ </ div >
410+ </ div >
411+
412+ < div className = "relative flex flex-col justify-center gap-4" >
413+ < div className = "grid place-items-center py-20" >
414+ < AudioRadialVisualizer
415+ size = { size as audioBarVisualizerVariantsSizeType }
416+ state = { state }
417+ audioTrack = { micTrackRef ! }
418+ barCount = { parseInt ( barCount ) || undefined }
419+ className = "mx-auto"
420+ />
421+ </ div >
422+ </ div >
423+ </ Container >
424+ ) ;
425+ } ,
426+
312427 // Agent control bar
313428 AgentControlBar : ( ) => {
314429 useMicrophone ( ) ;
0 commit comments