1/*
2 * ---------------------------------------------------------------------------
3 * UlawEncoderInputStream.java
4 *
5 * Copyright 2008 Nuance Communciations, Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the 'License'); you may not
8 * use this file except in compliance with the License.
9 *
10 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 * License for the specific language governing permissions and limitations under
17 * the License.
18 *
19 * ---------------------------------------------------------------------------
20 */
21
22package android.speech.srec;
23
24import java.io.IOException;
25import java.io.InputStream;
26
27/**
28 * InputStream which transforms 16 bit pcm data to ulaw data.
29 *
30 * Not yet ready to be supported, so
31 * @hide
32 */
33public final class UlawEncoderInputStream extends InputStream {
34    private final static String TAG = "UlawEncoderInputStream";
35
36    private final static int MAX_ULAW = 8192;
37    private final static int SCALE_BITS = 16;
38
39    private InputStream mIn;
40
41    private int mMax = 0;
42
43    private final byte[] mBuf = new byte[1024];
44    private int mBufCount = 0; // should be 0 or 1
45
46    private final byte[] mOneByte = new byte[1];
47
48
49    public static void encode(byte[] pcmBuf, int pcmOffset,
50            byte[] ulawBuf, int ulawOffset, int length, int max) {
51
52        // from  'ulaw' in wikipedia
53        // +8191 to +8159                          0x80
54        // +8158 to +4063 in 16 intervals of 256   0x80 + interval number
55        // +4062 to +2015 in 16 intervals of 128   0x90 + interval number
56        // +2014 to  +991 in 16 intervals of  64   0xA0 + interval number
57        //  +990 to  +479 in 16 intervals of  32   0xB0 + interval number
58        //  +478 to  +223 in 16 intervals of  16   0xC0 + interval number
59        //  +222 to   +95 in 16 intervals of   8   0xD0 + interval number
60        //   +94 to   +31 in 16 intervals of   4   0xE0 + interval number
61        //   +30 to    +1 in 15 intervals of   2   0xF0 + interval number
62        //     0                                   0xFF
63
64        //    -1                                   0x7F
65        //   -31 to    -2 in 15 intervals of   2   0x70 + interval number
66        //   -95 to   -32 in 16 intervals of   4   0x60 + interval number
67        //  -223 to   -96 in 16 intervals of   8   0x50 + interval number
68        //  -479 to  -224 in 16 intervals of  16   0x40 + interval number
69        //  -991 to  -480 in 16 intervals of  32   0x30 + interval number
70        // -2015 to  -992 in 16 intervals of  64   0x20 + interval number
71        // -4063 to -2016 in 16 intervals of 128   0x10 + interval number
72        // -8159 to -4064 in 16 intervals of 256   0x00 + interval number
73        // -8192 to -8160                          0x00
74
75        // set scale factors
76        if (max <= 0) max = MAX_ULAW;
77
78        int coef = MAX_ULAW * (1 << SCALE_BITS) / max;
79
80        for (int i = 0; i < length; i++) {
81            int pcm = (0xff & pcmBuf[pcmOffset++]) + (pcmBuf[pcmOffset++] << 8);
82            pcm = (pcm * coef) >> SCALE_BITS;
83
84            int ulaw;
85            if (pcm >= 0) {
86                ulaw = pcm <= 0 ? 0xff :
87                        pcm <=   30 ? 0xf0 + ((  30 - pcm) >> 1) :
88                        pcm <=   94 ? 0xe0 + ((  94 - pcm) >> 2) :
89                        pcm <=  222 ? 0xd0 + (( 222 - pcm) >> 3) :
90                        pcm <=  478 ? 0xc0 + (( 478 - pcm) >> 4) :
91                        pcm <=  990 ? 0xb0 + (( 990 - pcm) >> 5) :
92                        pcm <= 2014 ? 0xa0 + ((2014 - pcm) >> 6) :
93                        pcm <= 4062 ? 0x90 + ((4062 - pcm) >> 7) :
94                        pcm <= 8158 ? 0x80 + ((8158 - pcm) >> 8) :
95                        0x80;
96            } else {
97                ulaw = -1 <= pcm ? 0x7f :
98                          -31 <= pcm ? 0x70 + ((pcm -   -31) >> 1) :
99                          -95 <= pcm ? 0x60 + ((pcm -   -95) >> 2) :
100                         -223 <= pcm ? 0x50 + ((pcm -  -223) >> 3) :
101                         -479 <= pcm ? 0x40 + ((pcm -  -479) >> 4) :
102                         -991 <= pcm ? 0x30 + ((pcm -  -991) >> 5) :
103                        -2015 <= pcm ? 0x20 + ((pcm - -2015) >> 6) :
104                        -4063 <= pcm ? 0x10 + ((pcm - -4063) >> 7) :
105                        -8159 <= pcm ? 0x00 + ((pcm - -8159) >> 8) :
106                        0x00;
107            }
108            ulawBuf[ulawOffset++] = (byte)ulaw;
109        }
110    }
111
112    /**
113     * Compute the maximum of the absolute value of the pcm samples.
114     * The return value can be used to set ulaw encoder scaling.
115     * @param pcmBuf array containing 16 bit pcm data.
116     * @param offset offset of start of 16 bit pcm data.
117     * @param length number of pcm samples (not number of input bytes)
118     * @return maximum abs of pcm data values
119     */
120    public static int maxAbsPcm(byte[] pcmBuf, int offset, int length) {
121        int max = 0;
122        for (int i = 0; i < length; i++) {
123            int pcm = (0xff & pcmBuf[offset++]) + (pcmBuf[offset++] << 8);
124            if (pcm < 0) pcm = -pcm;
125            if (pcm > max) max = pcm;
126        }
127        return max;
128    }
129
130    /**
131     * Create an InputStream which takes 16 bit pcm data and produces ulaw data.
132     * @param in InputStream containing 16 bit pcm data.
133     * @param max pcm value corresponding to maximum ulaw value.
134     */
135    public UlawEncoderInputStream(InputStream in, int max) {
136        mIn = in;
137        mMax = max;
138    }
139
140    @Override
141    public int read(byte[] buf, int offset, int length) throws IOException {
142        if (mIn == null) throw new IllegalStateException("not open");
143
144        // return at least one byte, but try to fill 'length'
145        while (mBufCount < 2) {
146            int n = mIn.read(mBuf, mBufCount, Math.min(length * 2, mBuf.length - mBufCount));
147            if (n == -1) return -1;
148            mBufCount += n;
149        }
150
151        // compand data
152        int n = Math.min(mBufCount / 2, length);
153        encode(mBuf, 0, buf, offset, n, mMax);
154
155        // move data to bottom of mBuf
156        mBufCount -= n * 2;
157        for (int i = 0; i < mBufCount; i++) mBuf[i] = mBuf[i + n * 2];
158
159        return n;
160    }
161
162    @Override
163    public int read(byte[] buf) throws IOException {
164        return read(buf, 0, buf.length);
165    }
166
167    @Override
168    public int read() throws IOException {
169        int n = read(mOneByte, 0, 1);
170        if (n == -1) return -1;
171        return 0xff & (int)mOneByte[0];
172    }
173
174    @Override
175    public void close() throws IOException {
176        if (mIn != null) {
177            InputStream in = mIn;
178            mIn = null;
179            in.close();
180        }
181    }
182
183    @Override
184    public int available() throws IOException {
185        return (mIn.available() + mBufCount) / 2;
186    }
187}
188