Clip Play + volumeControl =一个Pop

时间:2014-05-25 06:58:04

标签: java javasound

我进行了搜索和搜索,但没有找到问题的原因。

设置:我试图无限期地按顺序播放剪辑列表,直到程序的GUI告诉停止。此外,用户可以在播放剪辑时调节音量。 让我们说我有片段A,B和C.播放顺序是ABCABCABCABC .....

问题:问题是第一次剪辑A,B和C开始 - 有POP - 在GUI之后告诉停止,没有POP。

代码:在线程的run()方法中,获取剪辑的所有文件名,创建所有剪辑并将其保存在LinkedHashSet中。接下来,循环播放这些片段,为每个片段创建音量控制对象。

我的观察:对违规行进行评论(使用// TODO:UNCOMMENT ME !!!!!)使用注释行,没有POP,但音量控制已被禁用。取消注释这些行后,POP将返回。

问题:我哪里错了?我头上没有任何头发可以拉出来! :)

    private LinkedHashSet<Clip> clips = new LinkedHashSet<Clip>();
    private FloatControl volControl = null;
    .
    .
    .
    .
    @Override
    public void run() {
        List<String> fileNames = smd.getFiles();

        if ( !(numFiles.isEmpty()) ) {
            try {
                // Create all clips and save in LinkedHashSet
                //
                for (String s: fileNames) {
                    clips.add(CreateClip(s));
                }

                // Play each clip till GUI says to stop
                //
                while (smd.getStopPlayStatus()) {
                    for (Clip c: clips) {

                        // Calculate and set the clip volume to what GUI says
                        //
                        float dB = (float) (Math.log(smd.getSwarMVolume()/100f) / Math.log(10.0) * 80.0);
                        System.out.println("  vol = " + volumeLevel/100f + " || dB = " + dB);
                        Control[ ] ctls = c.getControls();
                        for (Control ctl : ctls) {
                            if (ctl.toString().toLowerCase().contains("master gain")) {
                                volControl = (FloatControl) ctl;
                                break;
                            }
                        }
//                      volControl.setValue(dB); // TODO:   UNCOMMENT ME!!!!!!!!

                        //  Play the clip
                        //
                        loop(c, 0);
                    }
                }
            } catch (ConcurrentModificationException ex) { 
            } 
        }
    }



    void loop(Clip clip, int times){
        if (times == -1) {
            clip.loop(Clip.LOOP_CONTINUOUSLY);
        } else {
            clip.loop(times);
        }
        while (clip.isRunning()) {
            float dB = (float) (Math.log(smd.getVolume()/100f) / Math.log(10.0) * 80.0);
//          volControl.setValue(dB); // TODO:   UNCOMMENT ME!!!!!!!!

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
            }
        }
        clip.drain();
    }


    private Clip CreateClip(String fileName) {
        Clip c = null;
        try {
            File file = new File(fileName);

            AudioInputStream sound = AudioSystem.getAudioInputStream(file);
            AudioFormat format = sound.getFormat();
            DataLine.Info info = new DataLine.Info(Clip.class, format);
            c = (Clip) AudioSystem.getLine(info);
            c.addLineListener(this);

            c.open(sound);

        } catch (MalformedURLException ex) {
        } catch (UnsupportedAudioFileException ex) {
        } catch (IOException ex) {
        } catch (LineUnavailableException ex) {
        } 
        return c;
    }

1 个答案:

答案 0 :(得分:1)

提供的音量控制非常粗糙。设置新卷时,整个更改会立即发生。这可能会在声音中产生不连续,导致弹出。

为了消除这种情况,可以尝试逐渐改变音量。有时这很有效。您将不得不修补它以找到转换和平滑所需时间的最佳权衡。

有几个问题。 (1)根据使用的缩放比例,可以在没有弹出的单个音量变化中行进的“距离”在低端与高端可以不同。 (2)在一段时间内可以进行的更改次数受缓冲区大小的限制。每个缓冲区只能进行一次更改。因此,减小缓冲区大小将允许更大的粒度变化,但会增加辍学的风险。

坦率地说,我只是举起手来放弃使用提供的音量线。相反,我将更改编程为基于每帧的转换。这可以通过获取每个缓冲区并将字节转换为PCM,对各个帧进行体积倍增并转换回字节来完成。处理成本非常小。