csharp 在C#中生成WAV格式的dtmf音调

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了csharp 在C#中生成WAV格式的dtmf音调相关的知识,希望对你有一定的参考价值。

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

namespace ConversionLayer.Wave
{
  
	public enum AudioMode
	{
		Mono,
		Left,
		Right,
		LeftRight,
	}
	public enum Resolution
	{
		EightBit =	8, 
		SixteenBit = 16,
	}

	public class Wave
	{

		#region Fields

		#region Private Enumerations

		private enum WaveSegments
		{
			RIFF,
			WAVE,
			fmt,
		}
		private enum SoundFlags : int
		{
			SND_SYNC = 0x0000,            // play synchronously (default)
			SND_ASYNC = 0x0001,        // play asynchronously
			SND_NODEFAULT = 0x0002,        // silence (!default) if sound not found
			SND_MEMORY = 0x0004,        // pszSound points to a memory file
			SND_LOOP = 0x0008,            // loop the sound until next sndPlaySound
			SND_NOSTOP = 0x0010,        // don't stop any currently playing sound
			SND_NOWAIT = 0x00002000,        // don't wait if the driver is busy
			SND_ALIAS = 0x00010000,        // name is a registry alias
			SND_ALIAS_ID = 0x00110000,        // alias is a predefined id
			SND_FILENAME = 0x00020000,        // name is file name
			SND_RESOURCE = 0x00040004        // name is resource name or atom
		}

		#endregion

		private byte[] m_waveBytes;
		private Resolution m_resolution;
		private AudioMode m_audioMode;
		private double m_leftVolume;
		private double m_rightVolume;
		public static int HEADER_SIZE = 44;

			
		#endregion

		#region Properties
		
		public double LeftVolume
		{
			get { return m_leftVolume; }
		}
		public AudioMode AudioMode
		{
			get
			{
				return m_audioMode;
			}
			set
			{
				switch (value)
				{
					case AudioMode.Mono:
						m_waveBytes[22] = 1;
						m_waveBytes[23] = 0;						
						break;

					default:
						m_waveBytes[22] = 2;
						m_waveBytes[23] = 0;						
						break;
				}

				m_audioMode = value;
			}
		}	
		public double RightVolume
		{
			get { return m_rightVolume; }
		}
		public Resolution Resolution
		{
			get
			{
				return m_resolution;
			}
			set
			{
				m_waveBytes[34] = (byte) (short) value;
				m_waveBytes[35] = 0;
				this.m_resolution = value;
			}
		}
		internal byte[] Data
		{
			get
			{
				return this.m_waveBytes;
			}
		}

		/// <summary>
		///  Bytes 40 - 43  Length of Data   Samples * Channels * Bits per Sample / 8
		/// </summary>
		private int DataSize
		{
			get
			{			
				byte[] bytes = new byte[4];
				bytes[0] = this.m_waveBytes[40];
				bytes[1] = this.m_waveBytes[41];
				bytes[2] = this.m_waveBytes[42];
				bytes[3] = this.m_waveBytes[43];
				return ExtractInt(bytes);
			}

			set
			{
				this.m_waveBytes[40] = ExtractByte(value, 0);
				this.m_waveBytes[41] = ExtractByte(value, 1);
				this.m_waveBytes[42] = ExtractByte(value, 2);
				this.m_waveBytes[43] = ExtractByte(value, 3);

				//
				// Set the frame size :
				//						headerlength - 8 + datasize
				//							WHERE the 8 represents the header bytes before the FrameSizeMeasurement
				//
				this.FrameSize = (HEADER_SIZE - 8) + value;
			}
		}

		/// <summary>
		/// Bytes 04 - 07  Total Length to Follow  (Length of File - 8)
		/// </summary>
		private int FrameSize
		{
			get
			{
				byte[] bytes = new byte[4];
				bytes[0] = this.m_waveBytes[4];
				bytes[1] = this.m_waveBytes[5];
				bytes[2] = this.m_waveBytes[6];
				bytes[3] = this.m_waveBytes[7];
				return ExtractInt(bytes) ;
			}

			set
			{
				this.m_waveBytes[4] = ExtractByte(value, 0);
				this.m_waveBytes[5] = ExtractByte(value, 1);
				this.m_waveBytes[6] = ExtractByte(value, 2);
				this.m_waveBytes[7] = ExtractByte(value, 3);
			}
		}
		private int SampleRate
		{
			get
			{
				byte[] bytes = new byte[4];
				bytes[0] = this.m_waveBytes[24];
				bytes[1] = this.m_waveBytes[25];
				bytes[2] = this.m_waveBytes[26];
				bytes[3] = this.m_waveBytes[27];
				return ExtractInt(bytes);
			}

			set
			{
				this.m_waveBytes[24] = ExtractByte(value, 0);
				this.m_waveBytes[25] = ExtractByte(value, 1);
				this.m_waveBytes[26] = ExtractByte(value, 2);
				this.m_waveBytes[27] = ExtractByte(value, 3);
			}
		}
		private int BytesPerSecond
		{
			get
			{
				byte[] bytes = new byte[4];
				bytes[0] = this.m_waveBytes[28];
				bytes[1] = this.m_waveBytes[29];
				bytes[2] = this.m_waveBytes[30];
				bytes[3] = this.m_waveBytes[31];
				return ExtractInt(bytes);
			}

			set
			{
				this.m_waveBytes[28] = ExtractByte(value, 0);
				this.m_waveBytes[29] = ExtractByte(value, 1);
				this.m_waveBytes[30] = ExtractByte(value, 2);
				this.m_waveBytes[31] = ExtractByte(value, 3);
			}
		}
		private short BytesPerSample
		{			
			set
			{
				m_waveBytes[32] = (byte) value;
				m_waveBytes[33] = 0;
			}
		}
		#endregion

		#region Constructors

		private Wave( int p_sampleRate, Resolution p_resolution, AudioMode p_audioMode, double p_leftVolumeMix, double p_rightVolumeMix )
		{
			if (p_leftVolumeMix > 0.5 || p_rightVolumeMix > 0.5) throw new ArgumentException("The left and right Volume mixes cannot exceed 0.5");
			
			this.m_waveBytes = new byte[Wave.HEADER_SIZE];
			this.Resolution  = p_resolution;
			this.AudioMode   = p_audioMode;
			this.SampleRate  = p_sampleRate;

			this.m_leftVolume  = p_leftVolumeMix;
			this.m_rightVolume = p_rightVolumeMix;			
			
			BuildHeader();
		}

		#endregion

		#region Operators

		public static Wave operator +( Wave p_waveA, Wave p_waveB )
		{			
			if (p_waveA == null) return p_waveB;
			if (p_waveB == null) return p_waveA;

			if (p_waveA.AudioMode != p_waveB.AudioMode) throw new ArgumentException("Only Waves with identical settings can be appended to one another : Inconguous AudioMode");
			if (p_waveA.Resolution != p_waveB.Resolution) throw new ArgumentException("Only Waves with identical settings can be appended to one another : Inconguous Resolution");
			if (p_waveA.SampleRate != p_waveB.SampleRate) throw new ArgumentException("Only Waves with identical settings can be appended to one another : Inconguous SampleRate");
			
			
			Wave merged = new Wave(p_waveA.SampleRate, p_waveA.Resolution, p_waveA.AudioMode, p_waveA.LeftVolume, p_waveA.RightVolume);

			//
			// resize the target byte array to accomodate the merged data
			//
			merged.DataSize = p_waveA.DataSize + p_waveB.DataSize;
			System.Array.Resize(ref merged.m_waveBytes, merged.DataSize + Wave.HEADER_SIZE);



			//
			// Copy A's data
			//			
			//p_waveA.Data.CopyTo(merged.m_waveBytes, Wave.HEADER_SIZE);
			System.Array.Copy(p_waveA.Data, Wave.HEADER_SIZE, merged.m_waveBytes, Wave.HEADER_SIZE, p_waveA.DataSize);

			//
			// Append B's Array
			//
			System.Array.Copy(p_waveB.Data, Wave.HEADER_SIZE, merged.m_waveBytes, p_waveA.Data.Length, (p_waveB.Data.Length - Wave.HEADER_SIZE));

			Console.Out.WriteLine("Merged Total Size :" + merged.Data.Length.ToString());
			Console.Out.WriteLine("Merged Data Size :" + merged.DataSize.ToString());
			Console.Out.WriteLine("Merged Header Size :" + (merged.Data.Length - merged.DataSize).ToString());
			
			return merged;
		}

		#endregion

		#region Methods

		#region Private Methods

		private void BuildHeader()
		{
			//
			// Init headers
			//

			m_waveBytes[0] = 82; // R
			m_waveBytes[1] = 73; // I
			m_waveBytes[2] = 70; // F
			m_waveBytes[3] = 70; // F

			m_waveBytes[8] = 87; // W
			m_waveBytes[9] = 65; // A
			m_waveBytes[10] = 86; // V
			m_waveBytes[11] = 69; // E

			m_waveBytes[12] = 102; // f
			m_waveBytes[13] = 109; // m
			m_waveBytes[14] = 116; // t
			m_waveBytes[15] = 32; // .
			m_waveBytes[16] = 16; // Length of Format Chunk
			m_waveBytes[17] = 0; // Length of Format Chunk
			m_waveBytes[18] = 0; // Length of Format Chunk
			m_waveBytes[19] = 0; // Length of Format Chunk
			m_waveBytes[20] = 1; // Audio Format
			m_waveBytes[21] = 0; // Audio Format

			m_waveBytes[36] = 100; // d
			m_waveBytes[37] = 97; // a
			m_waveBytes[38] = 116; // t
			m_waveBytes[39] = 97; // a

			
			//--------------------------------------------------------------------------------
			// Set Bytes Per Second
			//											
			//
			//--------------------------------------------------------------------------------
			int audioChannels = 0;
			if (m_audioMode == AudioMode.Mono)
			{
				audioChannels = 1;
			}
			else
			{
				audioChannels = 2;
			}

			int sampleBytes = 1;
			if(m_resolution == Resolution.SixteenBit)
			{
				sampleBytes = 2;
			}
			this.BytesPerSecond = this.SampleRate * audioChannels * sampleBytes;	 			

			//-------------------------------------------------------------------------------
			// Bytes per Sample										Channels * Bits per Sample / 8
			//                                       1 = 8 bit Mono
			//                                       2 = 8 bit Stereo or 16 bit Mono
			//                                       4 = 16 bit Stereo
			//-------------------------------------------------------------------------------
			BytesPerSample = 2;
			if (this.m_audioMode == AudioMode.Mono && this.m_resolution == Resolution.EightBit)
			{				
				BytesPerSample = 1;
			}
			else if (this.m_audioMode != AudioMode.Mono && this.m_resolution == Resolution.SixteenBit)
			{				
				BytesPerSample = 4;				
			}
		}

		private byte ExtractByte( int p_int, int p_position )
		{
			int temp = p_int;
			short retValue = 0;

			if (p_position == 1)
			{
				//
				// shift right 8 bits.				
				//
				temp = (temp >> 8);
				retValue = (short) temp;
			}
			else if (p_position == 2)
			{
				//
				// shift right 16 bits.
				//		
				temp = (temp >> 16);				
				retValue = (short) temp;
			}
			else if (p_position == 3)
			{
				//
				// shift right 24 bits.
				//
				temp = (temp >> 24);				
				retValue = (short) temp;
			}
			else
			{
				//
				// only grab the first 8 bits (byte)
				//
				retValue = (short) (temp & 255);
			}
			return (byte) retValue;
		}

		private int ExtractInt( byte[] p_segment )
		{
			if (p_segment.Length != 4) throw new ArgumentException("4 bytes must be provided to convert to an int");

			int theValue = 0;
			int temp = (short) p_segment[0];
			theValue += temp;

			temp = ((short) p_segment[1]) << 8;
			theValue += temp;
			
			temp = ((short) p_segment[2]) << 16;
			theValue += temp;
			
			temp = ((short) p_segment[3]) << 24;
			theValue += temp;

			return theValue;
		}

		#endregion

		#region Public Methods

		public void PlayWave()
		{			
			IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(this.m_waveBytes, 0);
			PlaySound(ptr, UIntPtr.Zero, (uint) SoundFlags.SND_MEMORY);
		}

		public void SaveWave( string p_filePath )
		{
			FileStream writer = null;
			try
			{			
				writer = new FileStream(p_filePath, FileMode.Create, FileAccess.Write, FileShare.Write);
				writer.Write(this.m_waveBytes, 0, m_waveBytes.Length);
			}
			finally
			{
				if (writer != null) writer.Close();
			}			
		}

		#endregion

		#region Internal Methods

		//
		// Used in testing only
		//
		internal void AppendSimple()
		{
			int limit = 127;
			int dataSize	 = 8000;
			int sampleRate = 8000;
			int samples = 8000;
			double frequencyA = 1209;
			double frequencyB = 697;

			double waveTimeA = 1 / frequencyA;
			double waveTimeB = 1 / frequencyB;
			double sampleTime = 1 / sampleRate;
			double frequencyASlice = (2 * Math.PI) / (waveTimeA / sampleTime);
			double frequencyBSlice = (2 * Math.PI) / (waveTimeB / sampleTime);

			System.Array.Resize(ref this.m_waveBytes, 44 + dataSize);

			//-------------------------------------------------------------------------------
			// Bytes 40 - 43  Length of Data   Samples * Channels * Bits per Sample / 8
			//-------------------------------------------------------------------------------						
			this.m_waveBytes[40] = ExtractByte(dataSize, 0);
			this.m_waveBytes[41] = ExtractByte(dataSize, 1);
			this.m_waveBytes[42] = ExtractByte(dataSize, 2);
			this.m_waveBytes[43] = ExtractByte(dataSize, 3);

			//-------------------------------------------------------------------------------
			// Bytes 04 - 07  Total Length to Follow  (Length of File - 8)
			//-------------------------------------------------------------------------------
			int fileSize = dataSize + 36;

			m_waveBytes[4] = ExtractByte(fileSize, 0);
			m_waveBytes[5] = ExtractByte(fileSize, 1);
			m_waveBytes[6] = ExtractByte(fileSize, 2);
			m_waveBytes[7] = ExtractByte(fileSize, 3);

			//-----------------------------------------------------------------------
			// 8 Bit Sample Data
			// 128 + 63*sin(n*2*pi*f1/8000) + 63*sin(n*2*pi*f2/8000)
			//-----------------------------------------------------------------------
			for (int i = 0; i < samples; i++)
			{
				double sample = 128 + 63 * Math.Sin(i * 2 * Math.PI * frequencyA / 8000);
				sample = sample + (63 * Math.Sin(i * 2 * Math.PI * frequencyB / 8000));
				this.m_waveBytes[44 + i] = ExtractByte(Convert.ToInt32(sample), 0);			
			}
		}
		internal void AppendSample(SineWave[] p_sineWaves, TimeSpan p_duration)
		{
			double sampleTime = 1 / Convert.ToDouble(this.SampleRate);
			double seconds		= p_duration.TotalMilliseconds / 1000.0;
			int samples				= Convert.ToInt32(seconds / sampleTime);

			foreach (SineWave sine in p_sineWaves)
			{
				double waveTime	= 1 / sine.Frequency;				
				sine.DataSlice  = (2 * Math.PI) / (waveTime / sampleTime);

				//
				// if inserting silence
				//
				if (sine.Frequency == 0.0)
				{
					sine.DataSlice = 0.0;
				}
			}

			//
			// Set the dataSize measurement for the wav to be created
			// no of samples * sample size bytes
			//
			this.DataSize = samples * (((int) m_resolution) / 8);

			if(this.m_audioMode != AudioMode.Mono)
				DataSize = samples * 2 * (((int) m_resolution) / 8);

			//
			// Resize the array to accomodate the new samples
			//
			int startIndex = this.m_waveBytes.Length;
			System.Array.Resize(ref this.m_waveBytes, startIndex + DataSize);
			
		
			//-------------------------------------------------------------------------------
			// Bytes 44 - End of array   Data Samples
			//-------------------------------------------------------------------------------		

			for (int i = 0; i < samples; i++)
			{
				int limit = 127;			
				double dataPtLeft = 0;
				double dataPtRight = 0;

				//
				// Set the volume for the left and right channels
				//
				foreach (SineWave sine in p_sineWaves)
				{
					dataPtLeft += (Math.Sin(i * sine.DataSlice) * sine.LeftAmplitude);
					dataPtRight += (Math.Sin(i * sine.DataSlice) * sine.RightAmplitude);
				}

				//
				// 8 Bit Data
				//
				if (this.m_resolution == Resolution.EightBit)
				{
					//
					// Normalize the data for left and right channels
					//
					int dataLeft = Convert.ToInt32(dataPtLeft * m_leftVolume * limit) + limit;
					int dataRight = Convert.ToInt32(dataPtRight * m_rightVolume * limit) + limit;

					//
					// Write sample data to bytes collection
					//
					if (this.m_audioMode == AudioMode.Mono)
					{
						this.m_waveBytes[startIndex + i] = ExtractByte(dataLeft, 0);
					}
					else if (this.m_audioMode == AudioMode.LeftRight)
					{
						this.m_waveBytes[startIndex + (i * 2)] = ExtractByte(dataLeft, 0);					// 44
						this.m_waveBytes[(startIndex + 1) + (i * 2)] = ExtractByte(dataRight, 0);   // 45
					}
					else if (this.m_audioMode == AudioMode.Left)
					{
						this.m_waveBytes[startIndex + (i * 2)] = ExtractByte(dataLeft, 0);
						this.m_waveBytes[(startIndex + 1) + (i * 2)] = 0;
					}
					if (this.m_audioMode == AudioMode.Right)
					{
						this.m_waveBytes[startIndex + (i * 2)] = 0;
						this.m_waveBytes[(startIndex + 1) + (i * 2)] = ExtractByte(dataRight, 0);
					}
				}
				//
				// 16 bit data
				//
				else
				{
					limit = 32767;

					//
					// Normalize the data for left and right channels
					//
					int dataLeft = Convert.ToInt32(dataPtLeft * m_leftVolume * limit);
					int dataRight = Convert.ToInt32(dataPtRight * m_rightVolume * limit);

					if(AudioMode == AudioMode.Mono)
					{
						this.m_waveBytes[startIndex + (i*2)]			 = ExtractByte(dataLeft, 0);
						this.m_waveBytes[(startIndex + 1) + (i*2)] = ExtractByte(dataLeft, 1);
					}
					else if(AudioMode == AudioMode.LeftRight)
					{
						this.m_waveBytes[startIndex + (i*4)]			 = ExtractByte(dataLeft, 0);
						this.m_waveBytes[(startIndex + 1) + (i*4)] = ExtractByte(dataLeft, 1);
						this.m_waveBytes[(startIndex + 2) + (i*4)] = ExtractByte(dataRight, 1);
						this.m_waveBytes[(startIndex + 3) + (i*4)] = ExtractByte(dataRight, 1);
					}
					else if(AudioMode == AudioMode.Left)
					{
						this.m_waveBytes[startIndex + (i*4)]			 = ExtractByte(dataLeft, 0);
						this.m_waveBytes[(startIndex + 1) + (i*4)] = ExtractByte(dataLeft, 1);
						this.m_waveBytes[(startIndex + 2) + (i*4)] = 0;
						this.m_waveBytes[(startIndex + 3) + (i*4)] = 0;
					}
					else if(AudioMode == AudioMode.Right)
					{
						this.m_waveBytes[startIndex + (i*4)]			 = 0;
						this.m_waveBytes[(startIndex + 1) + (i*4)] = 0;
						this.m_waveBytes[(startIndex + 2) + (i*4)] = ExtractByte(dataRight, 0);
						this.m_waveBytes[(startIndex + 3) + (i*4)] = ExtractByte(dataRight, 1);
					}
				}
			}


		}

		#endregion

		#endregion

		#region Static Methods

		internal static Wave ConstructWave( SineWave[] p_waves )
		{
			Wave wave = new Wave(16000, Resolution.SixteenBit, AudioMode.Mono, 0.5, 0.5);
			wave.AppendSample(p_waves, TimeSpan.FromSeconds(0.25));			
			return wave;
		}
		internal static Wave ConstructWave( SineWave[] p_waves, int p_sampleRateHZ, Resolution p_resolution, AudioMode p_audioMode, TimeSpan p_duration )
		{
			Wave wave = new Wave(p_sampleRateHZ, p_resolution, p_audioMode, 0.5, 0.5);
			wave.AppendSample(p_waves, p_duration);
			return wave;
		}

		#endregion

		#region External Methods

		[DllImport("winmm.dll", SetLastError = true)]
		static extern bool PlaySound( string pszSound, System.UIntPtr hmod, uint fdwSound );

		[DllImport("winmm.dll", SetLastError = true)]
		static extern bool PlaySound( IntPtr pszSound, System.UIntPtr hmod, uint fdwSound );

		#endregion 
		
	}
}
using System;
using System.Collections.Generic;
using System.Text;

namespace ConversionLayer.Wave
{
  internal class SineWave
	{
		#region Fields

		private double m_frequency;
		private double m_dataSlice;
		private double m_leftAmplitude;
		private double m_rightAmplitude;

		#endregion

		#region Properties

		public double RightAmplitude
		{
			get { return m_rightAmplitude; }
			set { m_rightAmplitude = value; }
		}

		public double LeftAmplitude
		{
			get { return m_leftAmplitude; }
			set { m_leftAmplitude = value; }
		}

		public double DataSlice
		{
			get { return m_dataSlice; }
			set { m_dataSlice = value; }
		}

		public double Frequency
		{
			get { return m_frequency; }
			set { m_frequency = value; }
		}

		#endregion

		#region Constructors

		internal SineWave( double p_frequency, double p_leftAmplitude, double p_rightAmplitude )
		{
			this.m_frequency = p_frequency;			
			this.m_leftAmplitude  = p_leftAmplitude;
			this.m_rightAmplitude = p_rightAmplitude;
			
		}

		#endregion

	}
}

以上是关于csharp 在C#中生成WAV格式的dtmf音调的主要内容,如果未能解决你的问题,请参考以下文章

如何从 DTMF 音调中获得低频 [关闭]

在 Python 中生成音调而不冻结线程?

在 Haskell 中生成 .wav 声音数据

csharp 在C#中生成一个随机数

使用 C# 从字节数组中解码 dtmf

如何使用 pydub 库从 mp3 文件中生成带有 G.711alaw 的 wav?