防止 Java 7/8 采样声音在播放时点击(适用于 6)

Posted

技术标签:

【中文标题】防止 Java 7/8 采样声音在播放时点击(适用于 6)【英文标题】:Preventing Java 7/8 sampled sounds from clicking when played (works in 6) 【发布时间】:2016-01-31 20:58:28 【问题描述】:

以下代码在 OSX 上的 Java 6 中运行时按预期工作(它不会发出声音 - 它会多次播放静音样本),但在 Java 7 和 8 中运行时,在每个样本播放结束时,点击出现。无法理解为什么;在 6 和 7 之间看不到任何明显的 API 变化。(这是摩尔斯电码训练计划的一部分,它也播放 dits 和 dahs.. 在每个静音间隙或样本末尾单击显然是不可接受的)

提前感谢您的建议....

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.Audiosystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineEvent.Type;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;

// Demonstration of the 'clicking sound' fault in Java 7 & 8
// Repeatedly play a short sample of silence (happens on other samples too).
public class PlaySilence 
    private static int SAMPLE_RATE = 48000;
    private static final AudioFormat FORMAT = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 
            (float) SAMPLE_RATE, 8, 1, 1, SAMPLE_RATE, false);

    public static void main(String[] args) throws LineUnavailableException, InterruptedException 
        AudioListener listener = new AudioListener();
        Clip clip = oneSecondSilentClip();
        System.out.println("Clip is a " + clip);
        clip.addLineListener(listener);

        System.out.println("Starting");
        for (int i = 0; i < 10; i++) 
            listener.reset();

            System.out.println("Playing..." + System.currentTimeMillis());
            clip.setFramePosition(0);
            clip.start(); 
            clip.drain(); // just to make sure we've finished the clip; the listener is more reliable?

            // On OSX Java 1.6.0_65, you just hear silence here as expected.
            // On OSX Java 1.8.0_25 and 1.7.0_75, at the end of each playback of the clip, there's a
            // very noticeable click.

            listener.waitUntilDone();
        
        System.out.println("Done");
        clip.removeLineListener(listener);
        clip.close();
    

    private static Clip oneSecondSilentClip() throws LineUnavailableException 
        int samples = (int) (SAMPLE_RATE * (double) 1);
        byte[] out = new byte[samples];
        Clip clip = AudioSystem.getClip();
        clip.open(FORMAT, out, 0, out.length);
        clip.setFramePosition(0);
        return clip;
    

    // Adapted from http://***.com/questions/577724/trouble-playing-wav-in-java/577926#577926
    // Thanks, McDowell.
    static class AudioListener implements LineListener 
        private boolean done = false;

        public synchronized void reset() 
            done = false;
        

        public synchronized void update(LineEvent event) 
            Type eventType = event.getType();
            System.out.println("event " + eventType);
            if (eventType == Type.STOP || eventType == Type.CLOSE) 
                done = true;
                notifyAll();
            
        

        public synchronized void waitUntilDone() throws InterruptedException 
            while (!done) 
                wait();
            
        
    


【问题讨论】:

【参考方案1】:

它不喜欢音频格式中的帧大小为 1,任何大于 1 的东西似乎都可以:

private static int FRAME_SIZE = 2;

private static final AudioFormat FORMAT = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
      SAMPLE_RATE, 8, 1, FRAME_SIZE, SAMPLE_RATE, false);

【讨论】:

感谢您发现这一点,greg-449!它工作正常(在 Java 8 上测试) - 并且完全解决了这个问题。 你能解释一下为什么这行得通吗,格雷格,从 AudioFormat 文档中,我看到:“对于像 PCM 这样的编码,一个帧由给定时间点的所有通道的样本集组成,所以帧的大小(以字节为单位)总是等于样本的大小(以字节为单位)乘以通道数。” ...而且我使用的是单声道(1 个通道)和字节.. 所以帧大小是 1 字节 * 1 个通道 = 1(这会导致点击)...亲切的问候,马特 对不起,我真的不知道。一个框架大小似乎不寻常,所以我尝试了其他东西!

以上是关于防止 Java 7/8 采样声音在播放时点击(适用于 6)的主要内容,如果未能解决你的问题,请参考以下文章

Java 声音采样器未完成获取缓冲区

点击时音频播放的Javascript延迟

在 GBDK c 中使用采样音频播放时如何运行代码?

Java中可靠的声音API,用于简单的数字样本播放

音频中采样位数,采样率,比特率的名词解释(转)

如果设备已经在播放音乐,如何防止我的应用播放声音