音频转换的质量问题

时间:2014-11-03 00:53:06

标签: java audio javasound

我正在将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是否应用了一些花式滤镜或心理声学模型来改善音质?

1 个答案:

答案 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实现,因为我根本就没有得到它: - /