小鱼塘--自说自话的地方

  • 小玩意
  • 小想法
记录自己技术和想法地方
  1. 首页
  2. nes
  3. 正文

重采集精度处理

26 3 月, 2026 5点热度 0人点赞 0条评论
内容目录

背景

采集精度不够,这个我开发nes 模拟器 处理apu时候遇到一个问题,虽然没有太多影响,耳朵基本听不出来

比喻我们重采样公式大概是:
per_sample_cycle = fcpu/sample_rate

fcpu = 对应模拟器的采集率 , NTSC 的cpu
sample_rate = 采集频率,假设采集率 44100
per_sampe_cycle = 得到多少cycle 采集一个点

计算的得到:40.5 cycle 采集一个点,但我们指令最小都是一个cycle,那么每次间隔41 cycle,那这样子就不是44100 采集频率,那么会出现误差。

解决办法

相位了累加器,本质就是放大

比喻我们1/40.5 = 我们缩小放到1,这里等价1 分成40.5,1/40.5 每份是多少,1/40.5 这个就是相位宽度,进行转换到1,那么相位宽度是1/40.5。

// NES CPU 频率 (ntsc版本)
const double CPU_FREQ = 1789773.0; 

// 目标采样率
const double SAMPLE_RATE = 44100.0;

// 相位增量:每个 CPU cycle 增加多少“采样相位”
const double PHASE_INC = SAMPLE_RATE / CPU_FREQ;
double phase_acc = 0.0;  // 相位累加器

// 在每个 CPU cycle 调用一次
void step_cpu_cycle() {
    phase_acc += PHASE_INC;

    // 当累加器 >= 1,说明该输出一个采样
    if (phase_acc >= 1.0) {
        phase_acc -= 1.0;

        // 这里生成一个采样点
        int sample = mix_apu_output();  // 例如混合 APU 各声道
        push_to_audio_buffer(sample);
    }
}

放大65536
1/40.5 ===> 放大到:65536 / 40.5

// PAL NES 核心参数
#define PAL_CPU_CLOCK 1789773  //
#define SAMPLE_RATE   44100    // 音频采样率

// 相位累加器步长 = CPU频率 * 65536 / 采样率
// 65536 = 2^16,用16位定点数运算,无浮点误差
#define PHASE_STEP    (PAL_CPU_CLOCK * 65536 / SAMPLE_RATE)

// 每个 CPU cycle 执行一次
void cpu_cycle_tick() {
    // 1. 执行你的CPU/APU逻辑
    apu_tick(); 

    // 2. 累加相位步长
    phase_accumulator += PHASE_STEP;

    // 3. 如果溢出(超过2^16),采集一次音频样本
    if (phase_accumulator >= 65536) {
        // 采集APU当前音频值
        int16_t sample = apu_get_sample();

        // 写入音频缓冲区
        audio_buffer_push(sample);

        // 减去溢出值,保留剩余相位(核心:不丢失精度)
        phase_accumulator -= 65536;
    }
}

这样子好处不用浮点运算,速度快很多,virtualnes 模拟器貌似用这个,我开始不明白为什么乘65536,主要运算速度快,以前的人开发代码对代码速度要求很高的,因为计算不够快,那么程序就跑不快,现在我们看那时候代码就觉得不理解了。

标签: 采集
最后更新:26 3 月, 2026

小鱼儿

爱研究技术,爱玩LOL

点赞
< 上一篇

COPYRIGHT © 2022 小鱼塘. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

湘ICP备18005349号