/// <summary>Reads a new WAVE format section from the specified stream.</summary> /// <param name="preRead">Pre-parsed RIFF chunk header.</param> /// <param name="source">Source stream to read data from.</param> /// <param name="waveFormat">Format of the data section to be parsed.</param> /// <exception cref="InvalidOperationException">WAVE format or extra parameters section too small, wave file corrupted.</exception> public WaveDataChunk(RiffChunk preRead, Stream source, WaveFormatChunk waveFormat) : base(preRead, RiffTypeID) { m_waveFormat = waveFormat; m_sampleBlocks = new List<LittleBinaryValue[]>(); int blockSize = waveFormat.BlockAlignment; int sampleSize = waveFormat.BitsPerSample / 8; byte[] buffer = new byte[blockSize]; int channels = waveFormat.Channels; LittleBinaryValue[] sampleBlock; int bytesRead = source.Read(buffer, 0, blockSize); while (bytesRead == blockSize) { // Create a new sample block, one binary sample value for each channel sampleBlock = new LittleBinaryValue[channels]; for (int x = 0; x < channels; x++) { sampleBlock[x] = new LittleBinaryValue(buffer.CopyBuffer(x * sampleSize, sampleSize)); } m_sampleBlocks.Add(sampleBlock); bytesRead = source.Read(buffer, 0, blockSize); } }
/// <summary>Reads a new RIFF header from the specified stream.</summary> /// <param name="preRead">Pre-parsed <see cref="RiffChunk"/> header.</param> /// <param name="source">Source stream to read data from.</param> /// <param name="format">Expected RIFF media format (e.g., "WAVE").</param> /// <exception cref="ArgumentNullException"><paramref name="format"/> cannot be null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="format"/> must be extactly 4 characters in length.</exception> public RiffHeaderChunk(RiffChunk preRead, Stream source, string format) : base(preRead, RiffTypeID) { Format = format; int length = BinaryLength - preRead.BinaryLength; byte[] buffer = new byte[length]; int bytesRead = source.Read(buffer, 0, length); if (bytesRead < length) throw new InvalidOperationException("RIFF format section too small, media file corrupted."); // Read and validate format stored in RIFF section Initialize(buffer, 0, bytesRead); if (Format != Format) throw new InvalidDataException(string.Format("{0} format expected but got {1}, this does not appear to be a valid {0} file", Format, format)); }
/// <summary>Reads a new WAVE format section from the specified stream.</summary> /// <param name="preRead">Pre-parsed RIFF chunk header.</param> /// <param name="source">Source stream to read data from.</param> /// <exception cref="InvalidOperationException">WAVE format or extra parameters section too small, wave file corrupted.</exception> public WaveFormatChunk(RiffChunk preRead, Stream source) : base(preRead, RiffTypeID) { int length = ChunkSize; byte[] buffer = new byte[length]; int bytesRead = source.Read(buffer, 0, length); if (bytesRead < length) throw new InvalidOperationException("WAVE format section too small, wave file corrupted."); m_audioFormat = EndianOrder.LittleEndian.ToInt16(buffer, 0); m_channels = EndianOrder.LittleEndian.ToInt16(buffer, 2); m_sampleRate = EndianOrder.LittleEndian.ToInt32(buffer, 4); m_byteRate = EndianOrder.LittleEndian.ToInt32(buffer, 8); m_blockAlignment = EndianOrder.LittleEndian.ToInt16(buffer, 12); m_bitsPerSample = EndianOrder.LittleEndian.ToInt16(buffer, 14); if (m_bitsPerSample % 8 != 0) throw new InvalidDataException("Invalid bit rate encountered - wave file bit rates must be a multiple of 8"); if (length > 16) { m_extraParametersSize = EndianOrder.LittleEndian.ToInt16(buffer, 16); // Read extra parameters, if any if (m_extraParametersSize > 0) { m_extraParameters = new byte[m_extraParametersSize]; bytesRead = source.Read(m_extraParameters, 0, m_extraParametersSize); if (bytesRead < m_extraParametersSize) throw new InvalidOperationException("WAVE extra parameters section too small, wave file corrupted."); } } }
/// <summary>Reads a new WAVE list info section from the specified stream.</summary> /// <param name="preRead">Pre-parsed <see cref="RiffChunk"/> header.</param> /// <param name="source">Source stream to read data from.</param> /// <exception cref="InvalidOperationException">WAVE list info section is too small, wave file corrupted.</exception> public ListInfoChunk(RiffChunk preRead, Stream source) : base(preRead, RiffTypeID) { byte[] buffer = new byte[preRead.ChunkSize]; byte[] nullByte = new byte[] { 0 }; int bytesRead = source.Read(buffer, 0, buffer.Length); int length, index = 0; string key, value; m_infoStrings = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); if (bytesRead >= 4 && string.Compare(Encoding.ASCII.GetString(buffer, index, 4), "INFO", true) == 0) { index += 4; while (index < bytesRead) { // Read key length = buffer.IndexOfSequence(nullByte, index) - index + 1; key = Encoding.ASCII.GetString(buffer, index, length - 1).RemoveNull().RemoveControlCharacters(); index += length; // Skip through null values while (index < bytesRead && buffer[index] == 0) index++; // Read value length = buffer.IndexOfSequence(nullByte, index) - index + 1; value = Encoding.ASCII.GetString(buffer, index, length - 1).RemoveNull().RemoveControlCharacters(); index += length; // Skip through null values while (index < bytesRead && buffer[index] == 0) index++; if (!string.IsNullOrWhiteSpace(key)) m_infoStrings[key] = value; } } }
/// <summary>Reads a new RIFF header from the specified stream.</summary> /// <param name="preRead">Pre-parsed <see cref="RiffChunk"/> header.</param> /// <param name="source">Source stream to read data from.</param> /// <param name="format">Expected RIFF media format (e.g., "WAVE").</param> /// <exception cref="ArgumentNullException"><paramref name="format"/> cannot be null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="format"/> must be extactly 4 characters in length.</exception> public RiffHeaderChunk(RiffChunk preRead, Stream source, string format) : base(preRead, RiffTypeID) { if (string.IsNullOrWhiteSpace(format)) throw new ArgumentNullException("value"); if (format.Length != 4) throw new ArgumentOutOfRangeException("value", "Format must be exactly 4 characters in length"); byte[] buffer = BufferPool.TakeBuffer(4); try { int bytesRead = source.Read(buffer, 0, 4); if (bytesRead < 4) throw new InvalidOperationException("RIFF format section too small, media file corrupted"); // Read format stored in RIFF section m_format = Encoding.ASCII.GetString(buffer, 0, 4); if (m_format != format) throw new InvalidDataException(string.Format("{0} format expected but got {1}, this does not appear to be a valid {0} file", format, m_format)); } finally { if (buffer != null) BufferPool.ReturnBuffer(buffer); } }
/// <summary>Reads a new <see cref="WaveFormatChunk"/> from the specified stream.</summary> /// <param name="preRead">Pre-parsed <see cref="RiffChunk"/> header.</param> /// <param name="source">Source stream to read data from.</param> /// <exception cref="InvalidOperationException">WAVE format or extra parameters section too small, wave file corrupted.</exception> /// <exception cref="InvalidDataException">Invalid bit rate encountered - wave file bit rates must be a multiple of 8.</exception> public WaveFormatChunk(RiffChunk preRead, Stream source) : base(preRead, RiffTypeID) { int length = ChunkSize; byte[] buffer = BufferPool.TakeBuffer(length); try { int bytesRead = source.Read(buffer, 0, length); // Initialize class from buffer ParseBinaryImage(buffer, 0, bytesRead); // Read extra parameters, if any if (m_extraParametersSize > 0) { m_extraParameters = new byte[m_extraParametersSize]; bytesRead = source.Read(m_extraParameters, 0, m_extraParametersSize); if (bytesRead < m_extraParametersSize) throw new InvalidOperationException("WAVE extra parameters section too small, wave file corrupted"); } } finally { if (buffer != null) BufferPool.ReturnBuffer(buffer); } }
/// <summary>Reads a new WAVE format section from the specified stream.</summary> /// <param name="preRead">Pre-parsed <see cref="RiffChunk"/> header.</param> /// <param name="source">Source stream to read data from.</param> /// <param name="waveFormat">Format of the data section to be parsed.</param> /// <exception cref="InvalidOperationException">WAVE format or extra parameters section too small, wave file corrupted.</exception> public WaveDataChunk(RiffChunk preRead, Stream source, WaveFormatChunk waveFormat) : base(preRead, RiffTypeID) { m_waveFormat = waveFormat; m_sampleBlocks = new List<LittleBinaryValue[]>(); m_chunkSize = -1; int blockSize = waveFormat.BlockAlignment; int sampleSize = waveFormat.BitsPerSample / 8; int channels = waveFormat.Channels; TypeCode sampleTypeCode = m_waveFormat.GetSampleTypeCode(); LittleBinaryValue[] sampleBlock; byte[] buffer = BufferPool.TakeBuffer(blockSize); try { int bytesRead = source.Read(buffer, 0, blockSize); while (bytesRead == blockSize) { // Create a new sample block, one little-endian formatted binary sample value for each channel sampleBlock = new LittleBinaryValue[channels]; for (int x = 0; x < channels; x++) { sampleBlock[x] = new LittleBinaryValue(sampleTypeCode, buffer, x * sampleSize, sampleSize); } m_sampleBlocks.Add(sampleBlock); bytesRead = source.Read(buffer, 0, blockSize); } } finally { if (buffer != null) BufferPool.ReturnBuffer(buffer); } }
/// <summary>Reads a new <see cref="WaveFormatChunk"/> from the specified stream.</summary> /// <param name="preRead">Pre-parsed <see cref="RiffChunk"/> header.</param> /// <param name="source">Source stream to read data from.</param> /// <exception cref="InvalidOperationException">WAVE format or extra parameters section too small, wave file corrupted.</exception> /// <exception cref="InvalidDataException">Invalid bit rate encountered - wave file bit rates must be a multiple of 8.</exception> public WaveFormatChunk(RiffChunk preRead, Stream source) : base(preRead, RiffTypeID) { int length = ChunkSize; byte[] buffer = new byte[length]; int bytesRead = source.Read(buffer, 0, length); // Initialize class from buffer Initialize(buffer, 0, bytesRead); // Read extra parameters, if any if (m_extraParametersSize > 0) { m_extraParameters = new byte[m_extraParametersSize]; bytesRead = source.Read(m_extraParameters, 0, m_extraParametersSize); if (bytesRead < m_extraParametersSize) throw new InvalidOperationException("WAVE extra parameters section too small, wave file corrupted."); } }
// Static Methods /// <summary> /// Attempts to read the next RIFF chunk from the <paramref name="source"/> stream. /// </summary> /// <param name="source">Source stream for next RIFF chunk.</param> /// <returns>Next RIFF chunk read from the <paramref name="source"/> stream.</returns> /// <exception cref="InvalidOperationException">RIFF chunk too small, media file corrupted.</exception> public static RiffChunk ReadNext(Stream source) { RiffChunk riffChunk = new RiffChunk(); int length = riffChunk.BinaryLength; byte[] buffer = new byte[length]; int bytesRead = source.Read(buffer, 0, length); if (bytesRead < length) throw new InvalidOperationException("RIFF chunk too small, media file corrupted"); riffChunk.TypeID = Encoding.ASCII.GetString(buffer, 0, 4); riffChunk.ChunkSize = EndianOrder.LittleEndian.ToInt32(buffer, 4); return riffChunk; }
/// <summary> /// Creates a copy of the <see cref="RiffChunk"/>. /// </summary> /// <returns>A new copy of the <see cref="RiffChunk"/>.</returns> public RiffChunk Clone() { RiffChunk riffChunk = new RiffChunk(m_typeID); riffChunk.ChunkSize = m_chunkSize; return riffChunk; }
/// <summary> /// Constructor for derived classes used to initialize and validate <see cref="RiffChunk"/> properties. /// </summary> /// <param name="preRead">Pre-parsed <see cref="RiffChunk"/> header.</param> /// <param name="typeID">Expected type ID.</param> protected RiffChunk(RiffChunk preRead, string typeID) { if (typeID != preRead.TypeID) throw new InvalidDataException(string.Format("{0} chunk expected but got {1}, file does not appear to be valid", typeID, preRead.TypeID)); m_typeID = preRead.TypeID; m_chunkSize = preRead.ChunkSize; }