Click here to Skip to main content
15,885,366 members
Articles / Multimedia / DirectX

Sound Experiments in Managed DirectX

Rate me:
Please Sign up or sign in to vote.
4.85/5 (46 votes)
16 Feb 200726 min read 267.6K   4K   118  
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.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//  � 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.


Written By
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.

Comments and Discussions