/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.container.wav.adpcm.test; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; /** * RangeCoder * @author taktod */ public class RangeCoder { /** ロガー */ private Logger logger = Logger.getLogger(RangeCoder.class); // indexからデータ値を求めるたまえのリスト private List<Integer> dataList = new ArrayList<Integer>(); // 対応しているデータリスト // データ値からindexを求めるためのmap private Map<Integer, Integer> inverseMap = new HashMap<Integer, Integer>(); // indexからweight値を知るためのリスト private List<Integer> weightList = new ArrayList<Integer>(); // データのweightリスト // indexから下限を知るためのリスト private List<Integer> minBorderList = new ArrayList<Integer>(); // 下限リスト private int low; // 下限(エンコード用) private int code; // code(デコード用) private int range; // range private int weight; // 振り分け値の合計 private int carryBuffer; // 最上位の桁の値 private int carryCount; // 0xFFになっている桁数 private final int rangeMax; private final int rangeBorder; /** 処理結果 */ private List<Byte> target = new ArrayList<Byte>(); /** * コンストラクタ */ public RangeCoder(int rangeMax, int rangeBorder) { this.rangeMax = rangeMax; this.rangeBorder = rangeBorder; low = 0; range = this.rangeMax; carryBuffer = -1; carryCount = 0; } public RangeCoder() { this(0x01000000, 0x010000); } /** * テーブルを作ります * a:値 b:weight * [ * [a b], * [a b], * [a b], * [a b], * [a b], * [a b], * [a b] * ] * として定義します */ public void setupTable(Integer[][] data) { weight = 0; int i = 0; for(Integer[] dat : data) { inverseMap.put(dat[0], i); dataList.add(dat[0]); weightList.add(dat[1]); minBorderList.add(weight); weight += dat[1]; i ++; } } // エンコード処理 public void encodeData(int d) { low += range * minBorderList.get(inverseMap.get(d)) / weight; range = range * weightList.get(inverseMap.get(d)) / weight; // 桁上がりがあるか確認する。 // logger.info(Integer.toHexString(low) + " " + Integer.toHexString(range)); if(low >= rangeMax) { carryBuffer ++; if(carryCount != 0) { // carryCount = 2のとき // 16 FF FFが // 17 00 00になります。 // この場合このタイミングで17 00は書き込んでOK target.add((byte)carryBuffer); for(int i = 0;i < carryCount - 1; i ++) { target.add((byte)0x00); } carryCount = 0; carryBuffer = 0x00; } low = low & 0x00FFFFFF; } if(range < rangeBorder) { // 繰り上がり用のbufferが0xffの場合は繰り上げを実施せずに、countを増やしておく。 range *= 0x0100; // 8bitシフト int carryBufferTmp = (low & 0xFF0000) >> 16; if(carryBufferTmp == 0xFF) { // 追加データが0xFFの場合はcountにいれておきます。 carryCount ++; } else { if(carryBuffer != -1) { target.add((byte)carryBuffer); } if(carryCount != 0) { // carryCountが0でない場合 // 16 FF FF 15 target.add((byte)carryBuffer); for(int i = 0;i < carryCount;i ++) { target.add((byte)0xFF); } carryCount = 0; } carryBuffer = carryBufferTmp; } low = (low & 0x00FFFF) * 0x0100; } } // エンコード結果 public ByteBuffer getEncodeResult() { // のこっているlowをそのまま追加する。 if(carryBuffer != -1) { target.add((byte)carryBuffer); } target.add((byte)((low & 0xFF0000) >> 16)); target.add((byte)((low & 0x00FF00) >> 8)); target.add((byte)((low & 0x0000FF))); ByteBuffer result = ByteBuffer.allocate(target.size()); for(Byte b : target) { result.put(b); } result.flip(); return result; } // デコード処理 // デコードターゲット設定 public void setDecodeTarget(ByteBuffer buffer) { while(buffer.remaining() > 0) { target.add(buffer.get()); } // 初期コードを読み込んでおく。 code = 0; code = code << 8 | (target.remove(0) & 0xFF); code = code << 8 | (target.remove(0) & 0xFF); code = code << 8 | (target.remove(0) & 0xFF); } /** * デコード処理 * @return -1だったらデータなし */ public Integer decodeData() { double dpos = (1f * code * weight / range) + 0.001; // 精度の関係でうまくいかなかったので0.01だけ足しておく。 int val = 0; int pos = (int)dpos; for(int i = 0;i < dataList.size();i ++) { if(minBorderList.get(i) <= pos && pos < minBorderList.get(i) + weightList.get(i)) { val = dataList.get(i); code = code - range * minBorderList.get(i) / weight; range = range * weightList.get(i) / weight; if(range < rangeBorder) { if(target.size() <= 0) { return -1; // データがなくなったら-1を応答する } range = range * 0x0100; code = code << 8 | (target.remove(0) & 0xFF); } return val; } } logger.info("おかしなindex値でした。"); return -1; } }