← 都来码 / Dolaima← 例程索引c/demo/audio_steps.c原文

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;
}