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 */ ResampleInputStream(InputStream inputStream, int rateIn, int rateOut)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 read()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 read(byte[] b)77 public int read(byte[] b) throws IOException { 78 return read(b, 0, b.length); 79 } 80 81 @Override read(byte[] b, int offset, int length)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 close()128 public void close() throws IOException { 129 try { 130 if (mInputStream != null) mInputStream.close(); 131 } finally { 132 mInputStream = null; 133 } 134 } 135 136 @Override finalize()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