Android flac to wav
flac格式的音频属于“无损压缩”,对于我们最常说的“无损音频”来说,一般都是指传统CD格式中的16bit/44.1kHz采样率的文件格式,而知所以称为无损压缩,也是因为其包含了20Hz-22.05kHz这个完全覆盖人耳可闻范围的频响频率而得名。
简介
FLAC即是FreeLossless Audio Codec的缩写,全称应该叫OGGFLAC,中文可解为无损音频压缩编码。它是OGG计划的一部分,当然也就是开源、免费的了,这也难怪它这么快就得到了多家MP3厂商的支持。
FLAC是一套著名的自由音频压缩编码,其特点是无损压缩。FLAC压缩比可以达到2:1,对于无损压缩来说,这已经是相当高的比例了;而且它解码速度快,只需进行整数运算即可完成整个解码过程,对CPU的运算能力要求很低,所以普通的随身听,都可以轻松实现实时解码。
不同于其他有损压缩编码如MP3 及AAC,它不会破坏任何原有的音频资讯,所以可以还原音乐光盘音质。现在它已被很多软件及硬件音频产品所支持。简而言之,FLAC与MP3相仿,但是是无损压缩的,也就是说音频以FLAC方式压缩不会丢失任何信息。这种压缩与Zip的方式类似,但是FLAC将给你更大的压缩比率,因为
FLAC是专门针对音频的特点设计的压缩方式,并且你可以使用播放器播放FLAC压缩的文件,就象通常播放你的MP3文件一样。
补充:为无损格式,较ape而言,他体积大点,但是兼容性好,编码速度快,播放器支持更广
flac与mp3的区别
音质不同:flac不会破坏任何原有的音频信息,可以还原音乐光盘音质。mp3旨在大大减少音频数据量,利用mpeg音频第三层技术,将音乐压缩成1:10甚至1:12的小文件。压缩比不同:flac是一种专为pcm音频特性而设计的压缩模式,可以使用播放器直接播放FLAC压缩的文件。mp3其优点是压缩后占用空间小,适用于移动设备的存储和使用。
1、音质不同:flac不会破坏任何原有的音频信息,可以还原音乐光盘音质。mp3旨在大大减少音频数据量,利用mpeg音频第三层技术,将音乐压缩成1:10甚至1:12的小文件。
2、音频不同:flac音频以FLAC编码压缩后不会丢失任何信息,将FLAC文件还原为WAV文件后,与压缩前的WAV文件内容相同。mp3利用人耳对高频声音信号不敏感的特性,将时域波形信号转换成频域信号,并划分成多个频段,对不同的频段使用不同的压缩率,对高频加大压缩比对低频信号使用小压缩比,保证信号不失真。
3、压缩比不同:flac是一种专为pcm音频特性而设计的压缩模式,可以使用播放器直接播放FLAC压缩的文件。mp3其优点是压缩后占用空间小,适用于移动设备的存储和使用。
使用FLAC-library-Java库来实现 flac与wav互转。
1.flac to wav
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.zip.DataFormatException;
public final class SimpleDecodeFlacToWav {
public static void main(String[] args) throws IOException, DataFormatException {
if (args.length != 2) {
System.err.println("Usage: java SimpleDecodeFlacToWav InFile.flac OutFile.wav");
System.exit(1);
return;
}
try (BitInputStream in = new BitInputStream(new BufferedInputStream(new FileInputStream(args[0])));
OutputStream out = new BufferedOutputStream(new FileOutputStream(args[1]))) {
decodeFile(in, out);
}
}
public static void decodeFile(BitInputStream in, OutputStream out) throws IOException, DataFormatException {
// Handle FLAC header and metadata blocks
if (in.readUint(32) != 0x664C6143)
throw new DataFormatException("Invalid magic string");
int sampleRate = -1;
int numChannels = -1;
int sampleDepth = -1;
long numSamples = -1;
for (boolean last = false; !last; ) {
last = in.readUint(1) != 0;
int type = in.readUint(7);
int length = in.readUint(24);
if (type == 0) { // Stream info block
in.readUint(16);
in.readUint(16);
in.readUint(24);
in.readUint(24);
sampleRate = in.readUint(20);
numChannels = in.readUint(3) + 1;
sampleDepth = in.readUint(5) + 1;
numSamples = (long)in.readUint(18) << 18 | in.readUint(18);
for (int i = 0; i < 16; i++)
in.readUint(8);
} else {
for (int i = 0; i < length; i++)
in.readUint(8);
}
}
if (sampleRate == -1)
throw new DataFormatException("Stream info metadata block absent");
if (sampleDepth % 8 != 0)
throw new RuntimeException("Sample depth not supported");
// Start writing WAV file headers
long sampleDataLen = numSamples * numChannels * (sampleDepth / 8);
writeString("RIFF", out);
writeLittleInt(4, (int)sampleDataLen + 36, out);
writeString("WAVE", out);
writeString("fmt ", out);
writeLittleInt(4, 16, out);
writeLittleInt(2, 0x0001, out);
writeLittleInt(2, numChannels, out);
writeLittleInt(4, sampleRate, out);
writeLittleInt(4, sampleRate * numChannels * (sampleDepth / 8), out);
writeLittleInt(2, numChannels * (sampleDepth / 8), out);
writeLittleInt(2, sampleDepth, out);
writeString("data", out);
writeLittleInt(4, (int)sampleDataLen, out);
// Decode FLAC audio frames and write raw samples
while (decodeFrame(in, numChannels, sampleDepth, out));
}
private static void writeLittleInt(int numBytes, int val, OutputStream out) throws IOException {
for (int i = 0; i < numBytes; i++)
out.write(val >>> (i * 8));
}
private static void writeString(String s, OutputStream out) throws IOException {
out.write(s.getBytes(StandardCharsets.UTF_8));
}
private static boolean decodeFrame(BitInputStream in, int numChannels, int sampleDepth, OutputStream out)
throws IOException, DataFormatException {
// Read a ton of header fields, and ignore most of them
int temp = in.readByte();
if (temp == -1)
return false;
int sync = temp << 6 | in.readUint(6);
if (sync != 0x3FFE)
throw new DataFormatException("Sync code expected");
in.readUint(1);
in.readUint(1);
int blockSizeCode = in.readUint(4);
int sampleRateCode = in.readUint(4);
int chanAsgn = in.readUint(4);
in.readUint(3);
in.readUint(1);
temp = Integer.numberOfLeadingZeros(~(in.readUint(8) << 24)) - 1;
for (int i = 0; i < temp; i++)
in.readUint(8);
int blockSize;
if (blockSizeCode == 1)
blockSize = 192;
else if (2 <= blockSizeCode && blockSizeCode <= 5)
blockSize = 576 << (blockSizeCode - 2);
else if (blockSizeCode == 6)
blockSize = in.readUint(8) + 1;
else if (blockSizeCode == 7)
blockSize = in.readUint(16) + 1;
else if (8 <= blockSizeCode && blockSizeCode <= 15)
blockSize = 256 << (blockSizeCode - 8);
else
throw new DataFormatException("Reserved block size");
if (sampleRateCode == 12)
in.readUint(8);
else if (sampleRateCode == 13 || sampleRateCode == 14)
in.readUint(16);
in.readUint(8);
// Decode each channel's subframe, then skip footer
int[][] samples = new int[numChannels][blockSize];
decodeSubframes(in, sampleDepth, chanAsgn, samples);
in.alignToByte();
in.readUint(16);
// Write the decoded samples
for (int i = 0; i < blockSize; i++) {
for (int j = 0; j < numChannels; j++) {
int val = samples[j][i];
if (sampleDepth == 8)
val += 128;
writeLittleInt(sampleDepth / 8, val, out);
}
}
return true;
}
private static void decodeSubframes(BitInputStream in, int sampleDepth, int chanAsgn, int[][] result)
throws IOException, DataFormatException {
int blockSize = result[0].length;
long[][] subframes = new long[result.length][blockSize];
if (0 <= chanAsgn && chanAsgn <= 7) {
for (int ch = 0; ch < result.length; ch++)
decodeSubframe(in, sampleDepth, subframes[ch]);
} else if (8 <= chanAsgn && chanAsgn <= 10) {
decodeSubframe(in, sampleDepth + (chanAsgn == 9 ? 1 : 0), subframes[0]);
decodeSubframe(in, sampleDepth + (chanAsgn == 9 ? 0 : 1), subframes[1]);
if (chanAsgn == 8) {
for (int i = 0; i < blockSize; i++)
subframes[1][i] = subframes[0][i] - subframes[1][i];
} else if (chanAsgn == 9) {
for (int i = 0; i < blockSize; i++)
subframes[0][i] += subframes[1][i];
} else if (chanAsgn == 10) {
for (int i = 0; i < blockSize; i++) {
long side = subframes[1][i];
long right = subframes[0][i] - (side >> 1);
subframes[1][i] = right;
subframes[0][i] = right + side;
}
}
} else
throw new DataFormatException("Reserved channel assignment");
for (int ch = 0; ch < result.length; ch++) {
for (int i = 0; i < blockSize; i++)
result[ch][i] = (int)subframes[ch][i];
}
}
private static void decodeSubframe(BitInputStream in, int sampleDepth, long[] result)
throws IOException, DataFormatException {
in.readUint(1);
int type = in.readUint(6);
int shift = in.readUint(1);
if (shift == 1) {
while (in.readUint(1) == 0)
shift++;
}
sampleDepth -= shift;
if (type == 0) // Constant coding
Arrays.fill(result, 0, result.length, in.readSignedInt(sampleDepth));
else if (type == 1) { // Verbatim coding
for (int i = 0; i < result.length; i++)
result[i] = in.readSignedInt(sampleDepth);
} else if (8 <= type && type <= 12)
decodeFixedPredictionSubframe(in, type - 8, sampleDepth, result);
else if (32 <= type && type <= 63)
decodeLinearPredictiveCodingSubframe(in, type - 31, sampleDepth, result);
else
throw new DataFormatException("Reserved subframe type");
for (int i = 0; i < result.length; i++)
result[i] <<= shift;
}
private static void decodeFixedPredictionSubframe(BitInputStream in, int predOrder, int sampleDepth, long[] result)
throws IOException, DataFormatException {
for (int i = 0; i < predOrder; i++)
result[i] = in.readSignedInt(sampleDepth);
decodeResiduals(in, predOrder, result);
restoreLinearPrediction(result, FIXED_PREDICTION_COEFFICIENTS[predOrder], 0);
}
private static final int[][] FIXED_PREDICTION_COEFFICIENTS = {
{},
{1},
{2, -1},
{3, -3, 1},
{4, -6, 4, -1},
};
private static void decodeLinearPredictiveCodingSubframe(BitInputStream in, int lpcOrder, int sampleDepth, long[] result)
throws IOException, DataFormatException {
for (int i = 0; i < lpcOrder; i++)
result[i] = in.readSignedInt(sampleDepth);
int precision = in.readUint(4) + 1;
int shift = in.readSignedInt(5);
int[] coefs = new int[lpcOrder];
for (int i = 0; i < coefs.length; i++)
coefs[i] = in.readSignedInt(precision);
decodeResiduals(in, lpcOrder, result);
restoreLinearPrediction(result, coefs, shift);
}
private static void decodeResiduals(BitInputStream in, int warmup, long[] result) throws IOException, DataFormatException {
int method = in.readUint(2);
if (method >= 2)
throw new DataFormatException("Reserved residual coding method");
int paramBits = method == 0 ? 4 : 5;
int escapeParam = method == 0 ? 0xF : 0x1F;
int partitionOrder = in.readUint(4);
int numPartitions = 1 << partitionOrder;
if (result.length % numPartitions != 0)
throw new DataFormatException("Block size not divisible by number of Rice partitions");
int partitionSize = result.length / numPartitions;
for (int i = 0; i < numPartitions; i++) {
int start = i * partitionSize + (i == 0 ? warmup : 0);
int end = (i + 1) * partitionSize;
int param = in.readUint(paramBits);
if (param < escapeParam) {
for (int j = start; j < end; j++)
result[j] = in.readRiceSignedInt(param);
} else {
int numBits = in.readUint(5);
for (int j = start; j < end; j++)
result[j] = in.readSignedInt(numBits);
}
}
}
private static void restoreLinearPrediction(long[] result, int[] coefs, int shift) {
for (int i = coefs.length; i < result.length; i++) {
long sum = 0;
for (int j = 0; j < coefs.length; j++)
sum += result[i - 1 - j] * coefs[j];
result[i] += sum >> shift;
}
}
}
final class BitInputStream implements AutoCloseable {
private InputStream in;
private long bitBuffer;
private int bitBufferLen;
public BitInputStream(InputStream in) {
this.in = in;
}
public void alignToByte() {
bitBufferLen -= bitBufferLen % 8;
}
public int readByte() throws IOException {
if (bitBufferLen >= 8)
return readUint(8);
else
return in.read();
}
public int readUint(int n) throws IOException {
while (bitBufferLen < n) {
int temp = in.read();
if (temp == -1)
throw new EOFException();
bitBuffer = (bitBuffer << 8) | temp;
bitBufferLen += 8;
}
bitBufferLen -= n;
int result = (int)(bitBuffer >>> bitBufferLen);
if (n < 32)
result &= (1 << n) - 1;
return result;
}
public int readSignedInt(int n) throws IOException {
return (readUint(n) << (32 - n)) >> (32 - n);
}
public long readRiceSignedInt(int param) throws IOException {
long val = 0;
while (readUint(1) == 0)
val++;
val = (val << param) | readUint(param);
return (val >>> 1) ^ -(val & 1);
}
public void close() throws IOException {
in.close();
}
}
2. wav to flac
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.zip.DataFormatException;
public final class SimpleEncodeWavToFlac {
public static void main(String[] args) throws IOException, DataFormatException {
if (args.length != 2) {
System.err.println("Usage: java SimpleEncodeWavToFlac InFile.wav OutFile.flac");
System.exit(1);
return;
}
try (InputStream in = new BufferedInputStream(new FileInputStream(args[0]));
BitOutputStream out = new BitOutputStream(new BufferedOutputStream(new FileOutputStream(args[1])))) {
encodeFile(in, out);
}
}
public static void encodeFile(InputStream in, BitOutputStream out) throws IOException, DataFormatException {
// Read and parse WAV file headers
if (!readString(in, 4).equals("RIFF"))
throw new DataFormatException("Invalid RIFF file header");
readLittleInt(in, 4);
if (!readString(in, 4).equals("WAVE"))
throw new DataFormatException("Invalid WAV file header");
if (!readString(in, 4).equals("fmt "))
throw new DataFormatException("Unrecognized WAV file chunk");
if (readLittleInt(in, 4) != 16)
throw new DataFormatException("Unsupported WAV file type");
if (readLittleInt(in, 2) != 0x0001)
throw new DataFormatException("Unsupported WAV file codec");
int numChannels = readLittleInt(in, 2);
if (numChannels < 0 || numChannels > 8)
throw new RuntimeException("Too many (or few) audio channels");
int sampleRate = readLittleInt(in, 4);
if (sampleRate <= 0 || sampleRate >= (1 << 20))
throw new RuntimeException("Sample rate too large or invalid");
readLittleInt(in, 4);
readLittleInt(in, 2);
int sampleDepth = readLittleInt(in, 2);
if (sampleDepth == 0 || sampleDepth > 32 || sampleDepth % 8 != 0)
throw new RuntimeException("Unsupported sample depth");
if (!readString(in, 4).equals("data"))
throw new DataFormatException("Unrecognized WAV file chunk");
int sampleDataLen = readLittleInt(in, 4);
if (sampleDataLen <= 0 || sampleDataLen % (numChannels * (sampleDepth / 8)) != 0)
throw new DataFormatException("Invalid length of audio sample data");
// Start writing FLAC file header and stream info metadata block
out.writeInt(32, 0x664C6143);
out.writeInt(1, 1);
out.writeInt(7, 0);
out.writeInt(24, 34);
out.writeInt(16, BLOCK_SIZE);
out.writeInt(16, BLOCK_SIZE);
out.writeInt(24, 0);
out.writeInt(24, 0);
out.writeInt(20, sampleRate);
out.writeInt(3, numChannels - 1);
out.writeInt(5, sampleDepth - 1);
int numSamples = sampleDataLen / (numChannels * (sampleDepth / 8));
out.writeInt(18, numSamples >>> 18);
out.writeInt(18, numSamples >>> 0);
for (int i = 0; i < 16; i++)
out.writeInt(8, 0);
// Read raw samples and encode FLAC audio frames
for (int i = 0; numSamples > 0; i++) {
int blockSize = Math.min(numSamples, BLOCK_SIZE);
encodeFrame(in, i, numChannels, sampleDepth, sampleRate, blockSize, out);
numSamples -= blockSize;
}
}
private static final int BLOCK_SIZE = 4096;
private static String readString(InputStream in, int len) throws IOException {
byte[] temp = new byte[len];
for (int i = 0; i < temp.length; i++) {
int b = in.read();
if (b == -1)
throw new EOFException();
temp[i] = (byte)b;
}
return new String(temp, StandardCharsets.UTF_8);
}
private static int readLittleInt(InputStream in, int n) throws IOException {
int result = 0;
for (int i = 0; i < n; i++) {
int b = in.read();
if (b == -1)
throw new EOFException();
result |= b << (i * 8);
}
return result;
}
private static void encodeFrame(InputStream in, int frameIndex, int numChannels, int sampleDepth, int sampleRate, int blockSize, BitOutputStream out) throws IOException {
int[][] samples = new int[numChannels][blockSize];
int bytesPerSample = sampleDepth / 8;
for (int i = 0; i < blockSize; i++) {
for (int ch = 0; ch < numChannels; ch++) {
int val = 0;
for (int j = 0; j < bytesPerSample; j++) {
int b = in.read();
if (b == -1)
throw new EOFException();
val |= b << (j * 8);
}
if (sampleDepth == 8)
samples[ch][i] = val - 128;
else
samples[ch][i] = (val << (32 - sampleDepth)) >> (32 - sampleDepth);
}
}
out.resetCrcs();
out.writeInt(14, 0x3FFE);
out.writeInt(1, 0);
out.writeInt(1, 0);
out.writeInt(4, 7);
out.writeInt(4, sampleRate % 10 == 0 ? 14 : 13);
out.writeInt(4, numChannels - 1);
switch (sampleDepth) {
case 8: out.writeInt(3, 1); break;
case 16: out.writeInt(3, 4); break;
case 24: out.writeInt(3, 6); break;
case 32: out.writeInt(3, 0); break;
default: throw new IllegalArgumentException();
}
out.writeInt(1, 0);
out.writeInt(8, 0xFC | (frameIndex >>> 30));
for (int i = 24; i >= 0; i -= 6)
out.writeInt(8, 0x80 | ((frameIndex >>> i) & 0x3F));
out.writeInt(16, blockSize - 1);
out.writeInt(16, sampleRate / (sampleRate % 10 == 0 ? 10 : 1));
out.writeInt(8, out.crc8);
for (int[] chanSamples : samples)
encodeSubframe(chanSamples, sampleDepth, out);
out.alignToByte();
out.writeInt(16, out.crc16);
}
private static void encodeSubframe(int[] samples, int sampleDepth, BitOutputStream out) throws IOException {
out.writeInt(1, 0);
out.writeInt(6, 1); // Verbatim coding
out.writeInt(1, 0);
for (int x : samples)
out.writeInt(sampleDepth, x);
}
}
final class BitOutputStream implements AutoCloseable {
private OutputStream out;
private long bitBuffer;
private int bitBufferLen;
public int crc8;
public int crc16;
public BitOutputStream(OutputStream out) {
this.out = out;
bitBuffer = 0;
bitBufferLen = 0;
resetCrcs();
}
public void resetCrcs() {
crc8 = 0;
crc16 = 0;
}
public void alignToByte() throws IOException {
writeInt((64 - bitBufferLen) % 8, 0);
}
public void writeInt(int n, int val) throws IOException {
bitBuffer = (bitBuffer << n) | (val & ((1L << n) - 1));
bitBufferLen += n;
while (bitBufferLen >= 8) {
bitBufferLen -= 8;
int b = (int)(bitBuffer >>> bitBufferLen) & 0xFF;
out.write(b);
crc8 ^= b;
crc16 ^= b << 8;
for (int i = 0; i < 8; i++) {
crc8 = (crc8 << 1) ^ ((crc8 >>> 7) * 0x107);
crc16 = (crc16 << 1) ^ ((crc16 >>> 15) * 0x18005);
}
}
}
public void close() throws IOException {
out.close();
}
}
3.wav 2 flac
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.zip.DataFormatException;
public final class DecodeFlacResidueToWav {
public static void main(String[] args) throws IOException, DataFormatException {
if (args.length != 2) {
System.err.println("Usage: java DecodeFlacResidueToWav InFile.flac OutFile.wav");
System.exit(1);
return;
}
try (BitInputStream in = new BitInputStream(new BufferedInputStream(new FileInputStream(args[0])));
OutputStream out = new BufferedOutputStream(new FileOutputStream(args[1]))) {
decodeFile(in, out);
}
}
public static void decodeFile(BitInputStream in, OutputStream out) throws IOException, DataFormatException {
// Handle FLAC header and metadata blocks
if (in.readUint(32) != 0x664C6143)
throw new DataFormatException("Invalid magic string");
int sampleRate = -1;
int numChannels = -1;
int sampleDepth = -1;
long numSamples = -1;
for (boolean last = false; !last; ) {
last = in.readUint(1) != 0;
int type = in.readUint(7);
int length = in.readUint(24);
if (type == 0) { // Stream info block
in.readUint(16);
in.readUint(16);
in.readUint(24);
in.readUint(24);
sampleRate = in.readUint(20);
numChannels = in.readUint(3) + 1;
sampleDepth = in.readUint(5) + 1;
numSamples = (long)in.readUint(18) << 18 | in.readUint(18);
for (int i = 0; i < 16; i++)
in.readUint(8);
} else {
for (int i = 0; i < length; i++)
in.readUint(8);
}
}
if (sampleRate == -1)
throw new DataFormatException("Stream info metadata block absent");
if (sampleDepth % 8 != 0)
throw new RuntimeException("Sample depth not supported");
// Start writing WAV file headers
long sampleDataLen = numSamples * numChannels * (sampleDepth / 8);
writeString("RIFF", out);
writeLittleInt(4, (int)sampleDataLen + 36, out);
writeString("WAVE", out);
writeString("fmt ", out);
writeLittleInt(4, 16, out);
writeLittleInt(2, 0x0001, out);
writeLittleInt(2, numChannels, out);
writeLittleInt(4, sampleRate, out);
writeLittleInt(4, sampleRate * numChannels * (sampleDepth / 8), out);
writeLittleInt(2, numChannels * (sampleDepth / 8), out);
writeLittleInt(2, sampleDepth, out);
writeString("data", out);
writeLittleInt(4, (int)sampleDataLen, out);
// Decode FLAC audio frames and write raw samples
while (decodeFrame(in, numChannels, sampleDepth, out));
}
private static void writeLittleInt(int numBytes, int val, OutputStream out) throws IOException {
for (int i = 0; i < numBytes; i++)
out.write(val >>> (i * 8));
}
private static void writeString(String s, OutputStream out) throws IOException {
out.write(s.getBytes(StandardCharsets.UTF_8));
}
private static boolean decodeFrame(BitInputStream in, int numChannels, int sampleDepth, OutputStream out) throws IOException, DataFormatException {
// Read a ton of header fields, and ignore most of them
int temp = in.readByte();
if (temp == -1)
return false;
int sync = temp << 6 | in.readUint(6);
if (sync != 0x3FFE)
throw new DataFormatException("Sync code expected");
in.readUint(1);
in.readUint(1);
int blockSizeCode = in.readUint(4);
int sampleRateCode = in.readUint(4);
int chanAsgn = in.readUint(4);
in.readUint(3);
in.readUint(1);
temp = Integer.numberOfLeadingZeros(~(in.readUint(8) << 24)) - 1;
for (int i = 0; i < temp; i++)
in.readUint(8);
int blockSize;
if (blockSizeCode == 1)
blockSize = 192;
else if (2 <= blockSizeCode && blockSizeCode <= 5)
blockSize = 576 << (blockSizeCode - 2);
else if (blockSizeCode == 6)
blockSize = in.readUint(8) + 1;
else if (blockSizeCode == 7)
blockSize = in.readUint(16) + 1;
else if (8 <= blockSizeCode && blockSizeCode <= 15)
blockSize = 256 << (blockSizeCode - 8);
else
throw new DataFormatException("Reserved block size");
if (sampleRateCode == 12)
in.readUint(8);
else if (sampleRateCode == 13 || sampleRateCode == 14)
in.readUint(16);
in.readUint(8);
// Decode each channel's subframe, then skip footer
int[][] samples = new int[numChannels][blockSize];
decodeSubframes(in, sampleDepth, chanAsgn, samples);
in.alignToByte();
in.readUint(16);
// Write the decoded samples
for (int i = 0; i < blockSize; i++) {
for (int j = 0; j < numChannels; j++) {
int val = samples[j][i];
if (sampleDepth == 8)
val += 128;
writeLittleInt(sampleDepth / 8, val, out);
}
}
return true;
}
private static void decodeSubframes(BitInputStream in, int sampleDepth, int chanAsgn, int[][] result) throws IOException, DataFormatException {
int blockSize = result[0].length;
long[][] subframes = new long[result.length][blockSize];
if (0 <= chanAsgn && chanAsgn <= 7) {
for (int ch = 0; ch < result.length; ch++)
decodeSubframe(in, sampleDepth, subframes[ch]);
} else if (8 <= chanAsgn && chanAsgn <= 10) {
decodeSubframe(in, sampleDepth + (chanAsgn == 9 ? 1 : 0), subframes[0]);
decodeSubframe(in, sampleDepth + (chanAsgn == 9 ? 0 : 1), subframes[1]);
} else
throw new DataFormatException("Reserved channel assignment");
for (int ch = 0; ch < result.length; ch++) {
for (int i = 0; i < blockSize; i++)
result[ch][i] = (int)subframes[ch][i];
}
}
private static void decodeSubframe(BitInputStream in, int sampleDepth, long[] result) throws IOException, DataFormatException {
in.readUint(1);
int type = in.readUint(6);
int shift = in.readUint(1);
if (shift == 1) {
while (in.readUint(1) == 0)
shift++;
}
sampleDepth -= shift;
if (type == 0) // Constant coding
in.readSignedInt(sampleDepth);
else if (type == 1) { // Verbatim coding
for (int i = 0; i < result.length; i++)
in.readSignedInt(sampleDepth);
} else if (8 <= type && type <= 12)
decodeFixedPredictionSubframe(in, type - 8, sampleDepth, result);
else if (32 <= type && type <= 63)
decodeLinearPredictiveCodingSubframe(in, type - 31, sampleDepth, result);
else
throw new DataFormatException("Reserved subframe type");
for (int i = 0; i < result.length; i++)
result[i] <<= shift;
}
private static void decodeFixedPredictionSubframe(BitInputStream in, int predOrder, int sampleDepth, long[] result) throws IOException, DataFormatException {
for (int i = 0; i < predOrder; i++)
in.readSignedInt(sampleDepth);
decodeResiduals(in, predOrder, result);
}
private static void decodeLinearPredictiveCodingSubframe(BitInputStream in, int lpcOrder, int sampleDepth, long[] result) throws IOException, DataFormatException {
for (int i = 0; i < lpcOrder; i++)
in.readSignedInt(sampleDepth);
int precision = in.readUint(4) + 1;
in.readSignedInt(5);
for (int i = 0; i < lpcOrder; i++)
in.readSignedInt(precision);
decodeResiduals(in, lpcOrder, result);
}
private static void decodeResiduals(BitInputStream in, int warmup, long[] result) throws IOException, DataFormatException {
int method = in.readUint(2);
if (method >= 2)
throw new DataFormatException("Reserved residual coding method");
int paramBits = method == 0 ? 4 : 5;
int escapeParam = method == 0 ? 0xF : 0x1F;
int partitionOrder = in.readUint(4);
int numPartitions = 1 << partitionOrder;
if (result.length % numPartitions != 0)
throw new DataFormatException("Block size not divisible by number of Rice partitions");
int partitionSize = result.length / numPartitions;
for (int i = 0; i < numPartitions; i++) {
int start = i * partitionSize + (i == 0 ? warmup : 0);
int end = (i + 1) * partitionSize;
int param = in.readUint(paramBits);
if (param < escapeParam) {
for (int j = start; j < end; j++)
result[j] = in.readRiceSignedInt(param);
} else {
int numBits = in.readUint(5);
for (int j = start; j < end; j++)
result[j] = in.readSignedInt(numBits);
}
}
}
}
final class BitInputStream implements AutoCloseable {
private InputStream in;
private long bitBuffer;
private int bitBufferLen;
public BitInputStream(InputStream in) {
this.in = in;
}
public void alignToByte() {
bitBufferLen -= bitBufferLen % 8;
}
public int readByte() throws IOException {
if (bitBufferLen >= 8)
return readUint(8);
else
return in.read();
}
public int readUint(int n) throws IOException {
while (bitBufferLen < n) {
int temp = in.read();
if (temp == -1)
throw new EOFException();
bitBuffer = (bitBuffer << 8) | temp;
bitBufferLen += 8;
}
bitBufferLen -= n;
int result = (int)(bitBuffer >>> bitBufferLen);
if (n < 32)
result &= (1 << n) - 1;
return result;
}
public int readSignedInt(int n) throws IOException {
return (readUint(n) << (32 - n)) >> (32 - n);
}
public long readRiceSignedInt(int param) throws IOException {
long val = 0;
while (readUint(1) == 0)
val++;
val = (val << param) | readUint(param);
return (val >>> 1) ^ -(val & 1);
}
public void close() throws IOException {
in.close();
}
}
4.小结
wav2flac的过程结束后,flac文件的体积没啥变化,没有压缩。可以播放。
5.使用完整的jar包
wav2flac的过程结束后,flac文件的体积会变小。可以播放。详情参考githup库