AudioTrack - 使用jlayer(java的MP3去codeR)短阵字节数组失真数组、字节、jlayer、AudioTrack

2023-09-06 01:51:42 作者:清酒暖风

我用jLayer脱code MP3数据,与此调用:

I'm using jLayer to decode MP3 data, with this call:

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

这调用返回去codeD数据,返回短[]数组。     output.getBuffer();

This call which returns the decoded data, returns an array of short[]. output.getBuffer();

当我打电话AudioTrack的write()使用该方法,它起着细如通过文件I循环:

When I call AudioTrack write() with that method, it plays fine as I loop through the file:

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

然而,当我转换短期[]数组[]数组使用任何在这个答案的方法,以字节: HTTP:/ /stackoverflow.com/a/12347176/1176436 声音失真和抖动的:

However, when I convert the short[] array to byte[] array using any of the methods in this answer: http://stackoverflow.com/a/12347176/1176436 the sound gets distorted and jittery:

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

变成了:

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

难道我做错什么,我能做些什么来解决这个问题?不幸的是我需要的PCM数据是在一个字节数组我在使用其他第三方库。该文件是22kHz的,如果该事项,这是怎么在被实例:

Am I doing anything wrong and what can I do to fix it? Unfortunately I need the pcm data to be in a byte array for another 3rd party library I'm using. The file is 22kHz if that matters and this is how at is being instantiated:

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。

This is how I'm instantiating the AudioTrack variable now. So for 44kHz files, the value that is getting sent is 44100, while for 22kHz files, the value is 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);

这是德code方法:

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)

This is how it sounds (wait a few seconds): https://vimeo.com/60951237 (and this is the actual file: http://www.tonycuffe.com/mp3/tail%20toddle.mp3)

编辑:我曾经爱过的人已经分手了赏金,而是我已经给赏金比尔和接受的答案尼尔。两人都是一个巨大的帮助。对于那些想知道,我结束了重写索尼克本土code这让我向前走的过程。

I would have loved to have split the bounty, but instead I have given the bounty to Bill and the accepted answer to Neil. Both were a tremendous help. For those wondering, I ended up rewriting the Sonic native code which helped me move along the process.

推荐答案

由于@Bill Pringlemeir说,问题是,您的转换方法实际上并不转换。短的是一个16位的号码;一个字节是一个8位的数字。已选择不转换短裤的内容物的方法(即从16位到8位的内容),它改变其中相同的位的集合被存储的方式。正如你所说,你需要的是这样的:

As @Bill Pringlemeir says, the problem is that your conversion method doesn't actually convert. A short is a 16 bit number; a byte is an 8 bit number. The method you have chosen doesn't convert the contents of the shorts (ie go from 16 bits to 8 bits for the contents), it changes the way in which the same collection of bits is stored. As you say, you need something like this:

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

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

@Bill Pringlemeir's approach is equivalent to dividing all the shorts by 256 to ensure they fit in the byte range:

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();
}

这将工作,但很可能会给你很安静,前卫的色调。如果你能负担得起的处理时间,两个回合方法可能会给出更好的结果:

This will work, but will probably give you very quiet, edgy tones. If you can afford the processing time, a two pass approach will probably give better results:

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();
}

再次提防符号/无符号的问题。上述工程签署─>签署和unsigned->未签名的;但不能在两者之间。这可能是你正在阅读的签署短裤(-32768-32767),但需要输出的无符号字节(0-255),...

Again, beware signed / unsigned issue. The above works signed-> signed and unsigned->unsigned; but not between the two. It may be that you are reading signed shorts (-32768-32767), but need to output unsigned bytes (0-255), ...

如果你能负担得起的处理时间,更precise(更平滑)的方法是通过花车去(这也得到一轮符号/无符号的问题):

If you can afford the processing time, a more precise (smoother) approach would be to go via floats (this also gets round the signed/unsigned issue):

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();
}
 
精彩推荐
图片推荐