c/demo/audio_steps.c · 7396 字节 · 下载原文
/* audio_steps.c —— **音频逐级加音的听感测试**:1 个音、2 个音……一路加到 16 个,
* audio_steps.c -- **audio step-up listening test**: 1 voice, 2 voices, ... up to 16, to find
* 看加到第几个开始"破音"。
* the count at which it starts to distort.
*
* ## 为什么要有它
* ## Why it exists
*
* 用户实测报的两条(「多个音同时发声很容易破音」「声音持续长一点容易出怪声」)都指向
* Two things the user reported from real testing ("several voices at once distort easily", "a longer sustained sound goes strange") both point at
* **混音的余量**,而"几个音开始破"是**能听出来的数**——那就把它做成一个可复现的阶梯,
* **the mix headroom**, and "how many voices until it breaks" is **a number you can hear** -- so make it a reproducible ladder
* 而不是靠描述。判据是**耳朵**(所以必须有屏幕报数,你才知道此刻在听第几级)。
* instead of a description. The criterion is your **ears** (hence the on-screen readout, so you know which level you are hearing).
*
* ## 用法
* ## How to use it
*
* 打开后自动**一级一级往上加**(每级约 1.5 秒),到 16 个音后全停、停一拍、从头再来。
* Once open it **steps up one level at a time** (about 1.5 s per level); at 16 voices it stops everything, rests one tick and starts over.
* · **点屏幕 = 暂停/继续**:停在当前这一级上,想听多久听多久(这是最有用的一条)。
* · **Tapping the screen = pause/resume**: it stays on the current level so you can listen for as long as you like (the most useful one).
* · 屏幕正中报**此刻在响的声部数**(宿主 `ui_tone_get_voices()` 报的,不是猜的)。
* · The middle of the screen reports **how many voices are sounding right now** (what the host's `ui_tone_get_voices()` says, not a guess).
*
* ## 听什么
* ## What to listen for
*
* 用**正弦**(`ui_tone_set_wave(ch, 0)`)—— 纯音被削顶时冒出来的是**奇次谐波**,
* Use **sine** (`ui_tone_set_wave(ch, 0)`) -- what a clipped pure tone produces is **odd harmonics**,
* 听感是"发毛/发沙/带嗡声",最容易分辨;方波本来就满是谐波,反而听不出来。
* heard as "harsh / sandy / buzzy", which is the easiest to pick out; a square wave is already full of harmonics and hides it.
*
* ## 理论值(对照用)
* ## Theoretical values (for reference)
*
* 主增益 0.5、力度 100/127 ⇒ 单声部峰值 ≈ `0.39 × 32767`。
* Master gain 0.5 and velocity 100/127 => a single-voice peak of about `0.39 × 32767`.
* 旧限幅器的硬削点折合下来是 **4 个音**(`acc > 2.33`)——所以如果第 4~6 级开始发毛,
* The old limiter's hard-clip point works out to **4 voices** (`acc > 2.33`) -- so if levels 4-6 start to sound harsh,
* 那就是限幅那条;如果是**第 2 级就发毛**,那说明问题不在求和而在别处(要另查)。
* that is the limiter; if **level 2 already sounds harsh**, the problem is not in the summing but somewhere else (look into it separately).
*/
#include <stdio.h>
#include <vml_ui.h>
#define MAX_STEP 16
#define TICKS_PER_STEP 90 /* 90 拍 × 16ms ≈ 1.44 秒一级 */
/* 90 ticks x 16 ms = about 1.44 s per level */
/* C 大调叠置(跨 5 个八度):和声清楚、削减"两个音拍频"造成的干扰 */
/* C-major stacking (across 5 octaves): clear harmony, and it cuts the interference from "two-voice beating" */
int g_note[MAX_STEP];
int main(void) {
int sw; int sh; int w; int h;
int i; int step; int ticks; int paused; int holding;
int touch[3];
char buf[80];
g_note[0] = 48; g_note[1] = 52; g_note[2] = 55; g_note[3] = 60;
g_note[4] = 64; g_note[5] = 67; g_note[6] = 72; g_note[7] = 76;
g_note[8] = 79; g_note[9] = 84; g_note[10] = 88; g_note[11] = 91;
g_note[12] = 96; g_note[13] = 100; g_note[14] = 103; g_note[15] = 108;
sw = ui_scr_get_w_ex(ui_scr_get_orientation(), VML_CHROME_NONE);
sh = ui_scr_get_h_ex(ui_scr_get_orientation(), VML_CHROME_NONE);
if (sw <= 0) sw = 640;
if (sh <= 0) sh = 640;
w = sw; h = sh;
ui_win_open_ex("音频逐级加音 Audio Steps", w, h, VML_WIN_ROTATABLE, VML_WIN_NO_GAMEPAD);
/* 全部通道先设成正弦(纯音最听得清削顶) */
/* Set every channel to sine first (a pure tone makes clipping easiest to hear) */
for (i = 0; i < MAX_STEP; i = i + 1) ui_tone_set_wave(i, 0);
step = 0;
ticks = 0;
paused = 0;
holding = 0;
ui_tone_panic();
while (ui_win_is_closed() == 0) {
/* ── 点屏 = 暂停/继续(停在当前级上细听)──────────────────────── */
/* -- Tap the screen = pause/resume (stay on the current level to listen closely) -- */
if (ui_touch_read(0, touch)) {
if (touch[2] && holding == 0) { paused = (paused == 0) ? 1 : 0; holding = 1; }
if (touch[2] == 0) holding = 0;
}
if (paused == 0) {
ticks = ticks + 1;
if (ticks >= TICKS_PER_STEP) {
ticks = 0;
if (step < MAX_STEP) {
/* **加一个音**(前面的继续响)—— 这就是"逐级加" */
/* **add one voice** (the previous ones keep sounding) -- that is the "step-up" */
ui_tone_on(step, g_note[step], 100);
step = step + 1;
} else {
/* 到 16 个了:全停,歇一拍,从头再来 */
/* 16 reached: stop everything, rest one tick, start over */
ui_tone_off_all();
step = 0;
}
}
}
/* ── 屏幕上报数(**必须有**:不然你不知道此刻在听第几级)──────── */
/* -- Readout on screen (**mandatory**: otherwise you do not know which level you are hearing) -- */
ui_2d_clear(0);
ui_2d_text(12, 10, "音频逐级加音 Audio Steps", -1, 15, VML_ANCHOR_LEFT);
sprintf(buf, "第 %d 级 / 共 %d", step, MAX_STEP);
ui_2d_text(12, 40, buf, -8355712, 14, VML_ANCHOR_LEFT);
sprintf(buf, "宿主报的声部数 = %d", ui_tone_get_voices());
ui_2d_text(12, 62, buf, -8355712, 12, VML_ANCHOR_LEFT);
/* 大字:当前级数,隔着屏幕也看得见 */
/* Big text: the current level, readable across the room */
sprintf(buf, "%d", step);
ui_2d_text(w / 2 - 20, h / 3, buf, -14672448, 64, VML_ANCHOR_LEFT);
if (paused) ui_2d_text(12, 84, "[已暂停 · 点屏继续]", -256, 13, VML_ANCHOR_LEFT);
else ui_2d_text(12, 84, "点屏幕 = 暂停在这一级上细听", -8355712, 11, VML_ANCHOR_LEFT);
ui_2d_present();
ui_msg_fetch_wait(16); /* 16ms 一拍(≈60Hz),与钢琴示例同一条 */
/* one 16 ms tick (about 60 Hz), the same as in the piano example */
}
ui_tone_panic();
printf("AUDIO-STEPS-DONE\n");
return 0;
}