/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.frame.adpcmimawav.test; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import com.ttProject.nio.channels.FileReadChannel; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.unit.extra.Bit; import com.ttProject.unit.extra.BitConnector; import com.ttProject.unit.extra.bit.Bit1; import com.ttProject.unit.extra.bit.Bit4; import com.ttProject.unit.extra.bit.Bit5; import com.ttProject.unit.extra.bit.Bit6; import com.ttProject.util.BufferUtil; /** * search for canonical huffman codes(chc) * adpcm is just from 0 to f. can I do something? * @author taktod */ public class ChcTest { /** logger */ private Logger logger = Logger.getLogger(ChcTest.class); /** * firstly, I wanna get the count data for each hex digit. * @throws Exception */ // @Test public void test() throws Exception { logger.info("start to check."); IReadChannel channel = FileReadChannel.openFileReadChannel( Thread.currentThread().getContextClassLoader().getResource("bm_mono.wav") // Thread.currentThread().getContextClassLoader().getResource("test_mono.wav") // Thread.currentThread().getContextClassLoader().getResource("rtype_mono.wav") ); channel.position(0x5C); int[] order = new int[16]; // ここから、読み込んでいく。 while(channel.position() < channel.size()) { // とりあえずどの符号が一番おおくでてくるか調べていきたい。 ByteBuffer buffer = BufferUtil.safeRead(channel, 0x400); buffer.position(4); int[] result = new int[16]; while(buffer.remaining() > 0) { byte data = buffer.get(); result[(data & 0x0F)] ++; result[((data >> 4) & 0x0F)] ++; } int[] sortMap = Arrays.copyOf(result, result.length); Arrays.sort(sortMap); // 一番出現率の高いデータをみつけて、順番に並べる StringBuilder data = new StringBuilder(); for(int j = 0;j < 16;j ++) { for(int i = 0;i < 16;i ++) { if(sortMap[j] == result[i]) { if(j == 15) { order[i] ++; } data.append(Integer.toHexString(i)).append(":").append(result[i]).append(" "); result[i] = 99999; break; } } } logger.info(data.toString()); } StringBuilder dat = new StringBuilder(); for(int i = 0;i < 16;i ++) { dat.append(i).append(":").append(order[i]).append(" "); } logger.info(dat.toString()); } /* 13:09:21,369 [main] INFO [ChcTest] - 最終1位回数メモ 13:09:21,369 [main] INFO [ChcTest] - 0:16352 1:4217 2:4747 3:2926 4:0 5:0 6:0 7:0 8:11209 9:4878 10:5688 11:2906 12:0 13:0 14:0 15:0 13:25:41,307 [main] INFO [ChcTest] - 最終15位回数メモ 13:25:41,307 [main] INFO [ChcTest] - 0:0 1:2 2:2079 3:253 4:7 5:23 6:10873 7:16504 8:0 9:0 10:0 11:0 12:0 13:7 14:9391 15:13784 18:39:57,141 [main] INFO [ChcTest] - 最終1位回数メモ 18:39:57,142 [main] INFO [ChcTest] - 0:47170 1:21390 2:7042 3:5067 4:0 5:0 6:0 7:0 8:42730 9:23854 10:7446 11:4980 12:1 13:0 14:0 15:0 18:38:38,436 [main] INFO [ChcTest] - 最終15位回数メモ 18:38:38,436 [main] INFO [ChcTest] - 0:0 1:67 2:55 3:35 4:36 5:84 6:33375 7:50789 8:0 9:0 10:0 11:0 12:0 13:2 14:29492 15:45745 */ // 通常のadpcmの場合はlittleEndian動作をbigEndian動作に変更する。 // ハフマン圧縮を掛ける場合は次のようにする。 /* * 1: 0 8 * 0111: 8 0 * 0110: 2 2 * 0101: A A * 01001: 1 1 * 01000: 3 3 * 00111: 4 4 * 00110: 5 5 * 00101: 9 9 * 00100: B B * 00011: C C * 00010: D D * 000011:6 6 * 000010:7 7 * 000001:E E * 000000:F F */ private Bit getHVal3(int data) { return new Bit4(data); } private Bit getHVal2(int data) { switch(data) { case 0x08: return new Bit1(1); case 0x00: return new Bit4(7); case 0x02: return new Bit4(6); case 0x0A: return new Bit4(5); case 0x01: return new Bit5(9); case 0x03: return new Bit5(8); case 0x04: return new Bit5(7); case 0x05: return new Bit5(6); case 0x09: return new Bit5(5); case 0x0B: return new Bit5(4); case 0x0C: return new Bit5(3); case 0x0D: return new Bit5(2); case 0x06: return new Bit6(3); case 0x07: return new Bit6(2); case 0x0E: return new Bit6(1); case 0x0F: default: return new Bit6(0); } } private Bit getHVal1(int data) { switch(data) { case 0x00: return new Bit1(1); case 0x08: return new Bit4(7); case 0x02: return new Bit4(6); case 0x0A: return new Bit4(5); case 0x01: return new Bit5(9); case 0x03: return new Bit5(8); case 0x04: return new Bit5(7); case 0x05: return new Bit5(6); case 0x09: return new Bit5(5); case 0x0B: return new Bit5(4); case 0x0C: return new Bit5(3); case 0x0D: return new Bit5(2); case 0x06: return new Bit6(3); case 0x07: return new Bit6(2); case 0x0E: return new Bit6(1); case 0x0F: default: return new Bit6(0); } } // @Test public void test2() throws Exception { logger.info("start to check."); IReadChannel channel = FileReadChannel.openFileReadChannel( // Thread.currentThread().getContextClassLoader().getResource("test_mono.wav") Thread.currentThread().getContextClassLoader().getResource("rtype_mono.wav") ); channel.position(0x5C); while(channel.position() < channel.size()) { BitConnector connector = null; Bit[] bits1 = new Bit[2040]; // 2040nibble存在するので、やってみる。 Bit[] bits2 = new Bit[2040]; // 2040nibble存在するので、やってみる。 Bit[] bits3 = new Bit[2040]; // 2040nibble存在するので、やってみる。 ByteBuffer buffer = BufferUtil.safeRead(channel, 0x400); buffer.position(4); int i = 0; while(buffer.remaining() > 0) { byte data = buffer.get(); bits1[i] = getHVal1(data & 0x0F); bits2[i] = getHVal2(data & 0x0F); bits3[i] = getHVal3(data & 0x0F); i ++; bits1[i] = getHVal1((data >> 4) & 0x0F); bits2[i] = getHVal2((data >> 4) & 0x0F); bits3[i] = getHVal3((data >> 4) & 0x0F); i ++; } connector = new BitConnector(); logger.info("1:" + connector.connect(bits1).remaining()); logger.info("2:" + connector.connect(bits2).remaining()); logger.info("3:" + connector.connect(bits3).remaining()); } } /** * 思いついたadpcm_ttという圧縮形式 * 44100 kHz monoralのみ * 通常のadpcm返還を実施後、huffman符号化による圧縮を実施する。 * 基本テーブルはtest2と同じだが、先に最もよくつかうデータと最も使わないデータを割り出しておく。 * * 1: 8 * 0111: 0 * 0110: 2 * 0101: A * 01001: 1 * 01000: 3 * 00111: 4 * 00110: 5 * 00101: 9 * 00100: B * 00011: C * 00010: D * 000011:6 * 000010:7 * 000001:E * 000000:F * * あまりよくなかったので、次のmapをためしてみる。 * 111: 0 * 110: 8 * 1011: 1 * 1010: 2 * 1001: 3 * 1000: 4 * 0111: 5 * 0110: 9 * 0101: A * 0100: B * 0011: C * 0010: D * 00011:6 * 00010:7 * 00001:E * 00000:F * 全体的に圧縮かかるけど・・・ * * 111: 0 * 110: 1 * 101: 8 * 100: 9 * 0111: 2 * 0110: 3 * 0101: 4 * 0100: A * 0011: B * 0010: C * 00011: 5 * 00010: D * 000011:6 * 000010:7 * 000001:E * 000000:F * あまりかわらず、でもまぁそこそこかな * total:151 650 698 (7%) type1 * * 11: 8 * 1011: 0 * 1010: 1 * 1001: 2 * 1000: 3 * 0111: 4 * 0110: 5 * 0101: 9 * 0100: A * 0011: B * 0010: C * 0001: D * 000011:6 * 000010:7 * 000001:E * 000000:F * total:157 327 067 (4%) type2 * * 163 512 320(オリジナル) * * もっともよく使うデータは8と入れ替え * ただし8の場合は入れ替えは実施せず * もっとも使わないデータは6と入れ替えとします。 * ただし、6,7,E,Fだった場合は入れ替え実施せず。 * * 出力データフォーマットは次のようにする。 * 基本adpcm ima wavと同じにする。 * 始めの2byteは初期振幅(predictor) * 次の1byteは初期index * 次の1byteはよく使うデータ + 最も使わないデータ(ただし、両値が一致する場合は通常のadpcmとします。) * あとはnibbleの羅列(ただしadpcm_ima_wavとは違いbigendianにします。) * test_mono * 20480(オリジナル) * 19087(type1) 7% * 18160(type2) 11% * * bm_mono * 163 512 320(オリジナル) * total:151 650 698 (7%) type1 * total:157 327 067 (4%) type2 * * rtype_mono * 54 193 152 (original) * 49 798 993 type1 8% * 51 323 639 type2 5% */ // @Test public void test3() throws Exception { logger.info("start to check"); IReadChannel channel = FileReadChannel.openFileReadChannel( Thread.currentThread().getContextClassLoader().getResource("test_mono.wav") // Thread.currentThread().getContextClassLoader().getResource("bm_mono.wav") // Thread.currentThread().getContextClassLoader().getResource("rtype_mono.wav") ); channel.position(0x5C); long length = 0; // ここからループする部分 while(channel.position() < channel.size()) { Map<Integer, Bit> huffMap = new HashMap<Integer, Bit>(ttMap); ByteBuffer buffer = BufferUtil.safeRead(channel, 0x400); buffer.position(4); int[] result = new int[16]; while(buffer.remaining() > 0) { byte data = buffer.get(); result[(data & 0x0F)] ++; result[((data >> 4) & 0x0F)] ++; } int[] sortMap = Arrays.copyOf(result, result.length); Arrays.sort(sortMap); // データを調べる for(int j = 0;j < 16;j ++) { if(j == 0 || j == 15) { for(int i = 0;i < 16;i ++) { if(sortMap[j] == result[i]) { if(j == 0) { if(i == 6 || i == 7 || i == 0x0E || i == 0x0F) { continue; } // 最小値 logger.info("min:" + i); if(i != 6) { Bit val_6 = huffMap.get(0x6); Bit val_min = huffMap.get(i); huffMap.put(i, val_6); huffMap.put(6, val_min); } break; } else if(j == 15) { // 最大値 logger.info("max:" + i); if(i != 8) { Bit val_8 = huffMap.get(0x8); Bit val_max = huffMap.get(i); huffMap.put(i, val_8); huffMap.put(8, val_max); } } } } } } logger.info("huffMap:" + huffMap.toString()); // このデータで圧縮してみる。 buffer.position(4); Bit[] bits = new Bit[2040]; int i = 0; while(buffer.remaining() > 0) { int data = buffer.get(); bits[i ++] = huffMap.get(data & 0x0F); bits[i ++] = huffMap.get((data >> 4) & 0x0F); } BitConnector connector = new BitConnector(); ByteBuffer buf = connector.connect(bits); // buffer.position(0); if(buf.remaining() > 1020) { length += 1020; } else { length += buf.remaining(); } logger.info("length:" + buf.remaining()); } logger.info("total:" + length); } private static final Map<Integer, Bit> ttMap = new HashMap<Integer, Bit>(); { // type1 コンスタントに8%くらい減る /* ttMap.put(0x8, new Bit2(0x3)); ttMap.put(0x0, new Bit4(0xB)); ttMap.put(0x1, new Bit4(0xA)); ttMap.put(0x2, new Bit4(0x9)); ttMap.put(0x3, new Bit4(0x8)); ttMap.put(0x4, new Bit4(0x7)); ttMap.put(0x5, new Bit4(0x6)); ttMap.put(0x9, new Bit4(0x5)); ttMap.put(0xA, new Bit4(0x4)); ttMap.put(0xB, new Bit4(0x3)); ttMap.put(0xc, new Bit4(0x2)); ttMap.put(0xD, new Bit4(0x1)); ttMap.put(0x6, new Bit6(0x3)); ttMap.put(0x7, new Bit6(0x2)); ttMap.put(0xE, new Bit6(0x1)); ttMap.put(0xF, new Bit6(0x0)); // */ // type2 効くときは10%くらいいく /* ttMap.put(0x0, new Bit3(0x7)); ttMap.put(0x1, new Bit3(0x6)); ttMap.put(0x8, new Bit3(0x5)); ttMap.put(0x9, new Bit3(0x4)); ttMap.put(0x2, new Bit4(0x7)); ttMap.put(0x3, new Bit4(0x6)); ttMap.put(0x4, new Bit4(0x5)); ttMap.put(0xA, new Bit4(0x4)); ttMap.put(0xB, new Bit4(0x3)); ttMap.put(0xC, new Bit4(0x2)); ttMap.put(0x5, new Bit5(0x3)); ttMap.put(0xD, new Bit5(0x2)); ttMap.put(0x6, new Bit6(0x3)); ttMap.put(0x7, new Bit6(0x2)); ttMap.put(0xE, new Bit6(0x1)); ttMap.put(0xF, new Bit6(0x0)); // */ // type3(効くときには効くけど、全体としては、非常に圧縮率がわるくなる) /* ttMap.put(0x8, new Bit1(0x1)); ttMap.put(0x0, new Bit4(0x7)); ttMap.put(0x2, new Bit4(0x6)); ttMap.put(0xA, new Bit4(0x5)); ttMap.put(0x1, new Bit5(0x9)); ttMap.put(0x3, new Bit5(0x8)); ttMap.put(0x4, new Bit5(0x7)); ttMap.put(0x5, new Bit5(0x6)); ttMap.put(0x9, new Bit5(0x5)); ttMap.put(0xB, new Bit5(0x4)); ttMap.put(0xC, new Bit5(0x3)); ttMap.put(0xD, new Bit5(0x2)); ttMap.put(0x6, new Bit6(0x3)); ttMap.put(0x7, new Bit6(0x2)); ttMap.put(0xE, new Bit6(0x1)); ttMap.put(0xF, new Bit6(0x0)); // */ } }