Click here to Skip to main content
15,896,154 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 270K   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>
	/// RiffWaveWriter assembles a RIFF WAVE file to be written to disk, sets properties,
	/// and exposes methods for writing data to the file.  Construct an instance of 
	/// RiffWaveWriter and keep it around until the application is finished writing the file,
	/// then call Close() to clean up its resources.
	/// </summary>
	public class RiffWaveWriter : RiffWaveManager
	{
		private BinaryWriter	m_Writer = null;

		#region properties

		public WaveFormatTag FormatTag
		{
			get { return m_FormatTag; }
			set { m_FormatTag = value; }
		}

		public short Channels
		{
			get { return m_Channels;}
			set { m_Channels = value; }
		}

		public int SamplesPerSecond
		{
			get { return m_SamplesPerSecond;}
			set { m_SamplesPerSecond = value; }
		}

		public int AverageBytesPerSecond
		{
			get { return m_AverageBytesPerSecond;}
			set { m_AverageBytesPerSecond = value; }
		}

		public short BlockAlign
		{
			get { return m_BlockAlign;}
			set { m_BlockAlign = value; }
		}

		public short BitsPerSample
		{
			get { return m_BitsPerSample;}
			set { m_BitsPerSample = value; }
		}

		public long StreamLength
		{
			get { return m_StreamLength;}
			set { m_StreamLength = value; }
		}

		public long DataStart
		{
			get { return m_DataStart;}
			set { m_DataStart = value; }
		}

		public long DataLength
		{
			get { return m_DataLength;}
			set { m_DataLength = value; }
		}

		#endregion

		public RiffWaveWriter(string fileName)
		{
			try
			{
				m_Stream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite);
				DebugConsole.WriteLine("Set Wave properties for new file!");
			}
			catch (Exception e)
			{
				MessageBox.Show("RiffWaveWriter(string fileName) failed: " + e);
				Cleanup();
				ZeroAllInfo();
				throw;
			}
		}

		#region Methods to write RIFF WAVE files

		// Failed to find valid data header, so zero out the header information.
		protected void ZeroAllInfo()
		{
			m_AverageBytesPerSecond = 0;
			m_BitsPerSample = 0;
			m_BlockAlign = 0;
			m_Channels = 0;
			m_DataLength = 0;
			m_DataStart = 0;
			m_FormatTag = 0;
			m_SamplesPerSecond = 0;
			m_StreamLength = 0;
		}
		
		/// <summary>
		/// Sets properties to values appropriate for standard CD-quality 
		/// 16-bit 44.1KHz stereo samples
		/// </summary>
		public void SetCD16Info()
		{
			m_FormatTag = WaveFormatTag.Pcm;
			m_Channels = 2;
			m_SamplesPerSecond = 44100;
			m_AverageBytesPerSecond = 44100*2*2;
			m_BlockAlign = 2*2;
			m_BitsPerSample = 16;
		}

		/// <summary>
		/// Writes header information to RIFF WAVE file.  Properties must be set 
		/// before call. 
		/// Leaves writer pointer at start of data field, and zeros m_DataLength.
		/// </summary>
		public void WriteHeader()
		{
			m_Writer = new BinaryWriter(m_Stream);

			// uberheader
			m_Writer.Write(EncodeChars("RIFF"));
			Int32 i = 0;							// placeholder for (filesize - 8)
			m_Writer.Write(i);						// byte position = 4
			m_Writer.Write(EncodeChars("WAVE"));

			// fmt chunk (without extra format bytes)
			m_Writer.Write(EncodeChars("fmt "));
			i = 16;
			m_Writer.Write(i);
			m_Writer.Write((short) m_FormatTag);
			m_Writer.Write(m_Channels);
			m_Writer.Write(m_SamplesPerSecond);
			m_Writer.Write(m_AverageBytesPerSecond);
			m_Writer.Write(m_BlockAlign);
			m_Writer.Write(m_BitsPerSample);

			// data chunk
			m_Writer.Write(EncodeChars("data"));
			i = 0;									// placeholder for data length
			m_Writer.Write(i);						// byte position 0x28, 40 decimal

			Debug.Assert(m_Stream.Position == 44);
			m_DataLength = 0;
		}

		private byte[] EncodeChars(string s)
		{
			byte[] es = new byte[s.Length];
			es = Encoding.ASCII.GetBytes(s);
			return es;
		}

		/// <summary>
		/// Appends PCM16Data frames to a RiffWaveWriter-managed file.
		/// </summary>
		/// <param name="buf">PCM16Frame[] buffer to write to file</param>
		public void WritePCM16DataFrames(Pcm16Frame[] buf)
		{
			Debug.Assert(buf.Length <= Int32.MaxValue, "WritePCM16DataFrames called with buffer too big.");
			int i = 0;
			for (; i < buf.Length; i++)
			{
				m_Writer.Write(buf[i].Lch);
				m_Writer.Write(buf[i].Rch);
			}
			m_DataLength += buf.Length*m_BlockAlign;
		}

		/// <summary>
		/// Finishes writing chunk length fields to a RIFF WAVE file but does not close it.
		/// </summary>
		public void WriteFinal()
		{
			// RIFF spec wants uint, stream length is really a long
			Debug.Assert(m_Writer.BaseStream.Length <= Int32.MaxValue,
			             "RiffWaveWriter.WriteFinal() saw file too big for an int32 length");
			int size = (int) m_Writer.BaseStream.Length;
			m_Writer.BaseStream.Position = 4;
			m_Writer.Write(size - 8);
			m_Writer.BaseStream.Position = 40;
			m_Writer.Write(m_DataLength);
		}

		#endregion

		#region Finalization

		// Flushes and closes the file and releases resources.  
		public override void Close()
		{
			Dispose();
		}

		protected override void Cleanup()
		{
			try
			{
				if(m_Writer != null)
				{
					m_Writer.Flush();
					m_Writer.Close();
				}
			}
			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