Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version

Sound Experiments in Managed DirectX

, 16 Feb 2007
Using static and streaming sound buffers in Managed DirectX.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//  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.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//  � 2007 Gary W. Schwede and 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 sealed 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");

				// finding and starting format chunk  
				while(Next4chars(m_Reader) != "fmt ")
				{
					// skip this chunk's data field
					int otherChunkLen = m_Reader.ReadInt32();
					for (int otherDataBytes = 0; otherDataBytes < otherChunkLen; otherDataBytes++)
					{
						m_Reader.ReadByte();
					}					
				}
				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;
			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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

DrGary83
Software Developer (Senior)
United States United States
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.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 16 Feb 2007
Article Copyright 2006 by DrGary83
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid