Android flac to wav

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库

6.资料

flac

flac convert

githup

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值