Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

fix: skip stale first chunk on rec start + add mic compressor

- Skip first snd_pcm_readi chunk after recording starts (contains
pre-buffered audio from before Home was pressed)
- Simple 4:1 compressor with fast attack / slow release to prevent
clipping and auto-level the mic input

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

+35 -2
+35 -2
fedac/native/src/audio.c
··· 1110 1110 continue; 1111 1111 } 1112 1112 1113 + // Skip first chunk after recording starts — it contains stale 1114 + // pre-buffered audio from before the user pressed rec. 1115 + int is_first_rec_chunk = (audio->recording && audio->sample_write_pos == 0); 1116 + 1113 1117 float peak = 0.0f; 1118 + // Simple compressor: track envelope, apply gain reduction 1119 + static float env = 0.0f; // envelope follower 1120 + static float comp_gain = 1.0f; // current gain 1121 + const float threshold = 0.5f; // compress above this 1122 + const float ratio = 4.0f; // 4:1 compression 1123 + const float attack = 0.002f; // fast attack 1124 + const float release = 0.0001f; // slow release 1125 + 1114 1126 for (int s = 0; s < n; s++) { 1115 1127 float sample; 1116 1128 if (channels == 1) { ··· 1118 1130 } else { 1119 1131 sample = (buf[s * 2] + buf[s * 2 + 1]) / 65536.0f; 1120 1132 } 1133 + 1134 + // Envelope follower 1121 1135 float abs_s = fabsf(sample); 1136 + if (abs_s > env) 1137 + env += attack * (abs_s - env); 1138 + else 1139 + env += release * (abs_s - env); 1140 + 1141 + // Compute gain reduction 1142 + if (env > threshold) { 1143 + float over = env - threshold; 1144 + float reduced = threshold + over / ratio; 1145 + comp_gain = reduced / env; 1146 + } else { 1147 + // Slowly return to unity 1148 + comp_gain += 0.0001f * (1.0f - comp_gain); 1149 + } 1150 + 1151 + sample *= comp_gain; 1122 1152 if (abs_s > peak) peak = abs_s; 1123 1153 1124 1154 // Always write to ring buffer 1125 1155 audio->mic_ring[audio->mic_ring_pos % audio->sample_max_len] = sample; 1126 1156 audio->mic_ring_pos++; 1127 1157 1128 - // Direct-write when recording 1129 - if (audio->recording && audio->sample_write_pos < audio->sample_max_len) { 1158 + // Direct-write when recording (skip first stale chunk) 1159 + if (audio->recording && !is_first_rec_chunk && 1160 + audio->sample_write_pos < audio->sample_max_len) { 1130 1161 audio->sample_buf[audio->sample_write_pos++] = sample; 1131 1162 } 1132 1163 } 1164 + // If we skipped the first chunk, mark that we've consumed it 1165 + // by writing at least 0 (sample_write_pos stays 0, next chunk writes) 1133 1166 audio->mic_level = peak; 1134 1167 1135 1168 if (audio->recording && audio->sample_write_pos >= audio->sample_max_len) {