The problem is converting short to byte . The byte conversion link saves all information, including the upper and lower parts of the byte . When you convert from 16 bits to 8 bits PCM , you must discard the low byte. My Java skills are weak, so the following may not work verbatim. See also: short conversion to bytes.
ByteBuffer byteBuf = ByteBuffer.allocate(N); while (N >= i) { byte b = (byte)(buffer[i]&0xff); byteBuf.put(b); i++; }
This is the next conversion,
AAAA AAAA SBBB BBBB -> AAAA AAAA, +1 if S==1 and positive else -1 if S==1
A is the bit that is saved. B is the discarded bit, and S is the bit that you can use for rounding. Rounding is not required, but may seem a little better. Basically, 16 bit PCM has a higher resolution than 8 bit PCM. You lose these bits when the conversion is done. The short to byte routine tries to save all the information.
Of course, you must specify the sound library that you use 8-bit PCM . My suggestion,
at = new AudioTrack(AudioManager.STREAM_MUSIC, 22050, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_8BIT, 10000 , AudioTrack.MODE_STREAM);
If you can use 16bit PCM to play sound, you need to invert and convert 8bit PCM from the library to 16bit PCM for playback. Also note that usually 8bit samples are often NOT direct PCMs, but u-law or a-law is encoded. If a third-party 3 rd library uses these formats, the conversion is different, but you should be able to encode it from links in wikipedia.
NOTE. I did not include the rounding code as overflow and sign handling will complicate the response. You should check overflow (Ie, 0x8f + 1 gives 0xff or 255 + 1, giving -1). However, I suspect the library is not a direct 8bit PCM .
See also: Alsa PCM Review, PCM Multimedia Recording Wiki - Ultimately, Android uses ALSA for sound.
Other factors that must be correct for the raw PCM buffer are the sampling rate, the number of channels (stereo / mono), the PCM format, including bits, companding, small / large endian, and pattern rotation. Strike>
EDIT: After some research, the JLayer decoder usually returns a big endian 16 bits. The sound filter accepts byte , but threatens them like a 16bit little endian from below. Finally, the AudioTrack class expects 16 bits of little endian below it. I believe that for some reason, the JLayer mp3 decoder will return 16 bits of little endian . The decode() method in the question performs a byte replacement of 16-bit values. In addition, the placed sound sounds as if the bytes are reversed.
public byte[] decode(InputStream inputStream, int startMs, int maxMs, bool swap) throws IOException { ... short[] pcm = output.getBuffer(); for (short s : pcm) { if(swap) { outStream.write(s & 0xff); outStream.write((s >> 8) & 0xff); } else { outStream.write((s >> 8) & 0xff); outStream.write(s & 0xff); } } ...
For 44k mp3, you call the routine with swap = true; . For 22k mp3 swap = false . This explains all reported phenomena. I donβt know why the JLayer mp3 decoder sometimes outputs big endian and other little endian times. I think it depends on the original mp3, not the sample rate.