Skip to content

Commit f558733

Browse files
AudioGridVisualizer
1 parent a5c272f commit f558733

File tree

10 files changed

+1478
-5
lines changed

10 files changed

+1478
-5
lines changed

app/ui/_components.tsx

Lines changed: 126 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
import { useEffect, useMemo, useState } from 'react';
44
import { type VariantProps } from 'class-variance-authority';
55
import { Track } from 'livekit-client';
6+
import { RoomAudioRenderer, StartAudio } from '@livekit/components-react';
67
import {
78
type AgentState,
89
type TrackReference,
910
type TrackReferenceOrPlaceholder,
1011
useLocalParticipant,
12+
useVoiceAssistant,
1113
} from '@livekit/components-react';
1214
import { MicrophoneIcon } from '@phosphor-icons/react/dist/ssr';
1315
import { useSession } from '@/components/app/session-provider';
@@ -22,15 +24,13 @@ import {
2224
AudioBarVisualizer,
2325
audioBarVisualizerVariants,
2426
} 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';
27+
import { AudioGridVisualizer } from '@/components/livekit/audio-visualizer/audio-grid-visualizer/audio-grid-visualizer';
2928
import { gridVariants } from '@/components/livekit/audio-visualizer/audio-grid-visualizer/demos';
3029
import {
3130
AudioRadialVisualizer,
3231
audioRadialVisualizerVariants,
3332
} 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';
3434
import { Button, buttonVariants } from '@/components/livekit/button';
3535
import { ChatEntry } from '@/components/livekit/chat-entry';
3636
import {
@@ -442,11 +442,11 @@ export const COMPONENTS = {
442442
'speaking',
443443
] as AgentState[];
444444

445-
const { microphoneTrack, localParticipant } = useLocalParticipant();
446445
const [rowCount, setRowCount] = useState(rowCounts[0]);
447446
const [columnCount, setColumnCount] = useState(columnCounts[0]);
448447
const [state, setState] = useState<AgentState>(states[0]);
449448
const [demoIndex, setDemoIndex] = useState(0);
449+
const { microphoneTrack, localParticipant } = useLocalParticipant();
450450

451451
const micTrackRef = useMemo<TrackReferenceOrPlaceholder | undefined>(() => {
452452
return state === 'speaking'
@@ -562,6 +562,127 @@ export const COMPONENTS = {
562562
);
563563
},
564564

565+
AudioShaderVisualizer: () => {
566+
const [presetIndex, setPresetIndex] = useState(0);
567+
568+
// speed
569+
const [a, setA] = useState(50);
570+
// // amplitude
571+
// const [c, setC] = useState(0.5);
572+
// // frequency
573+
// const [d, setD] = useState(0.3);
574+
// // scale
575+
// const [e, setE] = useState(0.3);
576+
// blur
577+
const [f, setF] = useState(0.4);
578+
// shape
579+
const [g, setG] = useState(1.0);
580+
// const [rawColor2, setRawColor2] = useState('#0000ff');
581+
582+
const [rawColor1, setRawColor1] = useState('#00ffff');
583+
const [r1, g1, b1] = rawColor1.match(/\w\w/g)!.map((c) => parseInt(c, 16));
584+
// const [r2, g2, b2] = rawColor2.match(/\w\w/g)!.map((c) => parseInt(c, 16));
585+
586+
// const { microphoneTrack, localParticipant } = useLocalParticipant();
587+
// const micTrackRef = useMemo<TrackReferenceOrPlaceholder | undefined>(() => {
588+
// return {
589+
// participant: localParticipant,
590+
// source: Track.Source.Microphone,
591+
// publication: microphoneTrack,
592+
// } as TrackReference;
593+
// }, [localParticipant, microphoneTrack]);
594+
595+
const {
596+
// state,
597+
audioTrack,
598+
} = useVoiceAssistant();
599+
600+
useMicrophone();
601+
602+
const fields = [
603+
['speed', a, setA, 0, 250, 10],
604+
// ['amplitude', c, setC, 0, 3, 0.01],
605+
// ['frequency', d, setD],
606+
// ['scale', e, setE, 0.1, 1, 0.01],
607+
['blur', f, setF, 0, 2, 0.1],
608+
['shape', g, setG, 1, 5, 1],
609+
] as const;
610+
611+
return (
612+
<Container componentName="AudioShaderVisualizer">
613+
<StartAudio label="Start Audio" />
614+
<RoomAudioRenderer />
615+
<div className="grid grid-cols-2 gap-4">
616+
<AudioShaderVisualizer
617+
speed={a}
618+
// intensity={0}
619+
// amplitude={c}
620+
// frequency={d}
621+
// scale={e}
622+
blur={f}
623+
shape={g}
624+
colorPhase={[r1, g1, b1]}
625+
audioTrack={audioTrack}
626+
presetIndex={presetIndex}
627+
// className="bg-amber-100"
628+
/>
629+
<div>
630+
<div className="mb-4">
631+
<StoryTitle>Preset</StoryTitle>
632+
<Select
633+
value={String(presetIndex)}
634+
onValueChange={(value) => setPresetIndex(parseInt(value))}
635+
>
636+
<SelectTrigger id="presetIndex" className="w-full">
637+
<SelectValue placeholder="Select a preset" />
638+
</SelectTrigger>
639+
<SelectContent>
640+
<SelectItem value="0">Preset 1</SelectItem>
641+
<SelectItem value="1">Preset 2</SelectItem>
642+
<SelectItem value="2">Preset 3</SelectItem>
643+
<SelectItem value="3">Preset 4</SelectItem>
644+
</SelectContent>
645+
</Select>
646+
</div>
647+
648+
{fields.map(([name, value, setValue, min = 0.1, max = 10, step = 0.1]) => {
649+
// Use 0-1 range for color phase channels
650+
const isColorPhase = name.toString().startsWith('colorPhase');
651+
652+
return (
653+
<div key={name}>
654+
<div className="flex items-center justify-between">
655+
<StoryTitle>{name}</StoryTitle>
656+
<div className="text-muted-foreground mb-2 text-xs">
657+
{isColorPhase ? Number(value).toFixed(2) : String(value)}
658+
</div>
659+
</div>
660+
<input
661+
type="range"
662+
value={String(value)}
663+
min={min}
664+
max={max}
665+
step={step}
666+
onChange={(e) => setValue(parseFloat(e.target.value))}
667+
className="w-full"
668+
/>
669+
</div>
670+
);
671+
})}
672+
<div>
673+
<StoryTitle>Colors</StoryTitle>
674+
<input
675+
type="color"
676+
value={rawColor1}
677+
onChange={(e) => setRawColor1(e.target.value)}
678+
/>
679+
</div>
680+
</div>
681+
</div>
682+
</Container>
683+
);
684+
},
685+
565686
// Agent control bar
566687
AgentControlBar: () => {
567688
useMicrophone();
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
'use client';
2+
3+
import {
4+
type TrackReference,
5+
type TrackReferenceOrPlaceholder,
6+
// useMultibandTrackVolume,
7+
useTrackVolume,
8+
} from '@livekit/components-react';
9+
import { AuroraShaders, type AuroraShadersProps } from '@/components/ui/shadcn-io/aurora-shaders';
10+
import { cn } from '@/lib/utils';
11+
12+
const PRESETS = [
13+
(volume: number) => ({
14+
amplitude: 0.3,
15+
scale: 0.3 - 0.1 * (volume * 1.5),
16+
frequency: 0.25,
17+
}),
18+
(volume: number) => ({
19+
amplitude: 0.2 + 1 * volume,
20+
scale: 0.3 - 0.1 * (volume * 1.5),
21+
frequency: 0.25 + 5 * volume,
22+
}),
23+
(volume: number) => ({
24+
amplitude: 0.5 + 0.1 * volume,
25+
scale: 0.2 + 0.1 * (volume * 1.5),
26+
frequency: 5 - 6 * volume,
27+
}),
28+
(volume: number) => ({
29+
amplitude: 0.5 + 0.1 * volume,
30+
scale: 0.2 + 0.1 * (volume * 1.5),
31+
frequency: 1 - 1 * volume,
32+
}),
33+
];
34+
35+
export function AudioShaderVisualizer({
36+
speed = 1.0,
37+
intensity = 0,
38+
blur = 0.2,
39+
shape = 1,
40+
colorPosition = 0.5,
41+
colorScale = 0.5,
42+
colorPhase = [0.0, 0.8, 1.0],
43+
audioTrack,
44+
presetIndex = 0,
45+
className,
46+
}: AuroraShadersProps & { presetIndex?: number; audioTrack?: TrackReferenceOrPlaceholder }) {
47+
// const [volume] = useMultibandTrackVolume(audioTrack, {
48+
// bands: 1,
49+
// loPass: 100,
50+
// hiPass: 150,
51+
// });
52+
const volume = useTrackVolume(audioTrack as TrackReference, {
53+
fftSize: 2048,
54+
smoothingTimeConstant: 0.5,
55+
});
56+
57+
const { amplitude, scale, frequency } = PRESETS[presetIndex](volume);
58+
59+
return (
60+
<div className={cn('size-80 overflow-hidden rounded-full', className)}>
61+
<AuroraShaders
62+
speed={speed}
63+
intensity={intensity}
64+
amplitude={amplitude}
65+
frequency={frequency}
66+
scale={scale}
67+
blur={blur}
68+
shape={shape}
69+
colorPosition={colorPosition}
70+
colorScale={colorScale}
71+
colorPhase={colorPhase}
72+
// colorPhaseEnd={colorPhaseEnd}
73+
/>
74+
</div>
75+
);
76+
}
77+
78+
// import {
79+
// CosmicWavesShaders,
80+
// type CosmicWavesShadersProps,
81+
// } from '@/components/ui/shadcn-io/cosmic-waves-shaders';
82+
83+
// export function AudioShaderVisualizer({
84+
// speed = 1.0,
85+
// amplitude = 1.0,
86+
// frequency = 1.0,
87+
// starDensity = 1.0,
88+
// colorShift = 1.0,
89+
// }: CosmicWavesShadersProps) {
90+
// return (
91+
// <div className="size-40 overflow-hidden rounded-full">
92+
// <CosmicWavesShaders
93+
// speed={speed}
94+
// amplitude={amplitude}
95+
// frequency={frequency}
96+
// starDensity={starDensity}
97+
// colorShift={colorShift}
98+
// />
99+
// </div>
100+
// );
101+
// }
102+
103+
// import {
104+
// SingularityShaders,
105+
// type SingularityShadersProps,
106+
// } from '@/components/ui/shadcn-io/singularity-shaders';
107+
108+
// export function AudioShaderVisualizer({
109+
// speed = 1.0,
110+
// intensity = 1.0,
111+
// size = 1.0,
112+
// waveStrength = 1.0,
113+
// colorShift = 1.0,
114+
// }: SingularityShadersProps) {
115+
// return (
116+
// <div className="size-40 overflow-hidden rounded-full">
117+
// <SingularityShaders
118+
// speed={speed}
119+
// intensity={intensity}
120+
// size={size}
121+
// waveStrength={waveStrength}
122+
// colorShift={colorShift}
123+
// />
124+
// </div>
125+
// );
126+
// }

0 commit comments

Comments
 (0)