You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

i386系统下无库实现Sound Blaster 16指定频率声音播放方案

在i386架构下无依赖实现Sound Blaster 16指定频率发声(C语言)

核心问题分析与修正方案

你当前的代码已经具备SB16初始化和基础数据发送的框架,但在波形生成、时序控制和播放逻辑上存在需要调整的地方,以下是优化后的实现:


优化后的完整代码

#include "defs.h"
#include "sound/audio_card.h"
#include "asm.h"

// SB16端口定义
#define SB16_DSP_RESET     0x226
#define SB16_DSP_READ      0x22A
#define SB16_DSP_WRITE     0x22C
#define SB16_DSP_COMMAND   0x22C
#define SB16_DSP_BUFFER    0x22E
#define M_PI               3.1415926535

// 微秒级延时函数(适配i386架构)
static void delay_us(uint32_t us) {
    __asm__ __volatile__ (
        "1: dec %0\n"
        "jne 1b\n"
        : "=r"(us)
        : "0"(us * 3) // 根据CPU主频调整系数,适配QEMU中的i386环境
    );
}

// SB16复位与初始化
void sb16_reset() {
    outb(1, SB16_DSP_RESET);  // 触发复位
    delay_us(100);            // 硬件复位最小延时(无需1秒)
    outb(0, SB16_DSP_RESET);  // 退出复位模式
    
    // 等待DSP响应
    while (!(inb(SB16_DSP_READ) & 0x80));
    if (inb(SB16_DSP_READ) != 0xAA) {
        // 初始化失败处理,可根据需求添加 panic 或日志输出
        for(;;); // 死循环标记初始化失败
    }
}

// 向DSP发送命令
void sb16_send_command(uint8_t cmd) {
    while (inb(SB16_DSP_WRITE) & 0x80);  // 等待写入就绪
    outb(cmd, SB16_DSP_COMMAND);
}

// 设置采样率
void sb16_set_sample_rate(uint16_t hz) {
    sb16_send_command(0x40);              // 发送设置采样率命令
    outb((hz >> 8) & 0xFF, SB16_DSP_WRITE); // 发送高字节
    outb(hz & 0xFF, SB16_DSP_WRITE);       // 发送低字节
}

// 向DSP发送音频数据
void sb16_write_data(uint8_t data) {
    while (inb(SB16_DSP_WRITE) & 0x80);  // 等待缓冲区就绪
    outb(data, SB16_DSP_BUFFER);
}

// 生成指定频率的正弦波并播放
void sb16_play_sine_wave(uint16_t freq, uint32_t duration_ms) {
    sb16_reset();
    sb16_set_sample_rate(44100); // 使用标准44100Hz采样率保证音质
    
    // 启动8位单声道播放模式
    sb16_send_command(0xC6);
    
    uint32_t total_samples = (44100 * duration_ms) / 1000;
    double step = (2 * M_PI * freq) / 44100;
    double angle = 0.0;
    
    for (uint32_t i = 0; i < total_samples; i++) {
        // 转换为8位无符号音频数据(范围0-255,中心值128)
        uint8_t sample = (uint8_t)(sin(angle) * 127 + 128);
        sb16_write_data(sample);
        angle += step;
        if (angle >= 2 * M_PI) {
            angle -= 2 * M_PI;
        }
    }
    
    // 停止播放
    sb16_send_command(0xD0);
}

// 生成指定频率的方波并播放
void sb16_play_square_wave(uint16_t freq, uint32_t duration_ms) {
    sb16_reset();
    sb16_set_sample_rate(44100);
    
    sb16_send_command(0xC6);
    
    uint32_t samples_per_cycle = 44100 / freq;
    uint32_t total_samples = (44100 * duration_ms) / 1000;
    uint32_t half_cycle = samples_per_cycle / 2;
    
    for (uint32_t i = 0; i < total_samples; i++) {
        uint8_t sample = (i % samples_per_cycle) < half_cycle ? 255 : 0;
        sb16_write_data(sample);
    }
    
    sb16_send_command(0xD0);
}

关键优化点说明

  • 延时函数优化:替换原长延时为微秒级汇编延时,适配i386硬件时序,避免初始化超时。
  • 波形生成逻辑:
    • 正弦波:基于标准采样率计算每个采样点的正弦值,转换为符合8位音频格式的数据,保证声音平滑。
    • 方波:通过计算周期内高低电平的采样数量,生成标准方波信号,精准对应指定频率。
  • 播放时序修正:移除原代码中错误的循环延时,由SB16的采样率控制播放速度,避免声音卡顿或变速。
  • 初始化可靠性:缩短复位延时至硬件要求的最小值,添加初始化失败的标记逻辑,方便调试。

测试注意事项

  1. QEMU运行时需启用SB16模拟,添加命令行参数:-soundhw sb16
  2. VirtualBox中需在虚拟机「音频」设置里选择Sound Blaster 16作为音频控制器
  3. 若改用AC97,需重新适配寄存器操作逻辑(AC97的初始化、数据发送流程与SB16差异较大)

内容的提问来源于stack exchange,提问作者Хоменко Александр

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:57:32