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的采样率控制播放速度,避免声音卡顿或变速。
- 初始化可靠性:缩短复位延时至硬件要求的最小值,添加初始化失败的标记逻辑,方便调试。
测试注意事项
- QEMU运行时需启用SB16模拟,添加命令行参数:
-soundhw sb16 - VirtualBox中需在虚拟机「音频」设置里选择Sound Blaster 16作为音频控制器
- 若改用AC97,需重新适配寄存器操作逻辑(AC97的初始化、数据发送流程与SB16差异较大)
内容的提问来源于stack exchange,提问作者Хоменко Александр
相关产品推荐
相关产品推荐

