Java录音需用TargetDataLine而非SourceDataLine;精确时长控制应基于System.nanoTime()实时判断,避免Thread.sleep();缓冲区宜设为1024字节,每次read后立即检查时间,确保3秒误差≤±5ms。

Java录音怎么控制精确时长,而不是靠线程sleep硬等
Java原生SourceDataLine本身不提供“录满X秒自动停”这种高级控制,必须手动管理时间边界。靠Thread.sleep()停录音极不可靠——它只暂停当前线程,SourceDataLine.write()仍在后台缓冲、数据可能丢失或截断;更糟的是,JVM调度延迟会让实际录音时长漂移几十毫秒甚至上百毫秒。
正确做法是:用系统纳秒级时间戳驱动写入循环,在每次write()前检查是否超时。
-
System.nanoTime()比System.currentTimeMillis()精度高(微秒级),适合音频时长控制 - 录音缓冲区大小要合理(如1024字节),太大会导致单次
write()耗时波动,影响判断精度 - 务必在
write()后立即检查时间,不能写完一批再统一判断
完整可运行的定时录音代码(含异常处理和资源释放)
以下代码录制16位PCM、单声道、44.1kHz音频,持续3秒,并确保SourceDataLine干净关闭:
import javax.sound.sampled.*; import java.io.ByteArrayOutputStream; import java.util.concurrent.TimeUnit;public class TimedAudioRecorder { public static byte[] recordForSeconds(int seconds) throws LineUnavailableException { AudioFormat format = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 1, 2, 44100, false); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); if (!AudioSystem.isLineSupported(info)) { throw new IllegalArgumentException("Line not supported"); }
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info); line.open(format); line.start(); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; // 每次读取/写入大小 long startTime = System.nanoTime(); long durationNs = TimeUnit.SECONDS.toNanos(seconds); try { while (System.nanoTime() - startTime < durationNs) { int bytesRead = line.read(buffer, 0, buffer.length); if (bytesRead > 0) { out.write(buffer, 0, bytesRead); } // 注意:这里不sleep,靠read阻塞+时间判断双保险 } } finally { line.stop(); line.close(); } return out.toByteArray(); } // 使用示例 public static void main(String[] args) { try { byte[] audioData = recordForSeconds(3); System.out.println("录音完成,共 " + audioData.length + " 字节"); } catch (Exception e) { e.printStackTrace(); } }}
立即学习“Java免费学习笔记(深入)”;
为什么不用TargetDataLine?它和SourceDataLine的区别在哪
别混淆:
SourceDataLine用于**播放**(你把数据“源”给它),TargetDataLine才是用于**录音**(它把采集到的数据“目标”给你)。标题里说“SourceDataLine录音”是常见误解——Java中真正做麦克风录音的是TargetDataLine。如果你真在用
SourceDataLine并期望它录音,那代码根本不会采集任何声音,只会静音写入或抛IllegalStateException。请立刻检查:
- 是否误用了
AudioSystem.getTargetDataLine(format)? - 是否漏掉
line.start()前的line.open()? - 是否在Windows上没选对音频输入设备(需用
AudioSystem.getMixer(info)枚举)?
实际部署时最容易被忽略的三个坑
本地跑通不等于上线稳定。真实环境常因权限、设备状态、JVM参数失效:
- Linux服务器默认无音频设备,
AudioSystem.getMixer(null)返回空数组——必须确认运行环境有可用Mixer - Android或某些容器环境禁用JDK音频子系统,会直接抛
SecurityException或UnsupportedOperationException - 长时间录音(>60秒)不flush缓冲区,可能导致
OutOfMemoryError——建议改用文件流FileOutputStream替代ByteArrayOutputStream
时间精度要求高的场景(比如语音指令切分),3秒目标误差应控制在±5ms内,这意味着你得避开GC频繁时段运行,且避免在录音循环里做字符串拼接或日志打印。










