/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.media.mp3.frame; import java.nio.ByteBuffer; import com.ttProject.media.IAudioData; import com.ttProject.media.extra.Bit1; import com.ttProject.media.extra.Bit2; import com.ttProject.media.extra.Bit3; import com.ttProject.media.extra.Bit4; import com.ttProject.media.extra.Bit8; import com.ttProject.media.extra.BitConnector; import com.ttProject.media.mp3.Frame; import com.ttProject.media.mp3.IFrameAnalyzer; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.util.BufferUtil; /** * mp3のデータフレーム * @author taktod */ public class Mp3 extends Frame implements IAudioData { private final short syncBit = (short)0x07FF; private Bit2 mpegVersion; private Bit2 layer; private Bit1 protectionBit; private Bit4 bitrateIndex; private Bit2 samplingRateIndex; private Bit1 paddingBit; private Bit1 privateBit; private Bit2 channelMode; private Bit2 modeExtension; private Bit1 copyRight; private Bit1 originalFlg; private Bit2 emphasis; /** 保持している実データ部 */ private ByteBuffer data; private final int bitrateIndexV1L1[] = { -1, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }; private final int bitrateIndexV1L2[] = { -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }; private final int bitrateIndexV1L3[] = { -1, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }; private final int bitrateIndexV2L1[] = { -1, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }; private final int bitrateIndexV2L23[] = { -1, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }; private final float sampleRateTable[][] = { {11.025f, 12.0f, 8.0f}, // mpeg 2.5 {-1.0f, -1.0f, -1.0f}, // reserved {22.05f, 24.0f, 16.0f}, // mpeg 2 {44.1f, 48.0f, 32.0f} // mpeg 1 }; /** * サンプリングレートを応答します。 * @return */ public float getSamplingRate() { return sampleRateTable[mpegVersion.get()][samplingRateIndex.get()]; } @Override public int getSampleRate() { return (int)(getSamplingRate() *1000); } /** * フレームあたりのサンプル数を応答します。 * @return */ @Override public int getSampleNum() { if(layer.get() == 3) { // layer1 return 384; } else if(layer.get() == 2) { // layer2 return 1152; } else if(layer.get() == 1) { // layer3 if(mpegVersion.get() == 3) { // version1 return 1152; } else { return 576; } } return 0; } // videoData用の拡張動作 private long pts = 0; private long dts = 0; private double timebase = 0.001; public void setPts(long pts) { this.pts = pts; } public long getPts() { return pts; } public void setDts(long dts) { this.dts = dts; } public long getDts() { return dts; } public void setTimebase(double timebase) { this.timebase = timebase; } public double getTimebase() { return timebase; } @Override public ByteBuffer getRawData() throws Exception { return getBuffer(); } /** * チャンネルモードを応答します。 * @return */ public int getChannelMode() { return channelMode.get(); } /** * チャンネル数を応答します。 * @return */ public int getChannels() { return channelMode.get() == 3 ? 1 : 2; } public int getBitrate() { if(mpegVersion.get() == 0 || mpegVersion.get() == 2) { // 2.5と2の場合 if(layer.get() == 3) { // layer1 return bitrateIndexV2L1[bitrateIndex.get()]; } else if(layer.get() == 2 || layer.get() == 1) { // layer2,3 return bitrateIndexV2L23[bitrateIndex.get()]; } } if(mpegVersion.get() == 3) { // 1の場合 if(layer.get() == 1) { // layer3 return bitrateIndexV1L3[bitrateIndex.get()]; } else if(layer.get() == 2) { // layer2 return bitrateIndexV1L2[bitrateIndex.get()]; } else if(layer.get() == 3) { // layer1 return bitrateIndexV1L1[bitrateIndex.get()]; } } return -1; } private int getRealSize() { if(layer.get() == 3) { // layer1 return (int)Math.floor((12 * getBitrate() / getSamplingRate() + paddingBit.get()) * 4); } else if(layer.get() == 2) { // layer2 return (int)Math.floor(144 * getBitrate() / getSamplingRate() + paddingBit.get()); } else if(layer.get() == 1) { // layer3 if(mpegVersion.get() == 3) { // version1の場合 return (int)Math.floor(144 * getBitrate() / getSamplingRate() + paddingBit.get()); } else { return (int)Math.floor(72 * getBitrate() / getSamplingRate() + paddingBit.get()); } } return -1; } /** * コンストラクタ * TODO 3つつくる。設定で変わるデータをベースにつくるもの。なにもなしでの処理、ファイルからの読み込みデータをベースにした処理 */ public Mp3() { super(0, 0); } public Mp3(int position, Bit2 mpegVersion, Bit2 layer, Bit1 protectionBit, Bit4 bitrateIndex, Bit2 samplingRateIndex, Bit1 paddingBit, Bit1 privateBit, Bit2 channelMode, Bit2 modeExtension, Bit1 copyRight, Bit1 originalFlg, Bit2 emphasis) { super(position, 0); this.mpegVersion = mpegVersion; this.layer = layer; this.protectionBit = protectionBit; this.bitrateIndex = bitrateIndex; this.samplingRateIndex = samplingRateIndex; this.paddingBit = paddingBit; this.privateBit = privateBit; this.channelMode = channelMode; this.modeExtension = modeExtension; this.copyRight = copyRight; this.originalFlg = originalFlg; this.emphasis = emphasis; setSize(getRealSize()); } public void setData(ByteBuffer buffer) { this.data = buffer.duplicate(); } public ByteBuffer getBuffer() throws Exception { ByteBuffer buffer = ByteBuffer.allocate(getSize()); BitConnector bitConnector = new BitConnector(); buffer.put(bitConnector.connect( new Bit3((byte)(syncBit >>> 8)), new Bit8((byte)(syncBit)), mpegVersion, layer, protectionBit, bitrateIndex, samplingRateIndex, paddingBit, privateBit, channelMode, modeExtension, copyRight, originalFlg, emphasis)); buffer.put(data); buffer.flip(); return buffer; } @Override public void analyze(IReadChannel ch, IFrameAnalyzer analyzer) throws Exception { ch.position(getPosition() + 4); data = BufferUtil.safeRead(ch, getSize() - 4); } @Override public String toString() { StringBuilder data = new StringBuilder("mp3Frame:"); data.append(" "); data.append(getSamplingRate()); data.append("kHz"); data.append(" pos:"); data.append(Integer.toHexString(getPosition())); data.append(" size:"); data.append(Integer.toHexString(getSize())); return data.toString(); } }