【问题标题】:How to clear the `TargetDatatLine` read buffer如何清除“TargetDatatLine”读取缓冲区
【发布时间】:2016-03-05 22:25:24
【问题描述】:

所以我发现了大量代码here 可以满足我的需要。但是,我确实通过Jconsole 注意到它只是保持记录和记录数据而不是释放它。显然没有line.close() 电话。这是为了确定数据是否可以传递的问题(它必须足够响亮),线路必须不断记录,因此关闭它不会让这种决定发生。有没有办法在没有line.close() 的情况下释放line.read() 占用的所有内存?我注意到我能够从内存选项卡中的Jconsole 执行 GC,但它以大约 System.gc() 的方式调用。我知道这是你不应该调用的东西,但它似乎能够清除缓冲区正在使用的内存,所以line.close() 似乎不是唯一的方法。

这是链接中的代码,除了我删除了 GUI 元素,并添加了我认为应该执行 GC 的地方。

import javax.swing.SwingUtilities;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Control.Type;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.DataLine.Info;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;


public class LevelMeter {

    private float amp = 0f;
    private float peak = 0f;

    public void setAmplitude(float amp) {
        this.amp = Math.abs(amp);
    }

    public void setPeak(float peak) {
        this.peak = Math.abs(peak);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                LevelMeter meter = new LevelMeter();
                new Thread(new Recorder(meter)).start();
            }
        });
    }

    static class Recorder implements Runnable {
        final LevelMeter meter;

        Recorder(final LevelMeter meter) {
            this.meter = meter;
        }

        @Override
        public void run() {
            AudioFormat fmt = new AudioFormat(44100f, 16, 1, true, false);
            final DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt, 1);
            final SourceDataLine soundLine = (SourceDataLine) AudioSystem.getLine(info);
            final int bufferByteSize = 2048;


            TargetDataLine line;
            try {
                line = AudioSystem.getTargetDataLine(fmt);
                line.open(fmt, bufferByteSize);
                soundLine.open(fmt);
            } catch(LineUnavailableException e) {
                System.err.println(e);
                return;
            }

            byte[] buf = new byte[bufferByteSize];
            float[] samples = new float[bufferByteSize / 2];

            float lastPeak = 0f;

            line.start();
            for(int b; (b = line.read(buf, 0, buf.length)) > -1;) {

                // convert bytes to samples here
                for(int i = 0, s = 0; i < b;) {
                    int sample = 0;

                    sample |= buf[i++] & 0xFF; // (reverse these two lines
                    sample |= buf[i++] << 8;   //  if the format is big endian)

                    // normalize to range of +/-1.0f
                    samples[s++] = sample / 32768f;
                }

                float rms = 0f;
                float peak = 0f;
                for(float sample : samples) {

                    float abs = Math.abs(sample);
                    if(abs > peak) {
                        peak = abs;
                    }

                    rms += sample * sample;
                }

                rms = (float)Math.sqrt(rms / samples.length);

                if(lastPeak > peak) {
                    peak = lastPeak * 0.875f;
                }

                if(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory() > 7000000){
                    System.gc();
                    System.gc();
                }

                if(peak > 0.20){
                    sourceLine.start();


                }

                lastPeak = peak;
                System.out.println(peak);
                setMeterOnEDT(rms, peak);
            }
        }

        void setMeterOnEDT(final float rms, final float peak) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    meter.setAmplitude(rms);
                    meter.setPeak(peak);

                }
            });
        }
    }
}

编辑:不,我意识到这是因为我没有关闭线路是没有释放内存的原因。问题是我无法关闭线路,因为我只需要抓取表明某人实际上正在对着麦克风讲话的字节。如果我关闭线路,那么它实际上会静音,或者至少这是我的理解。但是,我在上面更新的代码中设置了一种不时擦除缓冲区的肮脏方式。我目前正在尝试获取字节并在它们高于某个阈值时播放它们。我可以从另一行正在读入的缓冲区中写出吗?还是我必须设置第二个缓冲区才能写出?

【问题讨论】:

  • 音频代码通常大部分时间都处于阻塞状态,允许其他线程运行。除非有特定问题,否则在您继续监视时让循环在自己的线程中运行可能是可以的。
  • 这看起来就像Java内存系统的正常运行。垃圾收集器会在必要时自动运行,无需调用。

标签: java garbage-collection javasound


【解决方案1】:

您看到的内存增加很可能是垃圾收集器目前没有在您的程序的时间范围内运行的结果。我怀疑如果你运行你的程序足够长的时间来用完新一代的空间,你会发现大部分内存又变得可用了。

这似乎得到了TargetDataLine 类的Javadoc 的支持。这个类是缓冲的,这意味着它只消耗固定数量的内存,直到行关闭才能回收,一旦你的程序不再需要从行中读取,你应该这样做。所以我怀疑堆大小的增加只是来自读取行时的瞬态对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 2013-08-10
    • 1970-01-01
    相关资源
    最近更新 更多