gpt4 book ai didi

java - AudioTrack - 使用 jlayer(java mp3 解码器)的短数组到字节数组失真

转载 作者:塔克拉玛干 更新时间:2023-11-02 18:51:06 28 4
gpt4 key购买 nike

我正在使用 jLayer 解码 MP3 数据,调用如下:

SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);

这个返回解码数据的调用,返回一个short[]数组。 output.getBuffer();

当我使用该方法调用 AudioTrack write() 时,它在我循环遍历文件时播放良好:

at.write(output.getBuffer(), 0, output.getBuffer().length);

但是,当我使用此答案中的任何方法将 short[] 数组转换为 byte[] 数组时:https://stackoverflow.com/a/12347176/1176436声音变得失真和抖动:

at.write(output.getBuffer(), 0, output.getBuffer().length);

变成:

byte[] array = ShortToByte_Twiddle_Method(output.getBuffer());
at.write(array, 0, array.length);

我做错了什么吗?我该如何解决?不幸的是,我需要将 pcm 数据放在我正在使用的另一个第 3 方库的字节数组中。如果重要的话,文件是 22kHz,这是 at 的实例化方式:

at = new AudioTrack(AudioManager.STREAM_MUSIC, 22050, AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT, 10000 /* 10 second buffer */,
AudioTrack.MODE_STREAM);

非常感谢您。

编辑:这就是我现在实例化 AudioTrack 变量的方式。因此,对于 44kHz 的文件,发送的值为 44100,而对于 22kHz 的文件,发送的值为 22050。

at = new AudioTrack(AudioManager.STREAM_MUSIC, decoder.getOutputFrequency(), 
decoder.getOutputChannels() > 1 ? AudioFormat.CHANNEL_OUT_STEREO : AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, 10000 /* 10 second buffer */,
AudioTrack.MODE_STREAM);

这是解码方法:

public byte[] decode(InputStream inputStream, int startMs, int maxMs) throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);

float totalMs = 0;
boolean seeking = true;

try {
Bitstream bitstream = new Bitstream(inputStream);
Decoder decoder = new Decoder();

boolean done = false;
while (!done) {
Header frameHeader = bitstream.readFrame();
if (frameHeader == null) {
done = true;
} else {
totalMs += frameHeader.ms_per_frame();

if (totalMs >= startMs) {
seeking = false;
}

if (!seeking) {
// logger.debug("Handling header: " + frameHeader.layer_string());
SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);

short[] pcm = output.getBuffer();
for (short s : pcm) {
outStream.write(s & 0xff);
outStream.write((s >> 8) & 0xff);
}
}

if (totalMs >= (startMs + maxMs)) {
done = true;
}
}
bitstream.closeFrame();
}

return outStream.toByteArray();
} catch (BitstreamException e) {
throw new IOException("Bitstream error: " + e);
} catch (DecoderException e) {
throw new IOException("Decoder error: " + e);
}
}

听起来是这样的(等几秒钟):https://vimeo.com/60951237 (这是实际文件:http://www.tonycuffe.com/mp3/tail%20toddle.mp3)

编辑:我很想平分赏金,但我把赏金给了 Bill,将接受的答案给了 Neil。两者都是巨大的帮助。对于那些想知道的人,我最终重写了 Sonic native 代码,这帮助我完成了整个过程。

最佳答案

正如@Bill Pringlemeir 所说,问题是您的转换方法实际上并没有转换。 short 是一个 16 位数字;一个字节是一个 8 位数。您选择的方法不会转换短裤的内容(即内容从 16 位变为 8 位),它会改变存储相同位集合的方式。正如你所说,你需要这样的东西:

SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);
byte[] array = MyShortToByte(output.getBuffer());
at.write(array, 0, array.length);

@Bill Pringlemeir 的方法相当于将所有短裤除以 256 以确保它们适合字节范围:

byte[] MyShortToByte(short[] buffer) {
int N = buffer.length;
ByteBuffer byteBuf = ByteBuffer.allocate(N);
while (N >= i) {
byte b = (byte)(buffer[i]/256); /*convert to byte. */
byteBuf.put(b);
i++;
}
return byteBuf.array();
}

这会起作用,但可能会给您带来非常安静、尖锐的音调。如果您能负担得起处理时间,两次通过的方法可能会产生更好的结果:

byte[] MyShortToByte(short[] buffer) {
int N = buffer.length;
short min = 0;
short max = 0;
for (int i=0; i<N; i++) {
if (buffer[i] > max) max = buffer[i];
if (buffer[i] < min) min = buffer[i];
}
short scaling = 1+(max-min)/256; // 1+ ensures we stay within range and guarantee no divide by zero if sequence is pure silence ...

ByteBuffer byteBuf = ByteBuffer.allocate(N);
for (int i=0; i<N; i++) {
byte b = (byte)(buffer[i]/scaling); /*convert to byte. */
byteBuf.put(b);
}
return byteBuf.array();
}

再次提醒,注意签名/未签名问题。以上作品已签名->已签名和未签名->未签名;但不在两者之间。可能是你正在读取带符号的短裤(-32768-32767),但需要输出无符号字节(0-255),...

如果您能负担得起处理时间,则更精确(更流畅)的方法是通过 float (这也解决了签名/未签名问题):

byte[] MyShortToByte(short[] buffer) {
int N = buffer.length;
float f[] = new float[N];
float min = 0.0f;
float max = 0.0f;
for (int i=0; i<N; i++) {
f[i] = (float)(buffer[i]);
if (f[i] > max) max = f[i];
if (f[i] < min) min = f[i];
}
float scaling = 1.0f+(max-min)/256.0f; // +1 ensures we stay within range and guarantee no divide by zero if sequence is pure silence ...

ByteBuffer byteBuf = ByteBuffer.allocate(N);
for (int i=0; i<N; i++) {
byte b = (byte)(f[i]/scaling); /*convert to byte. */
byteBuf.put(b);
}
return byteBuf.array();
}

关于java - AudioTrack - 使用 jlayer(java mp3 解码器)的短数组到字节数组失真,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15124050/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com