@@ -30,7 +30,10 @@ import {
3030 AudioRadialVisualizer ,
3131 audioRadialVisualizerVariants ,
3232} from '@/components/livekit/audio-visualizer/audio-radial-visualizer/audio-radial-visualizer' ;
33- import { AudioShaderVisualizer } from '@/components/livekit/audio-visualizer/audio-shader-visualizer/audio-shader-visualizer' ;
33+ import {
34+ AudioShaderVisualizer ,
35+ audioShaderVisualizerVariants ,
36+ } from '@/components/livekit/audio-visualizer/audio-shader-visualizer/audio-shader-visualizer' ;
3437import { Button , buttonVariants } from '@/components/livekit/button' ;
3538import { ChatEntry } from '@/components/livekit/chat-entry' ;
3639import {
@@ -52,6 +55,9 @@ type audioBarVisualizerVariantsSizeType = VariantProps<typeof audioBarVisualizer
5255type audioRadialVisualizerVariantsSizeType = VariantProps <
5356 typeof audioRadialVisualizerVariants
5457> [ 'size' ] ;
58+ type audioShaderVisualizerVariantsSizeType = VariantProps <
59+ typeof audioShaderVisualizerVariants
60+ > [ 'size' ] ;
5561
5662export function useMicrophone ( ) {
5763 const { startSession } = useSession ( ) ;
@@ -563,93 +569,166 @@ export const COMPONENTS = {
563569 } ,
564570
565571 AudioShaderVisualizer : ( ) => {
572+ const { startSession, endSession } = useSession ( ) ;
573+ const { localParticipant } = useLocalParticipant ( ) ;
566574 const [ presetIndex , setPresetIndex ] = useState ( 3 ) ;
567575
568- // speed
569- const [ a , setA ] = useState ( 50 ) ;
570- // // color scale
571- const [ h , setH ] = useState ( 0.1 ) ;
572- // // color position
573- const [ i , setI ] = useState ( 0.15 ) ;
574- // blur
575- const [ f , setF ] = useState ( 0.1 ) ;
576576 // shape
577- const [ g , setG ] = useState ( 1.0 ) ;
577+ const [ shape , setShape ] = useState ( 1.0 ) ;
578+ // color scale
579+ const [ colorScale , setColorScale ] = useState ( 0.1 ) ;
580+ // color position
581+ const [ colorPosition , setColorPosition ] = useState ( 0.15 ) ;
582+
583+ const sizes = [ 'icon' , 'sm' , 'md' , 'lg' , 'xl' ] ;
584+ const states = [
585+ 'disconnected' ,
586+ 'connecting' ,
587+ 'initializing' ,
588+ 'listening' ,
589+ 'thinking' ,
590+ 'speaking' ,
591+ ] as AgentState [ ] ;
592+
593+ const [ size , setSize ] = useState < audioShaderVisualizerVariantsSizeType > ( 'lg' ) ;
594+ const [ state , setState ] = useState < AgentState > ( states [ 0 ] ) ;
578595
579596 const {
580597 // state,
581598 audioTrack,
582599 } = useVoiceAssistant ( ) ;
583600
584- useMicrophone ( ) ;
601+ useEffect ( ( ) => {
602+ if ( state === 'speaking' ) {
603+ startSession ( ) ;
604+ localParticipant . setMicrophoneEnabled ( true , undefined ) ;
605+ } else {
606+ endSession ( ) ;
607+ localParticipant . setMicrophoneEnabled ( false , undefined ) ;
608+ }
609+ } , [ startSession , endSession , state , localParticipant ] ) ;
585610
586611 const fields = [
587- [ 'speed' , a , setA , 0 , 250 , 10 ] ,
588- [ 'color position' , i , setI , 0 , 1 , 0.01 ] ,
589- [ 'color scale' , h , setH , 0 , 1 , 0.01 ] ,
590- [ 'blur' , f , setF , 0 , 2 , 0.01 ] ,
591- [ 'shape' , g , setG , 1 , 5 , 1 ] ,
612+ [ 'color position' , colorPosition , setColorPosition , 0 , 1 , 0.01 ] ,
613+ [ 'color scale' , colorScale , setColorScale , 0 , 1 , 0.01 ] ,
592614 ] as const ;
593615
594616 return (
595617 < Container componentName = "AudioShaderVisualizer" >
596618 < StartAudio label = "Start Audio" />
597619 < RoomAudioRenderer />
598- < div className = "grid grid-cols-2 gap-4" >
620+
621+ < div className = "flex gap-4" >
622+ < div className = "flex-1" >
623+ < label className = "font-mono text-xs uppercase" htmlFor = "state" >
624+ State
625+ </ label >
626+ < Select value = { state } onValueChange = { ( value ) => setState ( value as AgentState ) } >
627+ < SelectTrigger id = "state" className = "w-full" >
628+ < SelectValue placeholder = "Select a state" />
629+ </ SelectTrigger >
630+ < SelectContent >
631+ { states . map ( ( state ) => (
632+ < SelectItem key = { state } value = { state } >
633+ { state }
634+ </ SelectItem >
635+ ) ) }
636+ </ SelectContent >
637+ </ Select >
638+ </ div >
639+
640+ < div className = "flex-1" >
641+ < label className = "font-mono text-xs uppercase" htmlFor = "size" >
642+ Size
643+ </ label >
644+ < Select
645+ value = { size as string }
646+ onValueChange = { ( value ) => setSize ( value as audioShaderVisualizerVariantsSizeType ) }
647+ >
648+ < SelectTrigger id = "size" className = "w-full" >
649+ < SelectValue placeholder = "Select a size" />
650+ </ SelectTrigger >
651+ < SelectContent >
652+ { sizes . map ( ( size ) => (
653+ < SelectItem key = { size } value = { size as string } >
654+ { size . toUpperCase ( ) }
655+ </ SelectItem >
656+ ) ) }
657+ </ SelectContent >
658+ </ Select >
659+ </ div >
660+
661+ < div className = "flex-1" >
662+ < StoryTitle > Preset</ StoryTitle >
663+ < Select
664+ value = { String ( presetIndex ) }
665+ onValueChange = { ( value ) => setPresetIndex ( parseInt ( value ) ) }
666+ >
667+ < SelectTrigger id = "presetIndex" className = "w-full" >
668+ < SelectValue placeholder = "Select a preset" />
669+ </ SelectTrigger >
670+ < SelectContent >
671+ < SelectItem value = "0" > Preset 1</ SelectItem >
672+ < SelectItem value = "1" > Preset 2</ SelectItem >
673+ < SelectItem value = "2" > Preset 3</ SelectItem >
674+ < SelectItem value = "3" > Preset 4</ SelectItem >
675+ </ SelectContent >
676+ </ Select >
677+ </ div >
678+
679+ < div className = "flex-1" >
680+ < label className = "font-mono text-xs uppercase" htmlFor = "shape" >
681+ Shape
682+ </ label >
683+ < Select value = { shape . toString ( ) } onValueChange = { ( value ) => setShape ( parseInt ( value ) ) } >
684+ < SelectTrigger id = "shape" className = "w-full" >
685+ < SelectValue placeholder = "Select a shape" />
686+ </ SelectTrigger >
687+ < SelectContent >
688+ < SelectItem value = "1" > Circle</ SelectItem >
689+ < SelectItem value = "2" > Line</ SelectItem >
690+ </ SelectContent >
691+ </ Select >
692+ </ div >
693+ </ div >
694+
695+ < div className = "py-12" >
599696 < AudioShaderVisualizer
600- speed = { a }
601- blur = { f }
602- shape = { g }
603- colorScale = { h }
604- colorPosition = { i }
605- audioTrack = { audioTrack }
606- presetIndex = { presetIndex }
607- // className="bg-amber-100"
697+ size = { size }
698+ state = { state }
699+ shape = { shape }
700+ colorScale = { colorScale }
701+ colorPosition = { colorPosition }
702+ audioTrack = { audioTrack as TrackReferenceOrPlaceholder }
703+ className = "mx-auto bg-black"
608704 />
609- < div >
610- < div className = "mb-4" >
611- < StoryTitle > Preset</ StoryTitle >
612- < Select
613- value = { String ( presetIndex ) }
614- onValueChange = { ( value ) => setPresetIndex ( parseInt ( value ) ) }
615- >
616- < SelectTrigger id = "presetIndex" className = "w-full" >
617- < SelectValue placeholder = "Select a preset" />
618- </ SelectTrigger >
619- < SelectContent >
620- < SelectItem value = "0" > Preset 1</ SelectItem >
621- < SelectItem value = "1" > Preset 2</ SelectItem >
622- < SelectItem value = "2" > Preset 3</ SelectItem >
623- < SelectItem value = "3" > Preset 4</ SelectItem >
624- </ SelectContent >
625- </ Select >
626- </ div >
705+ </ div >
627706
628- { fields . map ( ( [ name , value , setValue , min = 0.1 , max = 10 , step = 0.1 ] ) => {
629- // Use 0-1 range for color phase channels
630- const isColorPhase = name . toString ( ) . startsWith ( 'colorPhase' ) ;
631-
632- return (
633- < div key = { name } >
634- < div className = "flex items-center justify-between" >
635- < StoryTitle > { name } </ StoryTitle >
636- < div className = "text-muted-foreground mb-2 text-xs" >
637- { isColorPhase ? Number ( value ) . toFixed ( 2 ) : String ( value ) }
638- </ div >
707+ < div className = "grid grid-cols-2 gap-4" >
708+ { fields . map ( ( [ name , value , setValue , min = 0.1 , max = 10 , step = 0.1 ] ) => {
709+ // Use 0-1 range for color phase channels
710+ const isColorPhase = name . toString ( ) . startsWith ( 'colorPhase' ) ;
711+
712+ return (
713+ < div key = { name } >
714+ < div className = "flex items-center justify-between" >
715+ < StoryTitle > { name } </ StoryTitle >
716+ < div className = "text-muted-foreground mb-2 text-xs" >
717+ { isColorPhase ? Number ( value ) . toFixed ( 2 ) : String ( value ) }
639718 </ div >
640- < input
641- type = "range"
642- value = { String ( value ) }
643- min = { min }
644- max = { max }
645- step = { step }
646- onChange = { ( e ) => setValue ( parseFloat ( e . target . value ) ) }
647- className = "w-full"
648- />
649719 </ div >
650- ) ;
651- } ) }
652- </ div >
720+ < input
721+ type = "range"
722+ value = { String ( value ) }
723+ min = { min }
724+ max = { max }
725+ step = { step }
726+ onChange = { ( e ) => setValue ( parseFloat ( e . target . value ) ) }
727+ className = "w-full"
728+ />
729+ </ div >
730+ ) ;
731+ } ) }
653732 </ div >
654733 </ Container >
655734 ) ;
0 commit comments