3939 * ---------------------------------------------------------------------- */
4040#define DEFAULT_CARD 0
4141#define DEFAULT_DEVICE 11
42- #define DEFAULT_CHANNELS 1
42+ #define DEFAULT_CHANNELS 2
4343#define DEFAULT_RATE 16000
4444#define DEFAULT_FRAGMENT_SZ 65536 /* bytes per fragment */
4545#define DEFAULT_FRAGMENTS 8
@@ -157,17 +157,56 @@ static int ctl_open(unsigned int card)
157157 return fd ;
158158}
159159
160+ static int ctl_find_numid (unsigned int card , const char * name , int default_numid )
161+ {
162+ int fd = ctl_open (card );
163+ if (fd < 0 )
164+ return default_numid ;
165+
166+ struct snd_ctl_elem_list list ;
167+ memset (& list , 0 , sizeof (list ));
168+ if (ioctl (fd , SNDRV_CTL_IOCTL_ELEM_LIST , & list ) < 0 || list .count == 0 ) {
169+ close (fd );
170+ return default_numid ;
171+ }
172+
173+ struct snd_ctl_elem_id * ids = calloc (list .count , sizeof (* ids ));
174+ if (!ids ) {
175+ close (fd );
176+ return default_numid ;
177+ }
178+ list .space = list .count ;
179+ list .pids = ids ;
180+
181+ int numid = default_numid ;
182+ if (ioctl (fd , SNDRV_CTL_IOCTL_ELEM_LIST , & list ) >= 0 ) {
183+ for (unsigned int i = 0 ; i < list .used ; i ++ ) {
184+ if (strcmp ((const char * )ids [i ].name , name ) == 0 ) {
185+ numid = ids [i ].numid ;
186+ break ;
187+ }
188+ }
189+ }
190+ free (ids );
191+ close (fd );
192+ return numid ;
193+ }
194+
160195/* Read vad_gate_status_100: returns 0 on success, fills energy + active */
161196static int vad_read_status (unsigned int card , uint32_t * energy , bool * active )
162197{
198+ static int status_numid = -1 ;
199+ if (status_numid < 0 )
200+ status_numid = ctl_find_numid (card , "vad_gate_status_100" , VAD_STATUS_NUMID );
201+
163202 /* Buffer layout (56 bytes):
164203 * [0..7] outer tlv: numid, length=48
165204 * [8..15] inner tlv: filled by kernel
166205 * [16..47] sof_abi_hdr (32 bytes)
167206 * [48..55] status payload: energy(u32) + active(u8) + pad(3)
168207 */
169208 uint32_t buf [14 ] = { 0 }; /* 56 bytes */
170- buf [0 ] = VAD_STATUS_NUMID ;
209+ buf [0 ] = status_numid ;
171210 buf [1 ] = 8 + ABI_HDR_SIZE + VAD_STATUS_PAYLOAD_SZ ; /* 48 */
172211
173212 int fd = ctl_open (card );
@@ -191,6 +230,8 @@ static int vad_read_status(unsigned int card, uint32_t *energy, bool *active)
191230/* Write vad_gate_cfg_100 */
192231static int vad_write_config (const struct wov_state * s )
193232{
233+ int cfg_numid = ctl_find_numid (s -> card , "vad_gate_cfg_100" , VAD_CFG_NUMID );
234+
194235 /* Buffer layout (60 bytes):
195236 * [0..7] outer tlv: numid=CFG_NUMID, length=52
196237 * [8..15] inner tlv: SOF_CTRL_CMD_BINARY, length=44
@@ -201,7 +242,7 @@ static int vad_write_config(const struct wov_state *s)
201242
202243 /* outer tlv */
203244 uint32_t * u32 = (uint32_t * )buf ;
204- u32 [0 ] = VAD_CFG_NUMID ;
245+ u32 [0 ] = cfg_numid ;
205246 u32 [1 ] = 8 + ABI_HDR_SIZE + VAD_CFG_PAYLOAD_SZ ; /* 52 */
206247
207248 /* inner tlv */
@@ -380,27 +421,6 @@ static enum cycle_result run_cycle(const struct wov_state *s,
380421 break ;
381422 }
382423
383- if (ret == 0 ) {
384- int n = compress_read (compress , buf , s -> frag_sz );
385- if (n > 0 ) {
386- if (!triggered ) {
387- triggered = true;
388- wav_path = wav_filename (s -> out_dir , cycle_id );
389- wav_file = wav_open (wav_path , s -> channels , s -> rate );
390- if (!wav_file ) {
391- result = CYCLE_ERROR ;
392- break ;
393- }
394- log_state ("cycle %d: TRIGGERED → writing %s" ,
395- cycle_id , wav_path );
396- result = CYCLE_OK ;
397- }
398- fwrite (buf , 1 , n , wav_file );
399- total_bytes += n ;
400- clock_gettime (CLOCK_REALTIME , & last_data_ts );
401- }
402- }
403-
404424 /* Poll VAD kcontrol */
405425 uint32_t energy = 0 ;
406426 bool active = false;
@@ -420,14 +440,43 @@ static enum cycle_result run_cycle(const struct wov_state *s,
420440 prev_active = active ;
421441 first_status = false;
422442 }
423- if (triggered && !silence_seen && !active ) {
443+ if (!triggered ) {
444+ if (s -> vad_threshold == 0 || active || energy >= (uint32_t )s -> vad_threshold ) {
445+ triggered = true;
446+ wav_path = wav_filename (s -> out_dir , cycle_id );
447+ wav_file = wav_open (wav_path , s -> channels , s -> rate );
448+ if (!wav_file ) {
449+ result = CYCLE_ERROR ;
450+ break ;
451+ }
452+ log_state ("cycle %d: TRIGGERED (%s, energy=%u) → writing %s" ,
453+ cycle_id , (s -> vad_threshold == 0 ) ? "DIRECT" : (active ? "VAD_SPEECH" : "THRESHOLD" ), energy , wav_path );
454+ result = CYCLE_OK ;
455+ }
456+ }
457+ if (triggered && s -> vad_threshold > 0 && !silence_seen && !active ) {
424458 log_state ("cycle %d: drain start (energy=%u)" ,
425459 cycle_id , energy );
426460 silence_seen = true;
427461 clock_gettime (CLOCK_REALTIME , & drain_start_ts );
428462 }
429463 }
430464
465+ if (ret == 0 ) {
466+ int n = compress_read (compress , buf , s -> frag_sz );
467+ if (n > 0 ) {
468+ if (triggered && wav_file ) {
469+ fwrite (buf , 1 , n , wav_file );
470+ total_bytes += n ;
471+ clock_gettime (CLOCK_REALTIME , & last_data_ts );
472+ if (s -> vad_threshold == 0 && total_bytes >= (uint64_t )s -> rate * s -> channels * 4 * 4 ) {
473+ log_state ("cycle %d: DIRECT_CAPTURE complete (%" PRIu64 " bytes)" , cycle_id , total_bytes );
474+ break ;
475+ }
476+ }
477+ }
478+ }
479+
431480 /* Drain completion check */
432481 if (silence_seen ) {
433482 struct timespec now ;
@@ -500,6 +549,7 @@ static void usage(const char *prog)
500549 "\n"
501550 " -c CARD sound card number (default %d)\n"
502551 " -d DEVICE compress PCM device (default %d)\n"
552+ " -C CHANNELS channels count (default %d)\n"
503553 " -n CYCLES number of WOV cycles, 0=unlimited (default 0)\n"
504554 " -o DIR output directory for WAV files (default %s)\n"
505555 " -t THRESHOLD VAD gate threshold in raw S32 energy units\n"
@@ -516,7 +566,7 @@ static void usage(const char *prog)
516566 "\n"
517567 "State log tags: STATE=pipeline transitions, CTL=kcontrol changes\n" ,
518568 prog ,
519- DEFAULT_CARD , DEFAULT_DEVICE , DEFAULT_OUT_DIR ,
569+ DEFAULT_CARD , DEFAULT_DEVICE , DEFAULT_CHANNELS , DEFAULT_OUT_DIR ,
520570 DEFAULT_RATE , DEFAULT_FRAGMENT_SZ ,
521571 prog );
522572}
@@ -541,10 +591,11 @@ int main(int argc, char *argv[])
541591 g_state = & s ;
542592
543593 int opt ;
544- while ((opt = getopt (argc , argv , "c:d:n:o:t:r:f:vh" )) != -1 ) {
594+ while ((opt = getopt (argc , argv , "c:d:C: n:o:t:r:f:vh" )) != -1 ) {
545595 switch (opt ) {
546596 case 'c' : s .card = (unsigned int )atoi (optarg ); break ;
547597 case 'd' : s .device = (unsigned int )atoi (optarg ); break ;
598+ case 'C' : s .channels = (unsigned int )atoi (optarg ); break ;
548599 case 'n' : s .max_cycles = atoi (optarg ); break ;
549600 case 'o' : s .out_dir = optarg ; break ;
550601 case 't' : s .vad_threshold = atoi (optarg ); break ;
0 commit comments