/// <summary> /// Try to read the blocksize from previous frame /// </summary> /// <param name="stream"></param> /// <param name="headerStartPos"></param> /// <returns></returns> private ulong ReadPreviousFrameBlocksize(Stream stream, StreamInfo info, int headerStartPos) { ulong ret = 0; long strPos = stream.Position; stream.Position = headerStartPos; try { SyncronizeLeft(stream, info); ParseHeader(stream,info); ret = Blocksize; } catch (LostSynchronizationException) { } stream.Position = strPos; return ret; }
public static Metadata Decode(Stream stream) { byte header; header = (byte) stream.ReadByte(); bool lastBlock; lastBlock = (header & 0x80) == 0x80; if (lastBlock) { header ^= 0x80; } BlockType type = (BlockType) header; int buff; int len = 0; for (int i = 0; i < 3; i++) { len = len << 8; buff = stream.ReadByte(); len |= buff; } byte[] payload = new byte[len]; for (int i = 0; i < len; i++) { payload[i] = (byte) stream.ReadByte(); } Metadata instance; switch (type) { case BlockType.STREAMINFO: instance = new StreamInfo(); break; case BlockType.APPLICATION: instance = new Application(); break; case BlockType.CUESHEET: instance = new CueSheet(); break; case BlockType.PADDING: instance = new Padding(); break; case BlockType.PICTURE: instance = new Picture(); break; case BlockType.SEEKTABLE: instance = new SeekTable(); break; case BlockType.UNKNOWN: instance = new Unknown(); break; case BlockType.VORBIS_COMMENT: instance = new VorbisComment(); break; default: instance = new Reserved(); break; } instance.isLastBlock = lastBlock; instance.length = len; instance.type = type; instance.Parse(payload); return instance; }
internal void ParseHeader(Stream stream, StreamInfo streamInfo) { ushort buffer; int b; //to check EOS int additionalBlockSize = 0; //number of additional bytes at the end of the header that represent its length int sampleRateBytes = 0; //number of additional bytes at the end of the header that store the sample rate int sampleRateMultiplicator = 1; //scale of the stored sample rate buffer = Org.Nflac.Flac.Util.StreamReader.ReadUShort(stream); byte[] rawHeader = new byte[16]; byte headerLen = 2; rawHeader[0] = (byte)(buffer >> 8); rawHeader[1] = (byte)(buffer & 0xff); if ((buffer & 0xfff8) != 0xfff8) { throw new LostSynchronizationException(); //TODO: desync } else { //Console.WriteLine("SYNC: " + (stream.Position-2)); } if ((buffer & 0x4) != 0) { throw new MalformedFileException("Reserved bit at the header"); } variableLength = (buffer & 0x1) == 0x1; byte size; buffer = Org.Nflac.Flac.Util.StreamReader.ReadUShort(stream); rawHeader[headerLen++] = (byte)(buffer >> 8); rawHeader[headerLen++] = (byte)(buffer & 0xff); size = (byte)((buffer & 0xF000) >> 12); //higher 4 bits if (size == 0x0) { //reserved block-size throw new MalformedFileException("Reserved blocksize detected"); } else if (size == 0x1) { blocksize = 192; } else if ((size > 0x1) && (size < 0x6)) { blocksize = (ulong)(576 << (size - 2)); } else if (size == 0x6) { additionalBlockSize = 1; //get additional byte from the end of the header } else if (size == 0x7) { additionalBlockSize = 2; //get 2 bytes from the end of the header } else if ((size > 0x7) && (size < 0x11)) { blocksize = (ulong)(256 << (size - 8)); } else { //TODO: handle reserved blocksize value throw new MalformedFileException("Reserved blocksize value detected"); } byte rateCoded; rateCoded = (byte)((buffer & 0x0F00) >> 8); //next 4 bits switch (rateCoded) { case 0: //get from STREAMINFO break; case 1: sampleRate = 88200; break; case 2: sampleRate = 176400; break; case 3: sampleRate = 192000; break; case 4: sampleRate = 8000; break; case 5: sampleRate = 16000; break; case 6: sampleRate = 22050; break; case 7: sampleRate = 24000; break; case 8: sampleRate = 32000; break; case 9: sampleRate = 44100; break; case 10: sampleRate = 48000; break; case 11: sampleRate = 96000; break; case 12: sampleRate = 0; sampleRateBytes = 1; sampleRateMultiplicator = 1000; break; case 13: sampleRate = 0; sampleRateBytes = 2; sampleRateMultiplicator = 1; break; case 14: sampleRate = 0; sampleRateBytes = 2; sampleRateMultiplicator = 10; break; case 15: throw new MalformedFileException("Reserved sample rate value"); //handle reserved sample rate value } byte channelSetupCoded; channelSetupCoded = (byte)((buffer & 0x00F0) >> 4); //next 4 bits if (channelSetupCoded < 8) { if ((channelSetupCoded == 6) || (channelSetupCoded == 7)) { //some specific code for 7- and 8- channel music might be needed (those do not have appropriate channel assignment in the standard) } channels = (byte)(channelSetupCoded + 1); channelSetup = (ChannelAssignment)channelSetupCoded; } else if ((channelSetupCoded > 7) && (channelSetupCoded < 11)) { //stereo channels = 2; channelSetup = (ChannelAssignment)channelSetupCoded; } else { throw new MalformedFileException("Reserved channel assignment used"); //TODO: handle reserved channel assignment values } byte sampleSizeCoded; sampleSizeCoded = (byte)((buffer & 0x000E) >> 1); //next 3 bits switch (sampleSizeCoded) { case 0: //get from STREAMINFO break; case 1: sampleSize = 8; break; case 2: sampleSize = 12; break; case 3: throw new MalformedFileException("Reserved sample size"); case 4: sampleSize = 16; break; case 5: sampleSize = 20; break; case 6: sampleSize = 24; break; case 7: //reserved value 2 handling throw new MalformedFileException("Reserved sample size detected"); } byte mandatoryField2; mandatoryField2 = (byte)(buffer & 0x0001); //last 1 bit if (mandatoryField2 != 0) { //TODO: handle mandatory value 2 violation throw new MalformedFileException("Mandatory value violated"); } //handle UTF-8-like number frameNumber = ReadUTF8Number(stream, rawHeader, ref headerLen); //handle additional bytes for blocksize for (int i = 0; i < additionalBlockSize; i++) { blocksize = (ulong)(blocksize << 8); buffer = (ushort)stream.ReadByte(); rawHeader[headerLen++] = (byte)buffer; blocksize |= buffer; } if (!variableLength) { if (frameNumber * streamInfo.MaximumBlockSize + blocksize > streamInfo.TotalSamples) { blocksize = (streamInfo.TotalSamples - frameNumber * streamInfo.MaximumBlockSize); } /* f*****g ugly hack, but spec gives no better way to detect the last frame */ if (blocksize != streamInfo.MaximumBlockSize) { firstSampleNumber = frameNumber * streamInfo.MaximumBlockSize; } else { firstSampleNumber = frameNumber * blocksize; } lastSampleNumber = firstSampleNumber + blocksize - 1; } else { firstSampleNumber = frameNumber; lastSampleNumber = firstSampleNumber + blocksize - 1; } if (frameNumber == 32) { //debug breakpoint } //handle additional sample rate for (int i = 0; i < sampleRateBytes; i++) { sampleRate = (ulong)(sampleRate << 8); b = stream.ReadByte(); if (b == -1) { throw new UnexpectedEndOfStreamException(); } buffer = (ushort)b; rawHeader[headerLen++] = (byte)buffer; sampleRate |= buffer; } sampleRate *= (ulong)sampleRateMultiplicator; b = stream.ReadByte(); if (b == -1) { throw new UnexpectedEndOfStreamException(); } headerCRC = (byte)b; headerVal = new byte[headerLen]; for (int i = 0; i < headerLen; i++) { headerVal[i] = rawHeader[i]; } if (headerCRC != CRC8.Instance.Checksum(headerVal)) { //CRC-8 failed //Console.WriteLine(frameNumber + " CRC-8 failed"); throw new MalformedFileException("Frame header checksum validation failed"); } else { //Console.WriteLine(frameNumber + " CRC-8 passed"); } }
/// <summary> /// Syncronize the stream to the start of the next frame /// </summary> /// <param name="stream"></param> /// <param name="streamInfo"></param> public void SyncronizeRight(Stream stream, StreamInfo streamInfo) { for (; ; ) { SeekSynchRight(stream); var streamPos = stream.Position; try { ParseHeader(stream, streamInfo); } catch(MalformedFileException ex) { //not frame header, keep on synch continue; } stream.Position = streamPos; break; } }
/// <summary> /// Reads the frame header from the stream. /// /// Format description: http://flac.sourceforge.net/format.html#frame_header /// </summary> /// <param name="stream">The stream to read frame header from</param> public void Decode(Stream stream, StreamInfo streamInfo) { ParseHeader(stream, streamInfo); Subframe subfr = new Subframe(); BitReader br = new BitReader(stream); br.IsRecording = true; for (int i = 0; i < headerVal.Length; i++) { br.RememberStream.WriteByte(headerVal[i]); } br.RememberStream.WriteByte(headerCRC); double timeStart, timeEnd; SubframeType ch1Type = SubframeType.LPC, ch2Type = SubframeType.LPC; timeStart = ((double)(frameNumber * blocksize)) / (double)sampleRate; timeEnd = timeStart + ((double)blocksize / (double)sampleRate); for (int i = 0; i < channels; i++) { int bitsPerSampleSubfr = sampleSize; switch (channelSetup) { case ChannelAssignment.MID_SIDE: if (i == 1) { bitsPerSampleSubfr++; } break; case ChannelAssignment.LEFT_SIDE: if (i == 1) { bitsPerSampleSubfr++; } break; case ChannelAssignment.RIGHT_SIDE: if (i == 0) { bitsPerSampleSubfr++; } break; } subfr.Decode(this, streamInfo, br, bitsPerSampleSubfr, i); if (i == 0) { ch1Type = subfr.Type; } else if (i == 1) { ch2Type = subfr.Type; } //Console.WriteLine(subfr); switch(subfr.Type) { case SubframeType.Constant: subFrameConst++; break; case SubframeType.Verbatim: subFrameVerbatim++; break; case SubframeType.LPC: subFrameLPC++; break; case SubframeType.Fixed: subFrameFixed++; break; } } //Console.WriteLine("[{0:00.0000}-{1:00.0000}] setup: {2} 1ch: {3}, 2ch: {4}", timeStart, timeEnd, channelSetup, ch1Type, ch2Type); //decode channels if needed switch (channelSetup) { case ChannelAssignment.MID_SIDE: for (ulong i = 0; i < blocksize; i++) { int mid = (int)output[0,i]; int side = (int) output[1,i]; mid <<= 1; mid |= (side & 1); output[0,i] = (mid + side) >> 1; output[1,i] = (mid - side) >> 1; } break; case ChannelAssignment.LEFT_SIDE: for (ulong i = 0; i < blocksize; i++) { if (i == 264) { //degug breakpoint } output[1, i] = output[0, i] - output[1, i]; } break; case ChannelAssignment.RIGHT_SIDE: for (ulong i = 0; i < blocksize; i++) { output[0, i] = output[1, i] + output[0, i]; } break; } br.IsRecording = false; ushort crc16 = (ushort)Org.Nflac.Flac.Util.StreamReader.ReadUShort(stream); ushort crc16_calc = CRC16.Instance.Checksum(br.RememberStream); if (crc16 == crc16_calc) { //CRC-16 failed throw new MalformedFileException("Frame CRC-16 data checksum validation failed"); //Console.WriteLine(frameNumber + " CRC-16 failed"); } else { //Console.WriteLine(frameNumber + " CRC-16 passed"); } }
private void ParseHeaders() { Metadata block = null; do { block = Metadata.Decode(physicalStream); if (block is Org.Nflac.Flac.Metaheaders.StreamInfo) { flacStreamInfo = (Org.Nflac.Flac.Metaheaders.StreamInfo)block; streamInfo = new FLACNFlacInfo(flacStreamInfo); } headers.Add(block); } while (block.IsLastBlock == false); }
public void Decode(Frame fr, StreamInfo streamInfo, BitReader str, int bitsPerSample, int channelNum) { byte header; this.channelNum = channelNum; header = (byte) str.ReadByte(); if ((header & 0x80) != 0) { //TODO: handle corrupt bit return; } byte subtype = (byte)((header & 0x7E) >> 1); if (subtype == 0) { type = SubframeType.Constant; } else if (subtype == 1) { type = SubframeType.Verbatim; } else if ((subtype >= 8)&&(subtype<=12)) { type = SubframeType.Fixed; order = (uint)(subtype & 0x7); } else if ((subtype & 0x20) != 0) { type = SubframeType.LPC; order = (uint)(subtype & 0x1F) + 1; } else { //TODO: handle reserved frame types return; } wastedBits = 0; if ((header & 0x1) != 0) { int buff = 0; do { str.ReadBit(ref buff); wastedBits++; } while (buff == 0); } numSamples = 0; numResiduals = 0; switch (type) { case SubframeType.Constant: ProcessConstant(fr, str, bitsPerSample); break; case SubframeType.Verbatim: ProcessVerbatim(fr, str, bitsPerSample); break; case SubframeType.Fixed: ProcessFixed(fr, str, bitsPerSample); break; case SubframeType.LPC: ProcessLPC(fr, str, bitsPerSample); break; } }