@@ -33,6 +33,7 @@ export interface StreamConfig {
33
33
frameRate : number ;
34
34
prompt ?: any ;
35
35
selectedDeviceId : string ;
36
+ selectedAudioDeviceId : string ; // New property for audio device
36
37
}
37
38
38
39
interface VideoDevice {
@@ -45,6 +46,7 @@ export const DEFAULT_CONFIG: StreamConfig = {
45
46
process . env . NEXT_PUBLIC_DEFAULT_STREAM_URL || "http://127.0.0.1:3000" ,
46
47
frameRate : 30 ,
47
48
selectedDeviceId : "" ,
49
+ selectedAudioDeviceId : "" , // Default value for audio device
48
50
} ;
49
51
50
52
interface StreamSettingsProps {
@@ -110,7 +112,9 @@ interface ConfigFormProps {
110
112
function ConfigForm ( { config, onSubmit } : ConfigFormProps ) {
111
113
const [ prompt , setPrompt ] = useState < any > ( null ) ;
112
114
const [ videoDevices , setVideoDevices ] = useState < VideoDevice [ ] > ( [ ] ) ;
115
+ const [ audioDevices , setAudioDevices ] = useState < VideoDevice [ ] > ( [ ] ) ;
113
116
const [ selectedDevice , setSelectedDevice ] = useState < string > ( "" ) ;
117
+ const [ selectedAudioDevice , setSelectedAudioDevice ] = useState < string > ( "" ) ;
114
118
115
119
const form = useForm < z . infer < typeof formSchema > > ( {
116
120
resolver : zodResolver ( formSchema ) ,
@@ -138,17 +142,42 @@ function ConfigForm({ config, onSubmit }: ConfigFormProps) {
138
142
}
139
143
} , [ ] ) ;
140
144
145
+ const getAudioDevices = useCallback ( async ( ) => {
146
+ try {
147
+ const devices = await navigator . mediaDevices . enumerateDevices ( ) ;
148
+ const audioDevices = devices
149
+ . filter ( ( device ) => device . kind === "audioinput" )
150
+ . map ( ( device ) => ( {
151
+ deviceId : device . deviceId ,
152
+ label : device . label || `Microphone ${ device . deviceId . slice ( 0 , 5 ) } ...` ,
153
+ } ) ) ;
154
+
155
+ setAudioDevices ( audioDevices ) ;
156
+ if ( audioDevices . length > 0 ) {
157
+ setSelectedAudioDevice ( ( curr ) => curr || audioDevices [ 0 ] . deviceId ) ;
158
+ }
159
+ } catch ( err ) {
160
+ console . error ( "Failed to get audio devices" ) ;
161
+ }
162
+ } , [ ] ) ;
163
+
141
164
useEffect ( ( ) => {
142
165
getVideoDevices ( ) ;
166
+ getAudioDevices ( ) ;
143
167
navigator . mediaDevices . addEventListener ( "devicechange" , getVideoDevices ) ;
168
+ navigator . mediaDevices . addEventListener ( "devicechange" , getAudioDevices ) ;
144
169
145
170
return ( ) => {
146
171
navigator . mediaDevices . removeEventListener (
147
172
"devicechange" ,
148
173
getVideoDevices
149
174
) ;
175
+ navigator . mediaDevices . removeEventListener (
176
+ "devicechange" ,
177
+ getAudioDevices
178
+ ) ;
150
179
} ;
151
- } , [ getVideoDevices ] ) ;
180
+ } , [ getVideoDevices , getAudioDevices ] ) ;
152
181
153
182
const handleSubmit = ( values : z . infer < typeof formSchema > ) => {
154
183
onSubmit ( {
@@ -158,6 +187,7 @@ function ConfigForm({ config, onSubmit }: ConfigFormProps) {
158
187
: values . streamUrl ,
159
188
prompt,
160
189
selectedDeviceId : selectedDevice ,
190
+ selectedAudioDeviceId : selectedAudioDevice ,
161
191
} ) ;
162
192
} ;
163
193
@@ -221,6 +251,23 @@ function ConfigForm({ config, onSubmit }: ConfigFormProps) {
221
251
</ Select >
222
252
</ div >
223
253
254
+ < div className = "mt-4 mb-4" >
255
+ < Label > Microphone</ Label >
256
+ < Select value = { selectedAudioDevice } onValueChange = { setSelectedAudioDevice } >
257
+ < Select . Trigger className = "w-full mt-2" >
258
+ { audioDevices . find ( ( d ) => d . deviceId === selectedAudioDevice ) ?. label ||
259
+ "Select microphone" }
260
+ </ Select . Trigger >
261
+ < Select . Content >
262
+ { audioDevices . map ( ( device ) => (
263
+ < Select . Option key = { device . deviceId } value = { device . deviceId } >
264
+ { device . label }
265
+ </ Select . Option >
266
+ ) ) }
267
+ </ Select . Content >
268
+ </ Select >
269
+ </ div >
270
+
224
271
< div className = "mt-4 mb-4 grid max-w-sm items-center gap-3" >
225
272
< Label > Comfy Workflow</ Label >
226
273
< Input
0 commit comments