【发布时间】: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