Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.media;
     18 
     19 import java.io.InputStream;
     20 import java.io.IOException;
     21 
     22 
     23 /**
     24  * ResampleInputStream
     25  * @hide
     26  */
     27 public final class ResampleInputStream extends InputStream
     28 {
     29     static {
     30         System.loadLibrary("media_jni");
     31     }
     32 
     33     private final static String TAG = "ResampleInputStream";
     34 
     35     // pcm input stream
     36     private InputStream mInputStream;
     37 
     38     // sample rates, assumed to be normalized
     39     private final int mRateIn;
     40     private final int mRateOut;
     41 
     42     // input pcm data
     43     private byte[] mBuf;
     44     private int mBufCount;
     45 
     46     // length of 2:1 fir
     47     private static final int mFirLength = 29;
     48 
     49     // helper for bytewise read()
     50     private final byte[] mOneByte = new byte[1];
     51 
     52     /**
     53      * Create a new ResampleInputStream, which converts the sample rate
     54      * @param inputStream InputStream containing 16 bit PCM.
     55      * @param rateIn the input sample rate.
     56      * @param rateOut the output sample rate.
     57      * This only handles rateIn == rateOut / 2 for the moment.
     58      */
     59     public ResampleInputStream(InputStream inputStream, int rateIn, int rateOut) {
     60         // only support 2:1 at the moment
     61         if (rateIn != 2 * rateOut) throw new IllegalArgumentException("only support 2:1 at the moment");
     62         rateIn = 2;
     63         rateOut = 1;
     64 
     65         mInputStream = inputStream;
     66         mRateIn = rateIn;
     67         mRateOut = rateOut;
     68     }
     69 
     70     @Override
     71     public int read() throws IOException {
     72         int rtn = read(mOneByte, 0, 1);
     73         return rtn == 1 ? (0xff & mOneByte[0]) : -1;
     74     }
     75 
     76     @Override
     77     public int read(byte[] b) throws IOException {
     78         return read(b, 0, b.length);
     79     }
     80 
     81     @Override
     82     public int read(byte[] b, int offset, int length) throws IOException {
     83         if (mInputStream == null) throw new IllegalStateException("not open");
     84 
     85         // ensure that mBuf is big enough to cover requested 'length'
     86         int nIn = ((length / 2) * mRateIn / mRateOut + mFirLength) * 2;
     87         if (mBuf == null) {
     88             mBuf = new byte[nIn];
     89         } else if (nIn > mBuf.length) {
     90             byte[] bf = new byte[nIn];
     91             System.arraycopy(mBuf, 0, bf, 0, mBufCount);
     92             mBuf = bf;
     93         }
     94 
     95         // read until we have enough data for at least one output sample
     96         while (true) {
     97             int len = ((mBufCount / 2 - mFirLength) * mRateOut / mRateIn) * 2;
     98             if (len > 0) {
     99                 length = len < length ? len : (length / 2) * 2;
    100                 break;
    101             }
    102             // TODO: should mBuf.length below be nIn instead?
    103             int n = mInputStream.read(mBuf, mBufCount, mBuf.length - mBufCount);
    104             if (n == -1) return -1;
    105             mBufCount += n;
    106         }
    107 
    108         // resample input data
    109         fir21(mBuf, 0, b, offset, length / 2);
    110 
    111         // move any unused bytes to front of mBuf
    112         int nFwd = length * mRateIn / mRateOut;
    113         mBufCount -= nFwd;
    114         if (mBufCount > 0) System.arraycopy(mBuf, nFwd, mBuf, 0, mBufCount);
    115 
    116         return length;
    117     }
    118 
    119 /*
    120     @Override
    121     public int available() throws IOException {
    122         int nsamples = (mIn - mOut + mInputStream.available()) / 2;
    123         return ((nsamples - mFirLength) * mRateOut / mRateIn) * 2;
    124     }
    125 */
    126 
    127     @Override
    128     public void close() throws IOException {
    129         try {
    130             if (mInputStream != null) mInputStream.close();
    131         } finally {
    132             mInputStream = null;
    133         }
    134     }
    135 
    136     @Override
    137     protected void finalize() throws Throwable {
    138         if (mInputStream != null) {
    139             close();
    140             throw new IllegalStateException("someone forgot to close ResampleInputStream");
    141         }
    142     }
    143 
    144     //
    145     // fir filter code JNI interface
    146     //
    147     private static native void fir21(byte[] in, int inOffset,
    148             byte[] out, int outOffset, int npoints);
    149 
    150 }
    151