All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vacco.libmpghip.Jmpg123 Maven / Gradle / Ivy

The newest version!
package io.vacco.libmpghip;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

/**
 * Init:
 *
 * 
 * Jmpg123 decoder = new Jmpg123();
 * decoder.InitMP3();
 * if( decoder.open( stream ) < 0 ) {
 *     decoder.ExitMP3();
 * }
 * 
* * Calling ExitMP3() do not closing the input stream. The input stream must be closed manually; */ public final class Jmpg123 extends Jmpstr_tag { public static final int MP3_ERR = -1; public static final int MP3_OK = 0; public static final int MP3_NEED_MORE = 1; static final int SBLIMIT = 32; static final int SSLIMIT = 18; // private static final int MPG_MD_STEREO = 0;// FIXME never uses static final int MPG_MD_JOINT_STEREO = 1; // private static final int MPG_MD_DUAL_CHANNEL = 2;// FIXME never uses static final int MPG_MD_MONO = 3; static final int MAXFRAMESIZE = 2880; /* AF: ADDED FOR LAYER1/LAYER2 */ static final int SCALE_BLOCK = 12; /** Pre Shift fo 16 to 8 bit converter table */ // private static final int AUSHIFT = 3;// FIXME never uses static final double M_SQRT2 = Math.sqrt(2.); // ---------------------------- // java code for easy using private static final long MAX_U_32_NUM = 0xFFFFFFFFL; private static final int ENCDELAY = 576; // private static final int FORMAT_UNKNOWN = 0; private static final int FORMAT_MP1 = 1; private static final int FORMAT_MP2 = 2; private static final int FORMAT_MP3 = 3; // private static final byte sAbl2[] = {0, 7, 7, 7, 0, 7, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8}; // output stream parameters /** true if big endian output */ private final boolean mIsBigEndian; /** true if signed output */ private final boolean mIsSigned; /** mono sample size in bytes. supporting 1, 2, 3 bytes */ private final int mBytesPerSample; // private InputStream mInputStream = null; private boolean mIsEofReached = false; private int mInputFormat = FORMAT_UNKNOWN; private byte[] mId3v2TagBuff = null; /** true if mpeg header was parsed and following data was computed */ private boolean mIsHeaderParsed = false; /** Number of channels */ private int mNumChannels = 0; /** Sample rate */ private int mSampleRate = 0; /** Bitrate */ private int mBitrate = 0; /** Number of samples in mp3 file. Computed only if mpglib detects a Xing VBR header */ private long mTotalNumSamples = 0; // private static final int OUT_SIZE = 1152 * 2; // 4096; private final float mOutUnclipped[] = new float[OUT_SIZE]; // private final byte mByteBuffer[] = new byte[1024]; // pcm buffer /** buffer for interleaved samples */ private float mBuffer[] = null; /** number samples allocated */ private int mNumSamplesAllocated = 0; /** number samples used */ private int mNumSamplesUsed = 0; /** number samples to ignore at the beginning */ private int mSkipStart = 0; /** number samples to ignore at the end */ private int mSkipEnd = 0; // end pcm buffer /** * Constructor with parameters for output byte stream. * * @param sampleSize sample size in bits, 16, 24 or 8 bits. * @param isSigned true for signed data * @param isBigEndian true for big endian data * @throws IllegalArgumentException throws if sample size not one of the 8, 16, 24. */ public Jmpg123(final int sampleSize, final boolean isSigned, final boolean isBigEndian) throws IllegalArgumentException { if (sampleSize != 16 && sampleSize != 24 && sampleSize != 8) { throw new IllegalArgumentException("Unsupported sample size: " + sampleSize); } mBytesPerSample = sampleSize >> 3; mIsSigned = isSigned; mIsBigEndian = isBigEndian; } /** @return count of channels */ public int getChannelCount() { return mNumChannels; } /** @return sample rate */ public int getSampleRate() { return mSampleRate; } /** @return Number of samples in mp3 file if Xing VBR header was detected, otherwise 0. */ public long getTotalNumSamples() { return mTotalNumSamples; } private static final int getLenOfId3v2Tag(final byte[] buf, int offset) { final int b0 = (int) buf[offset++] & 127; final int b1 = (int) buf[offset++] & 127; final int b2 = (int) buf[offset++] & 127; final int b3 = (int) buf[offset] & 127; return (((((b0 << 7) + b1) << 7) + b2) << 7) + b3; } private final boolean isSyncwordMp123(final byte[] data) { if ((data[0] & 0xFF) != 0xFF) { return false; // * first 8 bits must be '1' } if ((data[1] & 0xE0) != 0xE0) { return false; // next 3 bits are also } if ((data[1] & 0x18) == 0x08) { return false; // no MPEG-1, -2 or -2.5 } switch (data[1] & 0x06) { default: case 0x00: // illegal Layer mInputFormat = FORMAT_UNKNOWN; return false; case 0x02: // Layer3 mInputFormat = FORMAT_MP3; break; case 0x04: // Layer2 mInputFormat = FORMAT_MP2; break; case 0x06: // Layer1 mInputFormat = FORMAT_MP1; break; } if ((data[2] & 0xF0) == 0xF0) { return false; // bad bitrate } if ((data[2] & 0x0C) == 0x0C) { return false; // no sample frequency with (32,44.1,48)/(1,2,4) } if ((data[1] & 0x18) == 0x18 && (data[1] & 0x06) == 0x04 && ((sAbl2[((int) data[2] & 0xff) >> 4] & (1 << (((int) data[3] & 0xff) >> 6))) != 0)) { return false; } if ((data[3] & 3) == 2) { return false; // reserved enphasis mode } return true; } /** * For lame_decode: return code -1 error 0 ok, but need more data before outputing any samples n * number of mono samples output. either 576 or 1152 depending on MP3 file. */ private final int decodeHeaders( final byte[] buffer, final int len, final float[] p, final int psize) { final int processed_mono_samples[] = new int[1]; // java: processed_bytes changed to processed_mono_samples mIsHeaderParsed = false; final int ret = decodeMP3_unclipped(buffer, len, p, psize, processed_mono_samples); /* three cases: * 1. headers parsed, but data not complete * pmp.header_parsed==1 * pmp.framesize=0 * pmp.fsizeold=size of last frame, or 0 if this is first frame * * 2. headers, data parsed, but ancillary data not complete * pmp.header_parsed==1 * pmp.framesize=size of frame * pmp.fsizeold=size of last frame, or 0 if this is first frame * * 3. frame fully decoded: * pmp.header_parsed==0 * pmp.framesize=0 * pmp.fsizeold=size of frame (which is now the last frame) * */ if (this.header_parsed || this.fsizeold > 0 || this.framesize > 0) { mIsHeaderParsed = true; mNumChannels = this.fr.stereo; mSampleRate = freqs[this.fr.sampling_frequency]; // free format, we need the entire frame before we can determine // the bitrate. If we haven't gotten the entire frame, bitrate=0 if (this.fsizeold > 0) { mBitrate = (int) (8 * (4 + this.fsizeold) * mSampleRate / (1.e3 * this.framesize) + 0.5); } else if (this.framesize > 0) { mBitrate = (int) (8 * (4 + this.framesize) * mSampleRate / (1.e3 * this.framesize) + 0.5); } else { mBitrate = tabsel_123[this.fr.lsf][this.fr.lay - 1][this.fr.bitrate_index]; } if (this.num_frames > 0) { // Xing VBR header found and num_frames was set mTotalNumSamples = this.framesize * this.num_frames; } } switch (ret) { case Jmpg123.MP3_OK: return processed_mono_samples[0]; case Jmpg123.MP3_NEED_MORE: return 0; } return -1; } /** * Inspect a input stream * * @param is a input stream. * @return 0 ok, this is mpeg stream, -1 an error. */ public final int open(final InputStream is) { try { final byte buf[] = new byte[100]; if (is.read(buf, 0, 4) != 4) { return -1; } // TODO java: how to detect not a mpeg stream? if ((buf[0] == 'R') && (buf[1] == 'I') && (buf[2] == 'F') && (buf[3] == 'F')) { // && // (buf[8] == 'W') && (buf[9] == 'A') && (buf[10] == 'V') && (buf[11] == 'E') ) { return -1; // RIFF/WAV stream found } if ((buf[0] == '.') && (buf[1] == 's') && (buf[2] == 'n') && (buf[3] == 'd')) { return -1; // AU stream found } if ((buf[0] == 'F') && (buf[1] == 'O') && (buf[2] == 'R') && (buf[3] == 'M')) { // && // (buf[8] == 'A') && (buf[9] == 'I') && (buf[10] == 'F') && (buf[11] == 'F')) { return -1; // AIFF stream found } if (((buf[0] == 'M') | (buf[0] == 'm')) && ((buf[1] == 'A') | (buf[1] == 'a')) && ((buf[2] == 'C') | (buf[2] == 'c'))) { return -1; // APE stream found } if (((buf[0] == 'F') | (buf[0] == 'f')) && ((buf[1] == 'L') | (buf[1] == 'l')) && ((buf[2] == 'A') | (buf[2] == 'a')) && ((buf[3] == 'C') | (buf[3] == 'c'))) { return -1; // FLAC stream found } if (buf[0] == 'O' && buf[1] == 'g' && buf[2] == 'g' && buf[3] == 'S') { return -1; // Ogg stream found } while (buf[0] == 'I' && buf[1] == 'D' && buf[2] == '3') { if (is.read(buf, 4, 6) != 6) { return -1; } final int len = getLenOfId3v2Tag(buf, 6); if (mId3v2TagBuff == null) { mId3v2TagBuff = new byte[10 + len]; System.arraycopy(buf, 0, mId3v2TagBuff, 0, 10); if (is.read(mId3v2TagBuff, 10, len) != len) { return -1; } } if (is.read(buf, 0, 4) != 4) { return -1; } } if (buf[0] == 'A' && buf[1] == 'i' && buf[2] == 'D' && buf[3] == '\1') { if (is.read(buf, 0, 2) != 2) { return -1; } int aid_header = ((int) buf[0] & 0xff) + (((int) buf[1] & 0xff) << 8); // System.out.printf("Album ID found. length = %d\n", aid_header ); // skip rest of AID, except for 6 bytes we have already read aid_header -= 6; // skip (aid_header - 6) bytes for (final int read = is.read(buf, 0, aid_header); read >= 0; ) { aid_header -= read; if (aid_header <= 0) { break; } } // read 4 more bytes to set up buffer for MP3 header check if (is.read(buf, 0, 4) != 4) { return -1; } } int len = OUT_SIZE; while (!isSyncwordMp123(buf)) { buf[0] = buf[1]; buf[1] = buf[2]; buf[2] = buf[3]; if (is.read(buf, 3, 1) != 1) { return -1; } if (--len <= 0) { return -1; // no sync word in 1152 bytes } } boolean freeformat = false; if ((buf[2] & 0xf0) == 0) { // System.out.println("Input file is freeformat."); freeformat = true; } int ret = decodeHeaders(buf, 4, mOutUnclipped, OUT_SIZE); if (-1 == ret) { return -1; } // repeat until we decode a valid mp3 header. while (!mIsHeaderParsed) { len = is.read(buf); if (len != buf.length) { return -1; } ret = decodeHeaders(buf, len, mOutUnclipped, OUT_SIZE); if (-1 == ret) { return -1; } } if (mBitrate == 0 && !freeformat) { // throw new IOException("fail to sync..."); return -1; } // if totalframes > 0, mpglib found a Xing VBR header and computed nsamp & totalframes if (this.num_frames <= 0) { // set as unknown. Later, we will take a guess based on file size ant bitrate mTotalNumSamples = MAX_U_32_NUM; } if (mNumChannels != 2 && mNumChannels != 1) { // System.err.printf("Unsupported number of channels: %d\n", mNumChannels ); return -1; } switch (mInputFormat) { case FORMAT_MP3: mSkipStart = ENCDELAY + 528 + 1; if (this.enc_delay > -1) { mSkipStart = this.enc_delay + 528 + 1; } if (this.enc_padding > -1) { mSkipEnd = this.enc_padding - (528 + 1); mSkipEnd = mSkipEnd < 0 ? 0 : mSkipEnd; } break; case FORMAT_MP2: case FORMAT_MP1: mSkipStart = 240 + 1; break; } mSkipStart *= mNumChannels; mSkipEnd *= mNumChannels; mSkipStart = mSkipStart < 0 ? 0 : mSkipStart; mInputStream = is; return 0; } catch (final IOException e) { return -1; } } private final int addBuffer(final float[] a, final int read) { if (read < 0) { return mNumSamplesUsed - mSkipEnd; } if (mSkipStart >= read) { mSkipStart -= read; return mNumSamplesUsed - mSkipEnd; } final int a_want = read - mSkipStart; if (a_want > 0) { final int b_need = (mNumSamplesUsed + a_want); if (mNumSamplesAllocated < b_need) { mNumSamplesAllocated = b_need; mBuffer = mBuffer == null ? new float[b_need] : Arrays.copyOf(mBuffer, b_need); } System.arraycopy(a, mSkipStart, mBuffer, mNumSamplesUsed, a_want); mNumSamplesUsed = b_need; } mSkipStart = 0; return mNumSamplesUsed - mSkipEnd; } private final void convert(final float[] buffer, final int samples, final byte[] b, int off) { if (mBytesPerSample == 2) { if (mIsSigned) { if (mIsBigEndian) { // signed, big endian for (int i = 0; i < samples; i++) { final float x = buffer[i]; if (x > 32767.0f) { // 0x7fff b[off++] = 0x7f; b[off++] = -1; } else if (x < -32768.0f) { // -0x8000 b[off++] = -0x80; b[off++] = 0; } else { final short v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v >> 8); b[off++] = (byte) (v); } } return; } // signed, little endian for (int i = 0; i < samples; i++) { final float x = buffer[i]; if (x > 32767.0f) { // 0x7fff b[off++] = -1; b[off++] = 0x7f; } else if (x < -32768.0f) { // -0x8000 b[off++] = 0; b[off++] = -0x80; } else { final short v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v); b[off++] = (byte) (v >> 8); } } return; } // if signed if (mIsBigEndian) { // unsigned, big endian for (int i = 0; i < samples; i++) { final float x = buffer[i] + 32768f; if (x > 32767.0f) { // 0x7fff b[off++] = 0x7f; b[off++] = -1; } else if (x < -32768.0f) { // -0x8000 b[off++] = -0x80; b[off++] = 0; } else { final short v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v >> 8); b[off++] = (byte) (v); } } return; } // unsigned, little endian for (int i = 0; i < samples; i++) { final float x = buffer[i] + 32768f; if (x > 32767.0f) { // 0x7fff b[off++] = -1; b[off++] = 0x7f; } else if (x < -32768.0f) { // -0x8000 b[off++] = 0; b[off++] = -0x80; } else { final short v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v); b[off++] = (byte) (v >> 8); } } return; } if (mBytesPerSample == 3) { // TODO dither if (mIsSigned) { if (mIsBigEndian) { // signed, big endian for (int i = 0; i < samples; i++) { float x = buffer[i]; if (x > 32767.0f) { // 0x7fff b[off++] = 0x7f; b[off++] = -1; b[off++] = -1; } else if (x < -32768.0f) { // -0x8000 b[off++] = -0x80; b[off++] = 0; b[off++] = 0; } else { x *= 256f; final int v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v >> 16); b[off++] = (byte) (v >> 8); b[off++] = (byte) (v); } } return; } // signed, stereo, little endian for (int i = 0; i < samples; i++) { float x = buffer[i]; if (x > 32767.0f) { // 0x7fff b[off++] = -1; b[off++] = -1; b[off++] = 0x7f; } else if (x < -32768.0f) { // -0x8000 b[off++] = 0; b[off++] = 0; b[off++] = -0x80; } else { x *= 256f; final int v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v); b[off++] = (byte) (v >> 8); b[off++] = (byte) (v >> 16); } } return; } // if signed if (mIsBigEndian) { // unsigned, stereo, big endian for (int i = 0; i < samples; i++) { float x = buffer[i] + 32768f; if (x > 32767.0f) { // 0x7fff b[off++] = 0x7f; b[off++] = -1; b[off++] = -1; } else if (x < -32768.0f) { // -0x8000 b[off++] = -0x80; b[off++] = 0; b[off++] = 0; } else { x *= 256f; final int v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v >> 16); b[off++] = (byte) (v >> 8); b[off++] = (byte) (v); } } return; } // unsigned, stereo, little endian for (int i = 0; i < samples; i++) { float x = buffer[i] + 32768f; if (x > 32767.0f) { // 0x7fff b[off++] = -1; b[off++] = -1; b[off++] = 0x7f; } else if (x < -32768.0f) { // -0x8000 b[off++] = 0; b[off++] = 0; b[off++] = -0x80; } else { x *= 256f; final int v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v); b[off++] = (byte) (v >> 8); b[off++] = (byte) (v >> 16); } } return; } if (mBytesPerSample == 1) { if (mIsSigned) { // signed for (int i = 0; i < samples; i++) { final float x = buffer[i]; if (x > 32767.0f) { // 0x7fff b[off++] = 0x7f; } else if (x < -32768.0f) { // -0x8000 b[off++] = -0x80; } else { final short v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v >> 8); } } return; } // unsigned for (int i = 0; i < samples; i++) { final float x = buffer[i] + 32768f; if (x > 32767.0f) { // 0x7fff b[off++] = 0x7f; } else if (x < -32768.0f) { // -0x8000 b[off++] = -0x80; } else { final short v = (short) (x > 0 ? x + 0.5f : x - 0.5f); b[off++] = (byte) (v >> 8); } } return; } } private final int getBuffer(final byte[] b, final int off, final int len) { final int samples_in_buffer = mNumSamplesUsed - mSkipEnd; if (samples_in_buffer <= 0) { return 0; } int take = len / mBytesPerSample; if (take > samples_in_buffer) { take = samples_in_buffer; } if (take > 0) { convert(mBuffer, take, b, off); mNumSamplesUsed -= take; if (mNumSamplesUsed < 0) { mNumSamplesUsed = 0; return take * mBytesPerSample; } System.arraycopy(mBuffer, take, mBuffer, 0, mNumSamplesUsed); return take * mBytesPerSample; } return 0; } /** * @param b a buffer to hold packed PCM data for return * @param off the start offset in array buffer at which the data is written. * @param len the byte length requested to be placed into buffer */ public final int read(final byte[] b, final int off, final int len) throws IOException { while (true) { int used = getBuffer(b, off, len); if (used != 0) { return used; } if (mIsEofReached) { return -1; } // final float[] out = mOutUnclipped; final byte[] byte_buff = mByteBuffer; int read; do { final int num_channels = mNumChannels; final int sample_rate = mSampleRate; // int num = 0; // read until we get a valid output frame while ((read = decodeHeaders(byte_buff, num, out, OUT_SIZE)) == 0) { num = mInputStream.read(byte_buff, 0, 1024); if (num <= 0) { // java: len = -1 if eof num = 0; // java: len = -1 if eof // we are done reading the file, but check for buffered data read = decodeHeaders(byte_buff, num, out, OUT_SIZE); if (read <= 0) { read = -1; // done with file } break; } } // read < 0: error, probably EOF // read = 0: not possible with lame_decode_fromfile() ??? // read > 0: number of output samples if (read < 0) { int i = OUT_SIZE; do { out[--i] = 0; } while (i > 0); read = 0; mIsEofReached = true; } if (num_channels != mNumChannels) { throw new IOException("Error: number of channels has changed - not supported"); } if (mSampleRate != sample_rate) { throw new IOException("Error: sample frequency has changed - not supported"); } used = addBuffer(out, read); } while (used <= 0 && read > 0); } // while no data in the buffer } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy