Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: enable capture mixer in thread + fix stop race + live mic level

- Enable capture switch/volume in capture thread (after PCM open, safe)
- Wait for mic_connected=0 in audio_mic_stop (thread done writing)
- Update mic_level with peak during recording for live meter
- Log peak level on recording stop for diagnostics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+45 -4
+45 -4
fedac/native/src/audio.c
··· 1055 1055 return NULL; 1056 1056 } 1057 1057 1058 + // Enable capture mixer controls NOW (after PCM open, in the capture thread) 1059 + // Must NOT be done in audio_init — that causes EIO on some HDA codecs. 1060 + { 1061 + int cnum = 0; 1062 + const char *d = audio->mic_device; 1063 + while (*d && (*d < '0' || *d > '9')) d++; 1064 + if (*d) cnum = atoi(d); 1065 + char ccard[16]; 1066 + snprintf(ccard, sizeof(ccard), "hw:%d", cnum); 1067 + snd_mixer_t *cmix = NULL; 1068 + if (snd_mixer_open(&cmix, 0) >= 0) { 1069 + snd_mixer_attach(cmix, ccard); 1070 + snd_mixer_selem_register(cmix, NULL, NULL); 1071 + snd_mixer_load(cmix); 1072 + snd_mixer_elem_t *elem; 1073 + for (elem = snd_mixer_first_elem(cmix); elem; elem = snd_mixer_elem_next(elem)) { 1074 + if (!snd_mixer_selem_is_active(elem)) continue; 1075 + if (snd_mixer_selem_has_capture_switch(elem)) { 1076 + snd_mixer_selem_set_capture_switch_all(elem, 1); 1077 + ac_log("[mic] capture switch ON: %s\n", snd_mixer_selem_get_name(elem)); 1078 + } 1079 + if (snd_mixer_selem_has_capture_volume(elem)) { 1080 + long cmin, cmax; 1081 + snd_mixer_selem_get_capture_volume_range(elem, &cmin, &cmax); 1082 + long cset = cmin + ((cmax - cmin) * 9) / 10; 1083 + snd_mixer_selem_set_capture_volume_all(elem, cset); 1084 + ac_log("[mic] capture volume %s: %ld/%ld\n", 1085 + snd_mixer_selem_get_name(elem), cset, cmax); 1086 + } 1087 + } 1088 + snd_mixer_close(cmix); 1089 + } 1090 + } 1091 + 1058 1092 audio->sample_rate = rate; 1059 1093 audio->sample_write_pos = 0; 1060 1094 audio->mic_connected = 1; 1061 1095 ac_log("[mic] recording at %u Hz, %u ch\n", rate, channels); 1062 1096 1063 1097 int16_t buf[1024 * 2]; // max stereo 1098 + float peak = 0.0f; 1064 1099 while (audio->recording && audio->sample_write_pos < audio->sample_max_len) { 1065 1100 int n = snd_pcm_readi(cap, buf, 512); 1066 1101 if (n < 0) { ··· 1075 1110 } else { 1076 1111 sample = (buf[s * 2] + buf[s * 2 + 1]) / 65536.0f; 1077 1112 } 1113 + float abs_s = fabsf(sample); 1114 + if (abs_s > peak) peak = abs_s; 1078 1115 audio->sample_buf[audio->sample_write_pos++] = sample; 1079 1116 } 1117 + audio->mic_level = peak; // update level meter live 1080 1118 } 1081 1119 1082 1120 audio->sample_len = audio->sample_write_pos; 1083 - ac_log("[mic] recorded %d samples (%.2fs)\n", 1084 - audio->sample_len, (double)audio->sample_len / audio->sample_rate); 1121 + ac_log("[mic] recorded %d samples (%.2fs) peak=%.4f\n", 1122 + audio->sample_len, (double)audio->sample_len / audio->sample_rate, peak); 1085 1123 snd_pcm_close(cap); 1086 1124 audio->mic_connected = 0; 1087 1125 audio->recording = 0; ··· 1122 1160 int audio_mic_stop(ACAudio *audio) { 1123 1161 if (!audio) return 0; 1124 1162 audio->recording = 0; 1125 - // Thread is detached and will exit on its own when it sees recording=0 1126 - usleep(50000); // 50ms for thread to finish last read 1163 + // Wait for capture thread to finish writing sample_len (ALSA defaults 1164 + // can have large periods so the last snd_pcm_readi may block ~700ms) 1165 + for (int i = 0; i < 500 && audio->mic_connected; i++) { 1166 + usleep(2000); // 2ms per iter, max 1s 1167 + } 1127 1168 ac_log("[mic] recording stopped, sample_len=%d sample_rate=%u\n", 1128 1169 audio->sample_len, audio->sample_rate); 1129 1170 return audio->sample_len;