|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES
// OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// � 2006 Gary W. Schwede Stream Computers, Inc. All rights reserved.
// Contact: gary at streamcomputers dot com. Permission to incorporate
// all or part of this code in your application is given on the condition
// that this notice accompanies it in your code and documentation.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;
using StreamComputers.Utilities;
namespace StreamComputers.Riff
{
/// <summary>
/// RiffWaveReader reads and parses a RIFF WAVE file, exposes properties of the file
/// and methods for reading data from the file. Construct an instance of
/// RiffWaveReader and keep it around until the application is finished reading the file,
/// then call Close() to clean up its resources.
/// </summary>
public class RiffWaveReader : RiffWaveManager
{
private BinaryReader m_Reader;
#region Read properties
public WaveFormatTag FormatTag
{
get { return m_FormatTag; }
}
public short Channels
{
get { return m_Channels; }
}
public int SamplesPerSecond
{
get { return m_SamplesPerSecond; }
}
public int AverageBytesPerSecond
{
get { return m_AverageBytesPerSecond; }
}
public short BlockAlign
{
get { return m_BlockAlign; }
}
public short BitsPerSample
{
get { return m_BitsPerSample; }
}
public long StreamLength
{
get { return m_StreamLength; }
}
public long DataStart
{
get { return m_DataStart; }
}
public long DataLength
{
get { return m_DataLength; }
}
#endregion
/// <summary>
/// Constructs a RiffWaveReader associated with the specified file.
/// The object parses the RIFF WAVE file, exposes properties of the file
/// and methods for reading data from the file. Construct an instance of
/// RiffWaveReader and keep it around until the application is finished
/// reading the file, then call Close() to clean up its resources.
/// </summary>
/// <param name="fileName">name of RIFF WAVE file to work with.</param>
public RiffWaveReader(string fileName)
{
try
{
m_Stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
m_Reader = new BinaryReader(m_Stream);
ParseWaveInfo();
}
catch (RiffParserException)
{
Cleanup();
throw;
}
}
#region Parser code
/// <summary>
/// Parses the file and sets properties. Leaves stream position property m_Stream
/// at beginning of data field. Throws RiffParserException if not a valid RIFF WAVE
/// file, or if fmt chunk does not immediately follow the RIFF WAVE uberheader.
/// </summary>
private void ParseWaveInfo()
{
try
{
// starting RIFF uberheader
if (Next4chars(m_Reader) != "RIFF") // reads ASCII Identifier
throw new RiffParserException("Not a RIFF file");
m_Reader.ReadInt32(); // File length minus first 8 bytes of RIFF description
if (Next4chars(m_Reader) != "WAVE") // reads ASCII Identifier
throw new RiffParserException("Not a WAVE type RIFF file");
// starting format chunk TODO fix this assumption: may be some other chunk first
if (Next4chars(m_Reader) != "fmt ") // reads ASCII Identifier
throw new RiffParserException("fmt chunk not in expected place");
int fmtChunkLen = m_Reader.ReadInt32();
if ( fmtChunkLen < 16) // bad format chunk length
throw new RiffParserException("fmt chunk length less than minimum 16 bytes");
m_FormatTag = (WaveFormatTag) m_Reader.ReadInt16();
m_Channels = m_Reader.ReadInt16();
m_SamplesPerSecond = m_Reader.ReadInt32();
m_AverageBytesPerSecond = m_Reader.ReadInt32();
m_BlockAlign = m_Reader.ReadInt16();
m_BitsPerSample = m_Reader.ReadInt16();
// Skip over extra format bytes until we get to the data chunk.
for (int numberfmtBytes = 16; numberfmtBytes < fmtChunkLen; numberfmtBytes++)
{
m_Reader.ReadByte();
}
while (m_Stream.Position < m_Stream.Length && Next4chars(m_Reader) != "data")
{
}
if (m_Stream.Position >= m_Stream.Length) // didn't find "data"
throw new RiffParserException("data chunk not found");
m_DataLength = m_Reader.ReadInt32(); // length of the data itself in bytes
m_DataStart = m_Stream.Position; // start of data itself
m_StreamLength = m_Stream.Length;
}
catch (RiffParserException e)
{
MessageBox.Show("File is not a valid Managed DirectSound RIFF file:"
+ "\n Please select another.");
DebugConsole.Write(e.ToString());
throw;
}
}
// returns the next 4 ASCII chars or bytes
private string Next4chars(BinaryReader reader)
{
byte[] ch = new byte[4];
reader.Read(ch, 0, ch.Length);
return Encoding.ASCII.GetString(ch, 0, ch.Length);
}
#endregion
#region Data reader methods
/// <summary>
/// Copy a block of PCM16 data frames from the Wave file to a buffer.
/// If stream end is encountered before buffer if full, remainder is filled with 0s.
/// </summary>
/// <param name="frameBuf">ref PCM16Frame[]</param>
/// <param name="blockStartIndex">Index in frames from start of data to start copying frames</param>
/// <param name="numFrames">Number of frames to copy</param>
/// <returns>Number of frames copied to buffer from file (0-filled frames not counted.)</returns>
public int GetPCM16DataFrames(ref Pcm16Frame[] frameBuf, long blockStartIndex, int numFrames)
{
if (m_Reader == null)
{
throw new InvalidOperationException(
"RiffWaveReader.GetPCM16DataFrames() called while object not readable.");
}
Debug.Assert(numFrames <= frameBuf.Length, "GetPCM16DataFrames called with frameBuf too small.");
Debug.Assert(m_BlockAlign == 4, "GetPCM16DataFrames(.) called with Frame size != 4 Bytes");
m_Reader.BaseStream.Position = m_DataStart + blockStartIndex*m_BlockAlign;
long numberFramesTotal = m_DataLength / m_BlockAlign;
long numberFramesAfterStartIndex = numberFramesTotal - blockStartIndex;
if(numberFramesAfterStartIndex >= numFrames )
{
for (int i = 0; i < numFrames; i++)
{
frameBuf[i].Lch = m_Reader.ReadInt16();
frameBuf[i].Rch = m_Reader.ReadInt16();
}
return numFrames;
}
else
{
int i = 0;
for (; i < numberFramesAfterStartIndex; i++)
{
frameBuf[i].Lch = m_Reader.ReadInt16();
frameBuf[i].Rch = m_Reader.ReadInt16();
}
for (; i < numFrames; i++)
{
frameBuf[i].Lch = 0;
frameBuf[i].Rch = 0;
}
return (int) numberFramesAfterStartIndex;
}
}
/// <summary>
/// Copy a block of data bytes from the Wave file to a buffer.
/// If end of data is encountered before buffer is full, remainder is filled with 0s.
/// </summary>
/// <param name="dataBuf">buffer for data bytes to transfer</param>
/// <param name="blockStartIndex">Index in bytes from start of data to start copying </param>
/// <param name="numBytes">Number of bytes to copy</param>
/// <returns>Number of bytes copied to buffer from file ( 0-filled bytes not counted.)</returns>
public int GetDataBytes(ref byte[] dataBuf, long blockStartIndex, int numBytes)
{
if (m_Reader == null)
{
throw new InvalidOperationException(
"RIFFWaveManager.GetPCM16DataFrames() called while object not readable.");
}
Debug.Assert(numBytes <= dataBuf.Length, "GetDataBytes called with byteBuf too small.");
m_Reader.BaseStream.Position = m_DataStart + blockStartIndex;
long dataBytesRemaining = m_DataLength - blockStartIndex;
int waveDataBytesRead = 0;
if(dataBytesRemaining > numBytes)
{
waveDataBytesRead = m_Reader.Read(dataBuf, 0, numBytes);
}
else
{
waveDataBytesRead = m_Reader.Read(dataBuf, 0, (int) dataBytesRemaining);
for (int j = waveDataBytesRead; j < numBytes; j++)
{
dataBuf[j] = 0;
}
}
return waveDataBytesRead;
}
#endregion
#region Finalization
// Closes the file, releases resources, and disposes the RiffReader object.
public override void Close()
{
Dispose();
}
protected override void Cleanup()
{
try
{
if (m_Reader != null)
{
m_Reader.Close();
m_Reader = null;
}
}
finally
{
base.Cleanup();
}
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
My life and career have been a bit unusual (mostly in good ways). So, I'm grateful every day for the opportunities God's given me to do different things and see different aspects of life.
Education: B.S. Physics '73 (atmospheric physics, sounding rockets), M.S. Computer Science '76 (radio astronomy, fuzzy controllers, music pattern recognition and visualization) New Mexico Tech; Ph.D. Engineering '83 (parallel computer architecture, digital signal processing, economics) U.C. Berkeley.
I'm married to Susan, a wonderful woman whom I met in a Computer Architecture class at U.C. Berkeley.
Professional activities: Digital systems engineer, digital audio pioneer, founder or key in several tech startups, consulting engineer, expert witness. I'm currently developing a multithreading framework in C# .NET, that makes it almost easy to write correct programs for multicore processors. I'm also implementing a new transform for recognizing, editing, and processing signals, especially sound.
I'm an occasional essayist, public speaker, and podcaster, and free-market space advocate. I enjoy good wine, good music, good friends, and cats.
If you think your project could use a different point of view, I'm available for consulting work in the San Francisco Bay area, or (preferrably) via the net.