001/* This actor decompresses a vector quantized signal.
002 @Copyright (c) 1998-2014 The Regents of the University of California.
003 All rights reserved.
004
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the
008 above copyright notice and the following two paragraphs appear in all
009 copies of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION 2
025 COPYRIGHTENDKEY
026 */
027package ptolemy.domains.sdf.lib.vq;
028
029import java.io.FileNotFoundException;
030import java.io.IOException;
031import java.io.InputStream;
032import java.net.MalformedURLException;
033import java.net.URL;
034
035import ptolemy.actor.lib.Transformer;
036import ptolemy.data.IntMatrixToken;
037import ptolemy.data.IntToken;
038import ptolemy.data.StringToken;
039import ptolemy.data.expr.Parameter;
040import ptolemy.data.type.BaseType;
041import ptolemy.kernel.CompositeEntity;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.NameDuplicationException;
044import ptolemy.kernel.util.Workspace;
045import ptolemy.math.IntegerMatrixMath;
046import ptolemy.util.FileUtilities;
047
048///////////////////////////////////////////////////////////////////
049//// VQDecode
050
051/**
052 This actor decompresses a vector quantized signal.   This operation is simply
053 a table lookup into the codebook.
054
055 @see HTVQEncode
056
057 @author Steve Neuendorffer
058 @version $Id$
059 @since Ptolemy II 0.2
060 @Pt.ProposedRating Yellow (neuendor)
061 @Pt.AcceptedRating Red
062 */
063
064// FIXME This should be generalized to a Table-lookup actor.
065public class VQDecode extends Transformer {
066    /** Construct an actor in the specified container with the specified
067     *  name.
068     *  @param container The container.
069     *  @param name The name of this adder within the container.
070     *  @exception IllegalActionException If the actor cannot be contained
071     *   by the proposed container.
072     *  @exception NameDuplicationException If the name coincides with
073     *   an actor already in the container.
074     */
075    public VQDecode(CompositeEntity container, String name)
076            throws IllegalActionException, NameDuplicationException {
077        super(container, name);
078
079        input.setTypeEquals(BaseType.INT);
080
081        output.setTypeEquals(BaseType.INT_MATRIX);
082
083        codeBook = new Parameter(this, "codeBook", new StringToken(
084                "/ptolemy/domains/sdf" + "/lib/vq/data/usc_hvq_s5.dat"));
085        codeBook.setTypeEquals(BaseType.STRING);
086
087        blockCount = new Parameter(this, "blockCount", new IntToken("1"));
088        blockCount.setTypeEquals(BaseType.INT);
089
090        blockWidth = new Parameter(this, "blockWidth", new IntToken("4"));
091        blockWidth.setTypeEquals(BaseType.INT);
092
093        blockHeight = new Parameter(this, "blockHeight", new IntToken("2"));
094        blockHeight.setTypeEquals(BaseType.INT);
095
096        input_tokenConsumptionRate = new Parameter(input,
097                "tokenConsumptionRate");
098        input_tokenConsumptionRate.setTypeEquals(BaseType.INT);
099        input_tokenConsumptionRate.setExpression("blockCount");
100
101        output_tokenProductionRate = new Parameter(output,
102                "tokenProductionRate");
103        output_tokenProductionRate.setTypeEquals(BaseType.INT);
104        output_tokenProductionRate.setExpression("blockCount");
105    }
106
107    ///////////////////////////////////////////////////////////////////
108    ////                      ports and parameters                 ////
109
110    /** A Parameter of type String, giving the location of the codebook data
111     *  file relative to the root classpath.
112     */
113    public Parameter codeBook;
114
115    /** The number of blocks to be decoded during each firing.
116     *  The default value is one, which will always work, but using a higher
117     *  number (such as the number of blocks in a frame) will speed things up.
118     *  This should contain an integer.
119     */
120    public Parameter blockCount;
121
122    /** The width, in integer pixels, of the block to decode. */
123    public Parameter blockWidth;
124
125    /** The width, in integer pixels, of the block to decode. */
126    public Parameter blockHeight;
127
128    /** The input rate. */
129    public Parameter input_tokenConsumptionRate;
130
131    /** The output rate. */
132    public Parameter output_tokenProductionRate;
133
134    ///////////////////////////////////////////////////////////////////
135    ////                         public methods                    ////
136
137    /** Clone the actor into the specified workspace.
138     *  @param workspace The workspace for the new object.
139     *  @return A new actor.
140     *  @exception CloneNotSupportedException If a derived class contains
141     *   an attribute that cannot be cloned.
142     */
143    @Override
144    public Object clone(Workspace workspace) throws CloneNotSupportedException {
145        VQDecode newObject = (VQDecode) super.clone(workspace);
146        newObject._codebook = new int[6][256][][];
147        return newObject;
148    }
149
150    /**
151     * Fire this actor.
152     * Consume a number of tokens on the input port, each representing a
153     * VQ codeword.  Index into the appropriate codebook given by the
154     * blockWidth and blockHeight parameters to find the decoded vector for
155     * each codeword.  Output an IntMatrixToken representing each decoded
156     * vector on the output port.
157     * @exception IllegalActionException If the input or output are not
158     * connected
159     * @exception ArrayIndexOutOfBoundsException If the input codewords are
160     * not between 0 and 255.
161     */
162    @Override
163    public void fire() throws IllegalActionException {
164        super.fire();
165        int j;
166        int stage = _stages(_blockWidth * _blockHeight);
167        _codewords = input.get(0, _blockCount);
168
169        for (j = 0; j < _blockCount; j++) {
170            _blocks[j] = new IntMatrixToken(
171                    _codebook[stage][((IntToken) _codewords[j]).intValue()]);
172        }
173
174        output.send(0, _blocks, _blocks.length);
175    }
176
177    /**
178     * Initialize this actor.
179     * Load the codebooks and lookup tables from the file given by the
180     * parameter "codeBook".
181     * @exception IllegalActionException If the parameters do not have
182     * legal values, or the codebook file cannot be read.
183     */
184    @Override
185    public void initialize() throws IllegalActionException {
186        super.initialize();
187
188        InputStream source = null;
189
190        _blockCount = ((IntToken) blockCount.getToken()).intValue();
191        _blockWidth = ((IntToken) blockWidth.getToken()).intValue();
192        _blockHeight = ((IntToken) blockHeight.getToken()).intValue();
193
194        _codewords = new ptolemy.data.Token[_blockCount];
195        _blocks = new IntMatrixToken[_blockCount];
196
197        String filename = ((StringToken) codeBook.getToken()).stringValue();
198
199        try {
200            if (filename != null) {
201                try {
202                    URL dataurl = FileUtilities.nameToURL(filename, null,
203                            getClass().getClassLoader());
204                    _debug("VQDecode: codebook = " + dataurl);
205                    source = dataurl.openStream();
206                } catch (MalformedURLException e) {
207                    System.err.println(e.toString());
208                } catch (FileNotFoundException e) {
209                    System.err.println("File not found: " + e);
210                } catch (IOException e) {
211                    throw new IllegalActionException(
212                            "Error reading" + " input file: " + e.getMessage());
213                }
214            }
215
216            int i;
217            int j;
218            int x;
219            int size = 1;
220            byte[] temp;
221            int[] intTemp;
222            int rows = 1;
223            int columns = 1;
224
225            for (i = 0; i < 5; i++) {
226                size = size * 2;
227
228                if (i % 2 == 0) {
229                    columns = columns * 2;
230                } else {
231                    rows = rows * 2;
232                }
233
234                temp = new byte[size];
235                intTemp = new int[size];
236
237                for (j = 0; j < 256; j++) {
238                    if (_fullRead(source, temp) != size) {
239                        throw new IllegalActionException(
240                                "Error reading " + "codebook file!");
241                    }
242
243                    for (x = 0; x < size; x++) {
244                        intTemp[x] = temp[x] & 255;
245                    }
246
247                    _codebook[i][j] = IntegerMatrixMath
248                            .toMatrixFromArray(intTemp, rows, columns);
249                }
250
251                // skip over the lookup tables.
252                temp = new byte[65536];
253
254                // read in the lookup table.
255                if (_fullRead(source, temp) != 65536) {
256                    throw new IllegalActionException(
257                            "Error reading " + "codebook file!");
258                }
259            }
260        } catch (Exception e) {
261            throw new IllegalActionException(e.getMessage());
262        } finally {
263            if (source != null) {
264                try {
265                    source.close();
266                } catch (IOException e) {
267                }
268            }
269        }
270    }
271
272    ///////////////////////////////////////////////////////////////////
273    ////                         private methods                   ////
274    private int _fullRead(InputStream s, byte[] b) throws IOException {
275        int length = 0;
276        int remaining = b.length;
277        int bytesRead = 0;
278
279        while (remaining > 0) {
280            bytesRead = s.read(b, length, remaining);
281
282            if (bytesRead == -1) {
283                throw new IOException("Unexpected EOF");
284            }
285
286            remaining -= bytesRead;
287            length += bytesRead;
288        }
289
290        return length;
291    }
292
293    /** Given a vector of the given length, compute the codebook stage
294     *  appropriate.  Basically, compute log base 2 of length, assuming
295     *  length is a power of 2.
296     */
297    private int _stages(int length) {
298        int x = 0;
299
300        if (length < 2) {
301            throw new RuntimeException(
302                    "Vector length of " + length + "must be greater than 1");
303        }
304
305        while (length > 2) {
306            length = length >> 1;
307            x++;
308        }
309
310        return x;
311    }
312
313    private int[][][][] _codebook = new int[6][256][][];
314
315    private ptolemy.data.Token[] _codewords;
316
317    private IntMatrixToken[] _blocks;
318
319    private int _blockCount;
320
321    private int _blockWidth;
322
323    private int _blockHeight;
324}