private void ReadFrame() { const int FrameHeaderLength = 2 + 1 + 1; const int FrameSync = 0xFFF8; const int FrameSyncMask = 0xFFFC; const int ReservedBlockSizeSamplesType = 0; const int InvalidSampleRateType = 15; int read; byte[] data = new byte[FrameHeaderLength]; if (recordType != FlacRecordType.Sync) { read = BaseStream.Read(data, 0, FrameHeaderLength); if (read < FrameHeaderLength) { if (read <= 0) { recordType = FlacRecordType.Eof; return; } throw new FlacException("Unexpected eof of stream: invalid frame header length"); } if (((data[0] << 8 | data[1]) & FrameSyncMask) != FrameSync) { throw new FlacException("Frame sync is expected"); } } else { const int SyncReadLength = 2; read = BaseStream.Read(data, SyncReadLength, FrameHeaderLength - SyncReadLength); if (read + SyncReadLength < FrameHeaderLength) { throw new FlacException("Unexpected eof of stream: invalid frame header length"); } data[0] = (byte)(FrameSync >> 8); data[1] = secondSyncByte; } if ((data[1] & 0x02) != 0) { throw new FlacException("Frame header reserved bit (15) shall not be 1"); } this.variableBlockSize = (data[1] & 0x01) != 0; int blockSizeSamplesType = data[2] >> 4; if (blockSizeSamplesType == ReservedBlockSizeSamplesType) { throw new FlacException("Frame header block size samples shall not be set to reserved"); } int previousBlockSize = this.blockSize; this.blockSize = FlacCommons.StaticBlockSizeSamples[blockSizeSamplesType]; int sampleRateType = data[2] & 0x0F; if (sampleRateType == InvalidSampleRateType) { throw new FlacException("Frame header sample rate type is invalid"); } this.sampleRate = FlacCommons.StaticSampleRates[sampleRateType]; int channelAssignmentType = data[3] >> 4; if (channelAssignmentType >= FlacCommons.StaticChannelAssignments.Length) { throw new FlacException("Frame header channel assignments are defined as reserved"); } this.channelAssignmentType = (SoundChannelAssignmentType)channelAssignmentType; this.channelAssignment = FlacCommons.StaticChannelAssignments[channelAssignmentType]; if (channelAssignment == null) { throw new FlacException("Frame header channel assignment are not defined"); } this.channelCount = channelAssignment.Length; int sampleSizeInBitsType = (data[3] >> 1) & 0x07; if (sampleSizeInBitsType == FlacCommons.StreaminfoSizeInBitsType) { this.sampleSizeInBits = Streaminfo.BitsPerSample; } else if (FlacCommons.StaticSampleSizeInBits[sampleSizeInBitsType] > 0) { this.sampleSizeInBits = FlacCommons.StaticSampleSizeInBits[sampleSizeInBitsType]; } else { throw new FlacException("Frame header sample size is defined as reserved"); } if ((data[3] & 1) != 0) { throw new FlacException("Frame header reserved bit (31) shall not be 1"); } MemoryStream ms = new MemoryStream(20); ms.Write(data, 0, FrameHeaderLength); byte[] numberData; if (variableBlockSize) { ReadUtf8Number(out this.sampleNumber, out numberData); if (numberData.Length > 7) { throw new FlacException("Invalid variable block size"); } } else { ReadUtf8Number(out this.frameNumber, out numberData); if (numberData.Length > 6) { throw new FlacException("Invalid frame number"); } this.sampleNumber = this.frameNumber == 0 ? 0 : previousBlockSize *this.frameNumber; } ms.Write(numberData, 0, numberData.Length); byte[] blockSizeData = null; switch (blockSizeSamplesType) { case FlacCommons.Bit8BlockSizeSamplesType: blockSizeData = ReadExactly(1); this.blockSize = (int)blockSizeData[0] + 1; break; case FlacCommons.Bit16BlockSizeSamplesType: blockSizeData = ReadExactly(2); this.blockSize = (blockSizeData[0] << 8 | blockSizeData[1]) + 1; break; } if (blockSizeData != null) { ms.Write(blockSizeData, 0, blockSizeData.Length); } byte[] sampleRateData = null; switch (sampleRateType) { case FlacCommons.StreaminfoSampleRateType: this.sampleRate = Streaminfo.SampleRate; break; case FlacCommons.Bit8SampleRateType: sampleRateData = ReadExactly(1); this.sampleRate = sampleRateData[0]; break; case FlacCommons.Bit16SampleRateType: sampleRateData = ReadExactly(2); this.sampleRate = sampleRateData[0] << 8 | sampleRateData[1]; break; case FlacCommons.Bit16Mult10SampleRateType: sampleRateData = ReadExactly(2); this.sampleRate = (sampleRateData[0] << 8 | sampleRateData[1]) * 10; break; } if (sampleRateData != null) { ms.Write(sampleRateData, 0, sampleRateData.Length); } byte[] readData = ms.ToArray(); byte crc8 = CrcUtils.Crc8(0, readData); int headerCrc8 = BaseStream.ReadByte(); if (headerCrc8 < 0) { throw new FlacException("Unexpected end of stream: frame CRC8 expected"); } else if (crc8 != headerCrc8) { throw new FlacException("Invalid frame CRC"); } ushort currentCrc16 = CrcUtils.Crc16( CrcUtils.Crc16(0, readData), (byte)headerCrc8); bitReader = new FlacBitStreamReader(BaseStream, currentCrc16); lastFrameHeaderData = new byte[readData.Length + 1]; Array.Copy(readData, lastFrameHeaderData, readData.Length); lastFrameHeaderData[readData.Length] = crc8; recordType = FlacRecordType.Frame; }
public void WriteFrame(FlacMethodAndDataPair[] methods, SoundChannelAssignmentType channelAssignment) { EnsureFramesMode(); sink.StartFrame(streamPosition, currentSample); if (methods == null) { throw new ArgumentNullException("methods"); } if (methods.Length != Streaminfo.ChannelsCount) { throw new ArgumentNullException("Methods items does not correspond to amount of channels"); } int samplesCount = methods[0].Data.Length; const byte Blocking = 0x00; // fixed MemoryStream frameHeader = new MemoryStream(); // sync code + reserved = 0 + blocking frameHeader.WriteByte(0xFF); frameHeader.WriteByte(0xF8 | Blocking); int interChannelSamplesTypeIndex = Array.IndexOf(FlacCommons.StaticBlockSizeSamples, samplesCount); int interChannelSamplesType; if (interChannelSamplesTypeIndex > 0) { interChannelSamplesType = interChannelSamplesTypeIndex; } else if (samplesCount > 256) { interChannelSamplesType = FlacCommons.Bit16BlockSizeSamplesType; } else { interChannelSamplesType = FlacCommons.Bit8BlockSizeSamplesType; } int sampleRateTypeIndex = Array.IndexOf(FlacCommons.StaticSampleRates, Streaminfo.SampleRate); int sampleRateType = sampleRateTypeIndex > 0 ? sampleRateTypeIndex : FlacCommons.StreaminfoSampleRateType; frameHeader.WriteByte((byte)(interChannelSamplesType << 4 | sampleRateType)); int channelAssignmetType = (int)channelAssignment; int sampleSizeInBitsTypeIndex = Array.IndexOf(FlacCommons.StaticSampleSizeInBits, Streaminfo.BitsPerSample); int sampleSizeInBitsType = sampleSizeInBitsTypeIndex > 0 ? sampleSizeInBitsTypeIndex : FlacCommons.StreaminfoSizeInBitsType; frameHeader.WriteByte((byte)(channelAssignmetType << 4 | sampleSizeInBitsType << 1)); WriteUtf8Number(frameHeader, frameNumber); switch (interChannelSamplesType) { case FlacCommons.Bit8BlockSizeSamplesType: frameHeader.WriteByte((byte)(samplesCount - 1)); break; case FlacCommons.Bit16BlockSizeSamplesType: frameHeader.WriteByte((byte)((samplesCount - 1) >> 8)); frameHeader.WriteByte((byte)(samplesCount - 1)); break; } frameHeader.Dispose(); byte[] frameHeaderData = frameHeader.ToArray(); byte crc8 = CrcUtils.Crc8(0, frameHeaderData); BaseStream.Write(frameHeaderData, 0, frameHeaderData.Length); BaseStream.WriteByte(crc8); ++frameNumber; ushort crc16Seed = CrcUtils.Crc16(CrcUtils.Crc16(0, frameHeaderData), crc8); // write channels FlacBitStreamWriter bitWriter = new FlacBitStreamWriter(BaseStream, crc16Seed); for (int i = 0; i < methods.Length; i++) { WriteSubframe(bitWriter, methods[i]); } int subframesLength; ushort crc16; bitWriter.Complete(out crc16, out subframesLength); // write footer BaseStream.WriteByte((byte)(crc16 >> 8)); BaseStream.WriteByte((byte)crc16); int frameSize = frameHeaderData.Length + 1 + subframesLength + 2; streamPosition += frameSize; currentSample += samplesCount; sink.EndFrame(streamPosition, currentSample); }