Click here to Skip to main content
15,886,639 members
Articles / Multimedia / GDI

Sound visualizer in C#

Rate me:
Please Sign up or sign in to vote.
4.90/5 (58 votes)
15 Aug 2007GPL3 435.7K   39.8K   228  
Simple sound sampler with visualization
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  This material may not be duplicated in whole or in part, except for 
//  personal use, without the express written consent of the author. 
//
//  Email:  ianier@hotmail.com
//
//  Copyright (C) 1999-2003 Ianier Munoz. All Rights Reserved.

using System;
using System.Threading;
using System.Runtime.InteropServices;

namespace SoundViewer
{
	internal class WaveInHelper
	{
		public static void Try(int err)
		{
			if (err != WaveNative.MMSYSERR_NOERROR)
				throw new Exception(err.ToString());
		}
	}

	public delegate void BufferDoneEventHandler(IntPtr data, int size);

	internal class WaveInBuffer : IDisposable
	{
		public WaveInBuffer NextBuffer;

		private AutoResetEvent m_RecordEvent = new AutoResetEvent(false);
		private IntPtr m_WaveIn;

		private WaveNative.WaveHdr m_Header;
		private byte[] m_HeaderData;
		private GCHandle m_HeaderHandle;
		private GCHandle m_HeaderDataHandle;

		private bool m_Recording;

		internal static void WaveInProc(IntPtr hdrvr, int uMsg, int dwUser, ref WaveNative.WaveHdr wavhdr, int dwParam2)
		{
			if (uMsg == WaveNative.MM_WIM_DATA)
			{
				try
				{
					GCHandle h = (GCHandle)wavhdr.dwUser;
					WaveInBuffer buf = (WaveInBuffer)h.Target;
					buf.OnCompleted();
				}
				catch
				{
				}
			}
		}

		public WaveInBuffer(IntPtr waveInHandle, int size)
		{
			m_WaveIn = waveInHandle;

			m_HeaderHandle = GCHandle.Alloc(m_Header, GCHandleType.Pinned);
			m_Header.dwUser = (IntPtr)GCHandle.Alloc(this);
			m_HeaderData = new byte[size];
			m_HeaderDataHandle = GCHandle.Alloc(m_HeaderData, GCHandleType.Pinned);
			m_Header.lpData = m_HeaderDataHandle.AddrOfPinnedObject();
			m_Header.dwBufferLength = size;
			WaveInHelper.Try(WaveNative.waveInPrepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)));
		}
		~WaveInBuffer()
		{
			Dispose();
		}

		public void Dispose()
		{
			if (m_Header.lpData != IntPtr.Zero)
			{
				WaveNative.waveInUnprepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
				m_HeaderHandle.Free();
				m_Header.lpData = IntPtr.Zero;
			}
			m_RecordEvent.Close();
			if (m_HeaderDataHandle.IsAllocated)
				m_HeaderDataHandle.Free();
			GC.SuppressFinalize(this);
		}

		public int Size
		{
			get { return m_Header.dwBufferLength; }
		}

		public IntPtr Data
		{
			get { return m_Header.lpData; }
		}

		public bool Record()
		{
			lock(this)
			{
				m_RecordEvent.Reset();
				m_Recording = WaveNative.waveInAddBuffer(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)) == WaveNative.MMSYSERR_NOERROR;
				return m_Recording;
			}
		}

		public void WaitFor()
		{
			if (m_Recording)
				m_Recording = m_RecordEvent.WaitOne();
			else
				Thread.Sleep(0);
		}

		private void OnCompleted()
		{
			m_RecordEvent.Set();
			m_Recording = false;
		}
	}

	public class WaveInRecorder : IDisposable
	{
		private IntPtr m_WaveIn;
		private WaveInBuffer m_Buffers; // linked list
		private WaveInBuffer m_CurrentBuffer;
		private Thread m_Thread;
		private BufferDoneEventHandler m_DoneProc;
		private bool m_Finished;

		private WaveNative.WaveDelegate m_BufferProc = new WaveNative.WaveDelegate(WaveInBuffer.WaveInProc);

		public static int DeviceCount
		{
			get { return WaveNative.waveInGetNumDevs(); }
		}

		public WaveInRecorder(int device, WaveFormat format, int bufferSize, int bufferCount, BufferDoneEventHandler doneProc)
		{
			m_DoneProc = doneProc;
			WaveInHelper.Try(WaveNative.waveInOpen(out m_WaveIn, device, format, m_BufferProc, 0, WaveNative.CALLBACK_FUNCTION));
			AllocateBuffers(bufferSize, bufferCount);
			for (int i = 0; i < bufferCount; i++)
			{
				SelectNextBuffer();
				m_CurrentBuffer.Record();
			}
			WaveInHelper.Try(WaveNative.waveInStart(m_WaveIn));
			m_Thread = new Thread(new ThreadStart(ThreadProc));
			m_Thread.Start();
		}
		~WaveInRecorder()
		{
			Dispose();
		}
		public void Dispose()
		{
			if (m_Thread != null)
				try
				{
					m_Finished = true;
					if (m_WaveIn != IntPtr.Zero)
						WaveNative.waveInReset(m_WaveIn);
					WaitForAllBuffers();
					m_Thread.Join();
					m_DoneProc = null;
					FreeBuffers();
					if (m_WaveIn != IntPtr.Zero)
						WaveNative.waveInClose(m_WaveIn);
				}
				finally
				{
					m_Thread = null;
					m_WaveIn = IntPtr.Zero;
				}
			GC.SuppressFinalize(this);
		}
		private void ThreadProc()
		{
			while (!m_Finished)
			{
				Advance();
				if (m_DoneProc != null && !m_Finished)
					m_DoneProc(m_CurrentBuffer.Data, m_CurrentBuffer.Size);
				m_CurrentBuffer.Record();
			}
		}
		private void AllocateBuffers(int bufferSize, int bufferCount)
		{
			FreeBuffers();
			if (bufferCount > 0)
			{
				m_Buffers = new WaveInBuffer(m_WaveIn, bufferSize);
				WaveInBuffer Prev = m_Buffers;
				try
				{
					for (int i = 1; i < bufferCount; i++)
					{
						WaveInBuffer Buf = new WaveInBuffer(m_WaveIn, bufferSize);
						Prev.NextBuffer = Buf;
						Prev = Buf;
					}
				}
				finally
				{
					Prev.NextBuffer = m_Buffers;
				}
			}
		}
		private void FreeBuffers()
		{
			m_CurrentBuffer = null;
			if (m_Buffers != null)
			{
				WaveInBuffer First = m_Buffers;
				m_Buffers = null;

				WaveInBuffer Current = First;
				do
				{
					WaveInBuffer Next = Current.NextBuffer;
					Current.Dispose();
					Current = Next;
				} while(Current != First);
			}
		}
		private void Advance()
		{
			SelectNextBuffer();
			m_CurrentBuffer.WaitFor();
		}
		private void SelectNextBuffer()
		{
			m_CurrentBuffer = m_CurrentBuffer == null ? m_Buffers : m_CurrentBuffer.NextBuffer;
		}
		private void WaitForAllBuffers()
		{
			WaveInBuffer Buf = m_Buffers;
			while (Buf.NextBuffer != m_Buffers)
			{
				Buf.WaitFor();
				Buf = Buf.NextBuffer;
			}
		}
	}
}

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, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Systems / Hardware Administrator
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions