如何在Java中播放指定频率声音?高频播放异常求助
问题根源分析
你遇到的问题是**混叠(Aliasing)**导致的,核心原因在代码的两个关键参数上:
- 采样率过低:代码中
SAMPLE_RATE = 8000f,根据奈奎斯特采样定理,音频系统能准确还原的最高频率是采样率的一半,也就是4000Hz。当你尝试播放15000-30000Hz的高频时,这些信号会被折叠成低于4000Hz的低频,也就是你听到的“随机可听见音调”。 - 位深限制:8位的位深会带来较大的量化噪声,虽然不是导致混叠的直接原因,但会影响高频信号的还原精度。
另外,循环次数的计算i < msecs*8是硬编码的,只适配8000Hz采样率,换成其他采样率时会出错,需要改成基于采样率的动态计算。
修复后的代码
以下是调整后的Sound类,解决了上述问题:
import javax.sound.sampled.*; public class Sound { // 采样率设为96000Hz,足以覆盖30000Hz的高频(96000/2=48000Hz) public static float SAMPLE_RATE = 96000f; // 位深设为16位,提升音质 public static int BIT_DEPTH = 16; public static void tone(int hz, int msecs) throws LineUnavailableException { tone(hz, msecs, 1.0); } public static void tone(int hz, int msecs, double vol) throws LineUnavailableException { // 计算总样本数:采样率 * 毫秒数 / 1000 long totalSamples = (long)(SAMPLE_RATE * msecs / 1000); // 16位音频用2字节存储一个样本 byte[] buf = new byte[2]; // 配置音频格式:采样率、位深、单声道、有符号、小端字节序 AudioFormat af = new AudioFormat(SAMPLE_RATE, BIT_DEPTH, 1, true, false); SourceDataLine sdl = AudioSystem.getSourceDataLine(af); sdl.open(af); sdl.start(); for (long i=0; i < totalSamples; i++) { double angle = i / (SAMPLE_RATE / hz) * 2.0 * Math.PI; // 生成16位的正弦波样本(范围-32768到32767) short sample = (short)(Math.sin(angle) * 32767.0 * vol); // 将short转换为小端字节序的byte数组 buf[0] = (byte)(sample & 0xFF); buf[1] = (byte)((sample >> 8) & 0xFF); sdl.write(buf, 0, 2); } sdl.drain(); sdl.stop(); sdl.close(); } }
额外注意事项
- 并非所有音频设备都支持96000Hz的高采样率,如果运行报错,可以尝试将采样率降到48000Hz(同样能覆盖24000Hz的高频,接近你需要的30000Hz)。
- 人类听觉上限通常在16000-20000Hz(随年龄增长下降),所以30000Hz的声音大部分人可能根本听不到,但修复后的代码会准确生成对应频率的信号。
- 如果需要更稳定的高频播放,可以考虑使用Java的
FloatControl来调整音量,避免直接计算样本时的溢出问题。
内容的提问来源于stack exchange,提问作者Ephestion
相关产品推荐
相关产品推荐

