@@ -22,6 +22,11 @@ import {
2222 AudioBarVisualizer ,
2323 audioBarVisualizerVariants ,
2424} from '@/components/livekit/audio-visualizer/audio-bar-visualizer/audio-bar-visualizer' ;
25+ import {
26+ AudioGridVisualizer ,
27+ type GridOptions ,
28+ } from '@/components/livekit/audio-visualizer/audio-grid-visualizer/audio-grid-visualizer' ;
29+ import { gridVariants } from '@/components/livekit/audio-visualizer/audio-grid-visualizer/demos' ;
2530import {
2631 AudioRadialVisualizer ,
2732 audioRadialVisualizerVariants ,
@@ -424,6 +429,139 @@ export const COMPONENTS = {
424429 ) ;
425430 } ,
426431
432+ // Audio bar visualizer
433+ AudioGridVisualizer : ( ) => {
434+ const rowCounts = [ '3' , '5' , '7' , '9' , '11' , '13' , '15' ] ;
435+ const columnCounts = [ '3' , '5' , '7' , '9' , '11' , '13' , '15' ] ;
436+ const states = [
437+ 'disconnected' ,
438+ 'connecting' ,
439+ 'initializing' ,
440+ 'listening' ,
441+ 'thinking' ,
442+ 'speaking' ,
443+ ] as AgentState [ ] ;
444+
445+ const { microphoneTrack, localParticipant } = useLocalParticipant ( ) ;
446+ const [ rowCount , setRowCount ] = useState ( rowCounts [ 0 ] ) ;
447+ const [ columnCount , setColumnCount ] = useState ( columnCounts [ 0 ] ) ;
448+ const [ state , setState ] = useState < AgentState > ( states [ 0 ] ) ;
449+ const [ demoIndex , setDemoIndex ] = useState ( 0 ) ;
450+
451+ const micTrackRef = useMemo < TrackReferenceOrPlaceholder | undefined > ( ( ) => {
452+ return state === 'speaking'
453+ ? ( {
454+ participant : localParticipant ,
455+ source : Track . Source . Microphone ,
456+ publication : microphoneTrack ,
457+ } as TrackReference )
458+ : undefined ;
459+ } , [ state , localParticipant , microphoneTrack ] ) ;
460+
461+ useMicrophone ( ) ;
462+
463+ const demoOptions = {
464+ rowCount : parseInt ( rowCount ) ,
465+ columnCount : parseInt ( columnCount ) ,
466+ ...gridVariants [ demoIndex ] ,
467+ } ;
468+
469+ return (
470+ < Container componentName = "AudioVisualizer" >
471+ < div className = "flex items-center gap-2" >
472+ < div className = "flex-1" >
473+ < label className = "font-mono text-xs uppercase" htmlFor = "state" >
474+ State
475+ </ label >
476+ < Select value = { state } onValueChange = { ( value ) => setState ( value as AgentState ) } >
477+ < SelectTrigger id = "state" className = "w-full" >
478+ < SelectValue placeholder = "Select a state" />
479+ </ SelectTrigger >
480+ < SelectContent >
481+ { states . map ( ( state ) => (
482+ < SelectItem key = { state } value = { state } >
483+ { state }
484+ </ SelectItem >
485+ ) ) }
486+ </ SelectContent >
487+ </ Select >
488+ </ div >
489+
490+ < div className = "flex-1" >
491+ < label className = "font-mono text-xs uppercase" htmlFor = "rowCount" >
492+ Row count
493+ </ label >
494+ < Select value = { rowCount . toString ( ) } onValueChange = { ( value ) => setRowCount ( value ) } >
495+ < SelectTrigger id = "rowCount" className = "w-full" >
496+ < SelectValue placeholder = "Select a bar count" />
497+ </ SelectTrigger >
498+ < SelectContent >
499+ { rowCounts . map ( ( rowCount ) => (
500+ < SelectItem key = { rowCount } value = { rowCount . toString ( ) } >
501+ { parseInt ( rowCount ) || 'Default' }
502+ </ SelectItem >
503+ ) ) }
504+ </ SelectContent >
505+ </ Select >
506+ </ div >
507+
508+ < div className = "flex-1" >
509+ < label className = "font-mono text-xs uppercase" htmlFor = "columnCount" >
510+ Column count
511+ </ label >
512+ < Select value = { columnCount . toString ( ) } onValueChange = { ( value ) => setColumnCount ( value ) } >
513+ < SelectTrigger id = "columnCount" className = "w-full" >
514+ < SelectValue placeholder = "Select a column count" />
515+ </ SelectTrigger >
516+ < SelectContent >
517+ { columnCounts . map ( ( columnCount ) => (
518+ < SelectItem key = { columnCount } value = { columnCount . toString ( ) } >
519+ { parseInt ( columnCount ) || 'Default' }
520+ </ SelectItem >
521+ ) ) }
522+ </ SelectContent >
523+ </ Select >
524+ </ div >
525+
526+ < div className = "flex-1" >
527+ < label className = "font-mono text-xs uppercase" htmlFor = "demoIndex" >
528+ Demo
529+ </ label >
530+ < Select
531+ value = { demoIndex . toString ( ) }
532+ onValueChange = { ( value ) => setDemoIndex ( parseInt ( value ) ) }
533+ >
534+ < SelectTrigger id = "demoIndex" className = "w-full" >
535+ < SelectValue placeholder = "Select a demo" />
536+ </ SelectTrigger >
537+ < SelectContent >
538+ { gridVariants . map ( ( _ , idx ) => (
539+ < SelectItem key = { idx } value = { idx . toString ( ) } >
540+ Demo { String ( idx + 1 ) }
541+ </ SelectItem >
542+ ) ) }
543+ </ SelectContent >
544+ </ Select >
545+ </ div >
546+ </ div >
547+
548+ < div className = "grid place-items-center py-12" >
549+ < AudioGridVisualizer
550+ key = { `${ demoIndex } -${ rowCount } -${ columnCount } ` }
551+ state = { state }
552+ audioTrack = { micTrackRef ! }
553+ options = { demoOptions }
554+ />
555+ </ div >
556+ < div className = "border-border bg-muted overflow-x-auto rounded-xl border p-8" >
557+ < pre className = "text-muted-foreground text-sm" >
558+ < code > { JSON . stringify ( demoOptions , null , 2 ) } </ code >
559+ </ pre >
560+ </ div >
561+ </ Container >
562+ ) ;
563+ } ,
564+
427565 // Agent control bar
428566 AgentControlBar : ( ) => {
429567 useMicrophone ( ) ;
0 commit comments