我正在将16位和48 kHz的单声道PCM Wave文件转换为16bit和8 kHz的单声道签名线性PCM AU文件,适用于使用javax.sound.sampled的电话:
public void convertSO(final String in, final String out) throws Exception {
try (final AudioInputStream ais = AudioSystem.getAudioInputStream(new File(in))) {
final AudioFormat af = new AudioFormat(Encoding.PCM_SIGNED, 8000f, 16, 1, 2, 8000, false);
try (final AudioInputStream cais = AudioSystem.getAudioInputStream(af, ais)) {
AudioSystem.write(cais, AudioFileFormat.Type.AU, new File(out));
}
}
}
它的工作方式与此类似,听起来不错,但如果我将质量与使用SoX进行的类似转换进行比较
sox in.wav -b 16 -r 8000 -c 1 -e signed-integer out.au
使用javax.sound.sampled完成转换的高频范围听起来有些沙哑,而使用SoX听起来非常流畅。
两个输出文件具有相同的大小,并且将它们的属性与mediainfo
进行比较没有区别。
我主要想知道差异可能来自哪里。将采样率从48 kHz转换为8 kHz是否可以使SoX做得更好?或者SoX是否应用了一些花式滤镜或心理声学模型来改善音质?
答案 0 :(得分:0)
我不接受我自己的答案作为解决方案,因为它不是解决方案,但对于任何有兴趣的人,我都尝试在转换之前应用phrogz low pass filter:
public void convertSO(final String in, final String out) throws Exception {
try (final AudioInputStream ais = AudioSystem.getAudioInputStream(new File(in))) {
final int frameSize = ais.getFormat().getFrameSize();
if (frameSize != 2) {
throw new Exception("Works only with frame size == 2");
}
final int smoothing = 10;
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final byte[] buf = new byte[frameSize];
ais.read(buf, 0, buf.length);
int value = (buf[1] << 8) | (buf[0] & 0xff);
while (ais.read(buf, 0, buf.length) != -1) {
final int currentValue = (buf[1] << 8) | (buf[0] & 0xff);
value += (currentValue - value) / smoothing;
final byte[] smoothed = {(byte)(value & 0xff), (byte)(value >> 8)};
baos.write(smoothed);
}
final AudioInputStream smoothedAis = new AudioInputStream(new ByteArrayInputStream(
baos.toByteArray()), ais.getFormat(), baos.size() / frameSize);
final AudioFormat af = new AudioFormat(Encoding.PCM_SIGNED, 8000f, 16, 1, frameSize, 8000, false);
try (final AudioInputStream cais = AudioSystem.getAudioInputStream(af, smoothedAis)) {
AudioSystem.write(cais, AudioFileFormat.Type.AU, new File(out));
}
}
}
对我来说这并不容易,因为AudioInputStream
只允许您读取至少frameSize
个字节,也许我每次迭代平滑两个字节所做的工作都是笨拙的,但它的工作原理和频率很高范围消失了。
结果确实没有&#34;刮擦&#34;但与SoX完成的转换相比,它也是闷闷不乐的,我想这是Ghostkeeper提到的结转在SoX中的技巧。
我现在不想尝试用Java实现,因为我根本就没有得到它: - /