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 android.util.Log; 20 21 import java.io.InputStream; 22 import java.io.IOException; 23 24 25 /** 26 * ResampleInputStream 27 * @hide 28 */ 29 public final class ResampleInputStream extends InputStream 30 { 31 static { 32 System.loadLibrary("media_jni"); 33 } 34 35 private final static String TAG = "ResampleInputStream"; 36 37 // pcm input stream 38 private InputStream mInputStream; 39 40 // sample rates, assumed to be normalized 41 private final int mRateIn; 42 private final int mRateOut; 43 44 // input pcm data 45 private byte[] mBuf; 46 private int mBufCount; 47 48 // length of 2:1 fir 49 private static final int mFirLength = 29; 50 51 // helper for bytewise read() 52 private final byte[] mOneByte = new byte[1]; 53 54 /** 55 * Create a new ResampleInputStream, which converts the sample rate 56 * @param inputStream InputStream containing 16 bit PCM. 57 * @param rateIn the input sample rate. 58 * @param rateOut the output sample rate. 59 * This only handles rateIn == rateOut / 2 for the moment. 60 */ ResampleInputStream(InputStream inputStream, int rateIn, int rateOut)61 public ResampleInputStream(InputStream inputStream, int rateIn, int rateOut) { 62 // only support 2:1 at the moment 63 if (rateIn != 2 * rateOut) throw new IllegalArgumentException("only support 2:1 at the moment"); 64 rateIn = 2; 65 rateOut = 1; 66 67 mInputStream = inputStream; 68 mRateIn = rateIn; 69 mRateOut = rateOut; 70 } 71 72 @Override read()73 public int read() throws IOException { 74 int rtn = read(mOneByte, 0, 1); 75 return rtn == 1 ? (0xff & mOneByte[0]) : -1; 76 } 77 78 @Override read(byte[] b)79 public int read(byte[] b) throws IOException { 80 return read(b, 0, b.length); 81 } 82 83 @Override read(byte[] b, int offset, int length)84 public int read(byte[] b, int offset, int length) throws IOException { 85 if (mInputStream == null) throw new IllegalStateException("not open"); 86 87 // ensure that mBuf is big enough to cover requested 'length' 88 int nIn = ((length / 2) * mRateIn / mRateOut + mFirLength) * 2; 89 if (mBuf == null) { 90 mBuf = new byte[nIn]; 91 } else if (nIn > mBuf.length) { 92 byte[] bf = new byte[nIn]; 93 System.arraycopy(mBuf, 0, bf, 0, mBufCount); 94 mBuf = bf; 95 } 96 97 // read until we have enough data for at least one output sample 98 while (true) { 99 int len = ((mBufCount / 2 - mFirLength) * mRateOut / mRateIn) * 2; 100 if (len > 0) { 101 length = len < length ? len : (length / 2) * 2; 102 break; 103 } 104 // TODO: should mBuf.length below be nIn instead? 105 int n = mInputStream.read(mBuf, mBufCount, mBuf.length - mBufCount); 106 if (n == -1) return -1; 107 mBufCount += n; 108 } 109 110 // resample input data 111 fir21(mBuf, 0, b, offset, length / 2); 112 113 // move any unused bytes to front of mBuf 114 int nFwd = length * mRateIn / mRateOut; 115 mBufCount -= nFwd; 116 if (mBufCount > 0) System.arraycopy(mBuf, nFwd, mBuf, 0, mBufCount); 117 118 return length; 119 } 120 121 /* 122 @Override 123 public int available() throws IOException { 124 int nsamples = (mIn - mOut + mInputStream.available()) / 2; 125 return ((nsamples - mFirLength) * mRateOut / mRateIn) * 2; 126 } 127 */ 128 129 @Override close()130 public void close() throws IOException { 131 try { 132 if (mInputStream != null) mInputStream.close(); 133 } finally { 134 mInputStream = null; 135 } 136 } 137 138 @Override finalize()139 protected void finalize() throws Throwable { 140 if (mInputStream != null) { 141 close(); 142 throw new IllegalStateException("someone forgot to close ResampleInputStream"); 143 } 144 } 145 146 // 147 // fir filter code JNI interface 148 // 149 private static native void fir21(byte[] in, int inOffset, 150 byte[] out, int outOffset, int npoints); 151 152 } 153