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
17package android.media;
18
19import android.util.Log;
20
21import java.io.InputStream;
22import java.io.IOException;
23
24
25/**
26 * AmrInputStream
27 * @hide
28 */
29public final class AmrInputStream extends InputStream
30{
31    static {
32        System.loadLibrary("media_jni");
33    }
34
35    private final static String TAG = "AmrInputStream";
36
37    // frame is 20 msec at 8.000 khz
38    private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000;
39
40    // pcm input stream
41    private InputStream mInputStream;
42
43    // native handle
44    private int mGae;
45
46    // result amr stream
47    private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2];
48    private int mBufIn = 0;
49    private int mBufOut = 0;
50
51    // helper for bytewise read()
52    private byte[] mOneByte = new byte[1];
53
54    /**
55     * Create a new AmrInputStream, which converts 16 bit PCM to AMR
56     * @param inputStream InputStream containing 16 bit PCM.
57     */
58    public AmrInputStream(InputStream inputStream) {
59        mInputStream = inputStream;
60        mGae = GsmAmrEncoderNew();
61        GsmAmrEncoderInitialize(mGae);
62    }
63
64    @Override
65    public int read() throws IOException {
66        int rtn = read(mOneByte, 0, 1);
67        return rtn == 1 ? (0xff & mOneByte[0]) : -1;
68    }
69
70    @Override
71    public int read(byte[] b) throws IOException {
72        return read(b, 0, b.length);
73    }
74
75    @Override
76    public int read(byte[] b, int offset, int length) throws IOException {
77        if (mGae == 0) throw new IllegalStateException("not open");
78
79        // local buffer of amr encoded audio empty
80        if (mBufOut >= mBufIn) {
81            // reset the buffer
82            mBufOut = 0;
83            mBufIn = 0;
84
85            // fetch a 20 msec frame of pcm
86            for (int i = 0; i < SAMPLES_PER_FRAME * 2; ) {
87                int n = mInputStream.read(mBuf, i, SAMPLES_PER_FRAME * 2 - i);
88                if (n == -1) return -1;
89                i += n;
90            }
91
92            // encode it
93            mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0);
94        }
95
96        // return encoded audio to user
97        if (length > mBufIn - mBufOut) length = mBufIn - mBufOut;
98        System.arraycopy(mBuf, mBufOut, b, offset, length);
99        mBufOut += length;
100
101        return length;
102    }
103
104    @Override
105    public void close() throws IOException {
106        try {
107            if (mInputStream != null) mInputStream.close();
108        } finally {
109            mInputStream = null;
110            try {
111                if (mGae != 0) GsmAmrEncoderCleanup(mGae);
112            } finally {
113                try {
114                    if (mGae != 0) GsmAmrEncoderDelete(mGae);
115                } finally {
116                    mGae = 0;
117                }
118            }
119        }
120    }
121
122    @Override
123    protected void finalize() throws Throwable {
124        if (mGae != 0) {
125            close();
126            throw new IllegalStateException("someone forgot to close AmrInputStream");
127        }
128    }
129
130    //
131    // AudioRecord JNI interface
132    //
133    private static native int GsmAmrEncoderNew();
134    private static native void GsmAmrEncoderInitialize(int gae);
135    private static native int GsmAmrEncoderEncode(int gae,
136            byte[] pcm, int pcmOffset, byte[] amr, int amrOffset) throws IOException;
137    private static native void GsmAmrEncoderCleanup(int gae);
138    private static native void GsmAmrEncoderDelete(int gae);
139
140}
141