⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 waveutility.cs

📁 visual c++编写关于声音分析的 傅立叶变换.超牛
💻 CS
字号:
using System;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows.Forms;

namespace SteganoTape {
	public class WaveUtility {

		/// <summary>Called by FindBeeps for every Beep detected</summary>
		public event BeepFoundHandler BeepFound;

		private WaveSound waveSound;
		private const int beepLength = 32; // 1/32 second

		public WaveSound WaveSound
		{
			get { return waveSound; }
		}

		public WaveUtility(WaveSound waveSound)
		{
			this.waveSound = waveSound;
		}
		
		public WaveUtility(String fileName)
		{
			this.waveSound = ReadFile(fileName);
		}

		/// <summary>Creates sine sound of a specific frequency</summary>
		/// <param name="frequencyHz">Frequency in Hertz</param>
		/// <param name="volumne">Amplitude</param>
		/// <returns></returns>
		private WaveSound CreateBeep(int frequencyHz, int volumne)
		{
			// samples for 1/beepLength seconds
			short[] samples = new short[waveSound.Format.SamplesPerSec / beepLength];
			double xValue = 0;
			short yValue;

			double xStep = (2 * Math.PI) / waveSound.Format.SamplesPerSec; // 1 Hz
			xStep = xStep * frequencyHz;
			
			for (int n = 0; n < samples.Length; n++) {
				xValue += xStep;
				yValue = (short)(Math.Sin(xValue) * volumne);
				samples[n] = yValue;
			}

			WaveSound beep = new WaveSound(waveSound.Format, samples);
			return beep;
		}

		/// <summary>Read a wave file</summary>
		/// <param name="fileName"></param>
		/// <returns></returns>
		private WaveSound ReadFile(String fileName)
		{
			BinaryReader reader = new BinaryReader(new FileStream(fileName, FileMode.Open));

			WaveFormat format = ReadHeader(reader);
			int dataLength = reader.ReadInt32();
			
			//Test
			dataLength = (int)(reader.BaseStream.Length - reader.BaseStream.Position);

			int maxSampleValue = 0;
			int bytesPerSample = format.BitsPerSample / 8;
			int countSamples = dataLength / bytesPerSample;

			sbyte[] channelSamples8 = new sbyte[countSamples];
			short[] channelSamples16 = new short[countSamples];

			int channelSamplesIndex = 0;
			int absValue;
			for (int sampleIndex = 0; sampleIndex < countSamples; sampleIndex++) {

				if (format.BitsPerSample == 8) {
					channelSamples8[channelSamplesIndex] = reader.ReadSByte();
					if (Math.Abs((int)channelSamples8[channelSamplesIndex]) > maxSampleValue) {
						maxSampleValue = Math.Abs((int)channelSamples8[channelSamplesIndex]);
					}
				} else {
					channelSamples16[channelSamplesIndex] = reader.ReadInt16();
					absValue = Math.Abs((int)channelSamples16[channelSamplesIndex]);
					if (absValue > maxSampleValue) {
						maxSampleValue = (absValue > short.MaxValue) ? absValue - 1 : absValue;
					}
				}

				channelSamplesIndex++;
			}

			reader.Close();

			if (format.BitsPerSample == 8) {
				for (int n = 0; n < channelSamples8.Length; n++) {
					channelSamples16[n] = (short)channelSamples8[n];
				}
			}

			return new WaveSound(format, channelSamples16);
		}

		/// <summary>Read a chunk of four bytes from a wave file</summary>
		/// <param name="reader"></param>
		/// <returns></returns>
		private string ReadChunk(BinaryReader reader)
		{
			byte[] ch = new byte[4];
			reader.Read(ch, 0, ch.Length);
			return System.Text.Encoding.ASCII.GetString(ch);
		}

		/// <summary>
		/// Read the header from a wave file, and move the
		/// reader's position to the beginning of the data chunk
		/// </summary>
		/// <param name="reader"></param>
		/// <returns></returns>
		private WaveFormat ReadHeader(BinaryReader reader)
		{
			WaveFormat format = new WaveFormat();

			if (ReadChunk(reader) != "RIFF") {
				throw new Exception("Invalid file format");
			}

			reader.BaseStream.Seek(4, SeekOrigin.Current); // .ReadInt32();

			if (ReadChunk(reader) != "WAVE") {
				throw new ArgumentException("Kein WAVE-Stream", "reader");
			}

			if (ReadChunk(reader) != "fmt ") {
				throw new ArgumentException("Format-Chunk nicht gefunden", "reader");
			}

			int len = reader.ReadInt32();
			if (len < 16) {
				throw new ArgumentException("Format-Chunk hat eine ung黮tige L鋘ge", "reader");
			}

			format.FormatTag = reader.ReadInt16();
			format.Channels = reader.ReadInt16();
			format.SamplesPerSec = reader.ReadInt32();
			format.AvgBytesPerSec = reader.ReadInt32();
			format.BlockAlign = reader.ReadInt16();
			format.BitsPerSample = reader.ReadInt16();

			// go to beginning of wave samples
			while (reader.BaseStream.Position < reader.BaseStream.Length && ReadChunk(reader) != "data")
				;

			return format;
		}

		/// <summary>Find anything but silence in the sound</summary>
		/// <remarks>Raises the BeepFound event everytime a sound is detected between two blocks of silence</remarks>
		/// <param name="tolerance">
		/// Sample values greater than [tolerance] are sound,
		/// sample values less than [tolerance] are silence
		/// </param>
        public void FindAnything(short tolerance) {
            //size of scan window in samples
			int scanWindowSize = waveSound.Format.SamplesPerSec / beepLength;
            //size of scan window in seconds
            float scanWindowSizeSeconds = (float)scanWindowSize / (float)waveSound.Format.SamplesPerSec;

            int startIndex = -1;
            int endIndex = -1;
            int countSilentSamples = 0;
            for (int n = 0; n < waveSound.Count; n++) {
				if (Math.Abs(WaveSound[n]) > tolerance) { //found a sound
                    countSilentSamples = 0;
                    if(startIndex < 0){
                        startIndex = n;
                    }
                } else if (startIndex > -1) { //searched and found silence
                    countSilentSamples++;
                    if (countSilentSamples == scanWindowSize) {
                        endIndex = n - scanWindowSize;
						NotifyOnBeep(startIndex, endIndex, scanWindowSizeSeconds);

                        //scan next time window
                        countSilentSamples = 0;
                        startIndex = -1;
                    }
                }
            }

            if (startIndex > -1) { //wave ends with a beep
				NotifyOnBeep(startIndex, waveSound.Count-1, scanWindowSizeSeconds);
            }
        }

		/// <summary>Raise the BeepFound event</summary>
		/// <param name="startIndex">Index of the sound's first sample</param>
		/// <param name="endIndex">Index of the sound's last sample</param>
		/// <param name="silentSeconds"></param>
		private void NotifyOnBeep(int startIndex, int endIndex, float silentSeconds) {
			if (BeepFound != null) {
				//get the second in the wave at which the sound stops
				float second = (float)endIndex / (float)waveSound.Format.SamplesPerSec;

				//notify
				BeepFound(this, new BeepFoundEventArgs(
					new Beep(startIndex, endIndex,
					second - silentSeconds, second)));
			}
		}

		/// <summary>Replaces a part of the sound with a beep</summary>
		/// <param name="insertAtSecond">Where to put the beep</param>
		/// <param name="frequencyHz">Frequency of the beep in Hertz</param>
		/// <param name="volumne">Maximum sample value of the beep</param>
		public void InsertBeep(float insertAtSecond, int frequencyHz, int volumne)
		{
			short[] beepWave = CreateBeep(frequencyHz, volumne).Samples;
			int insertAtSample = (int)(waveSound.Format.SamplesPerSec * insertAtSecond);
			int longWaveIndex = insertAtSample;
			for (int index = 0; index < beepWave.Length; index++) {
				waveSound[longWaveIndex] = beepWave[index];
				longWaveIndex++;
			}
		}

		/// <summary>Get the minimum duration a sound must have in order to hide [message]</summary>
		/// <param name="message"></param>
		/// <returns></returns>
		public long CountRequiredSeconds(Stream message)
		{
			message.Position = 0;

			long countSeconds = 0;
			int messageByte;
			byte highHalfByte;
			byte lowHalfByte;

			while ((messageByte = message.ReadByte()) > -1) {
				highHalfByte = (byte)(messageByte >> 4);
				lowHalfByte = (byte)(messageByte - (highHalfByte << 4));

				//intervals of 0 seconds are not possible -> add 1 to all intervals
				countSeconds += highHalfByte + 1;
				countSeconds += lowHalfByte + 1;
			}

			return countSeconds;
		}

		/// <summary>Split the bytes of a message into four-bit-blocks</summary>
		/// <param name="message">Stream containing the message</param>
		/// <returns>Stream containing the same message with two bytes per original byte</returns>
		private Stream PrepareMessage(Stream message)
		{
			message.Position = 0;
			
			MemoryStream preparedMessage = new MemoryStream();
			int messageByte;
			int highHalfByte;
			int lowHalfByte;
			
			while ((messageByte = message.ReadByte()) > -1) {
				//split into high and low part
				highHalfByte = (messageByte >> 4);
				lowHalfByte = (messageByte - (highHalfByte << 4));

				//intervals of 0 seconds are not possible -> add 1 to all intervals
				preparedMessage.WriteByte((byte)(highHalfByte + 1));
				preparedMessage.WriteByte((byte)(lowHalfByte + 1));
			}

			preparedMessage.Position = 0;
			return preparedMessage;
		}

		/// <summary>Hide a message in the wave</summary>
		/// <param name="message">Stream containing the message</param>
		/// <param name="frequencyHz">Frequency of the beeps, which will be inserted into the sound</param>
		/// <param name="volumne">Maximum sample value of the beeps</param>
		public void Hide(Stream message, int frequencyHz, int volumne)
		{
			Stream preparedMessage = PrepareMessage(message);
			int messageByte;
			int offset = 0;
			while ((messageByte = preparedMessage.ReadByte()) > -1) {
				offset += messageByte;
				InsertBeep(offset, frequencyHz, volumne);
			}
		}

		/// <summary>Read a message from a series of seconds</summary>
		/// <param name="seconds">Seconds at which beeps have been found in the sound</param>
		/// <returns>Stream containing the decoded message</returns>
		public Stream Extract(Collection<float> seconds)
		{
			MemoryStream message = new MemoryStream();

			byte interval;
			int second;
			int previousSecond = 0;
			byte messageByte = 0;
			bool isLowPart = false;
			foreach(float floatSecond in seconds) {
				second = (int)Math.Round(floatSecond);
				try {
					interval = (byte)(second - previousSecond);
					if (interval > 16) { interval = 16; }
				} catch (OverflowException ex) {
					Console.WriteLine(ex);
					interval = 16; //highest possible value for a half byte + 1
				}
				
				if (isLowPart) {
					messageByte += (byte)(interval - 1);
					message.WriteByte(messageByte);
				}else{
					messageByte = (byte)((interval-1) << 4);
				}

				isLowPart = !isLowPart;
				previousSecond = second;
			}

			message.Position = 0;
			return message;
		}

		/// <summary>Let "Sound Exchange" convert the sound</summary>
		/// <param name="soxPath">Path and Name of sox.exe</param>
		/// <param name="soxArguments">Arguments for sox.exe</param>
		private void RunSox(String soxPath, String soxArguments)
		{
			if (Application.OpenForms.Count > 0) { //there is a visible form
				Application.OpenForms[0].Cursor = Cursors.WaitCursor;
			}

			try {

				ProcessStartInfo startInfo = new ProcessStartInfo(soxPath, soxArguments);
				startInfo.RedirectStandardError = true;
				startInfo.UseShellExecute = false;
				Process sox = Process.Start(startInfo);

				StreamReader errorReader = sox.StandardError;
				String errors = errorReader.ReadLine();
				if (errors != null) {
					throw new ApplicationException("sox failed: " + errors);
				}

				sox.WaitForExit(10000);

			} finally {
				if (Application.OpenForms.Count > 0) { //reset cursor for the visible form
					Application.OpenForms[0].Cursor = Cursors.Default;
				}
			}
		}

		/// <summary>Convert an 8 bit sound to 16 bit.</summary>
		/// <param name="tempDirectory">Path of the directory for temporary files</param>
		/// <param name="soxPath">Path and Name of sox.exe</param>
		public void ConvertToDefaultFormat(String tempDirectory, String soxPath)
		{
			String inFileName = Path.Combine(tempDirectory, "in.wav");
			String outFileName = Path.Combine(tempDirectory, "out.wav");
			int fileLength = this.WaveSound.SaveToFile(inFileName);

			String soxArguments = String.Format(
				"-t wav \"{0}\" -t .wav -c 1 -s -w \"{1}\"",
				inFileName,
				outFileName);

			RunSox(soxPath, soxArguments);

			this.waveSound = ReadFile(outFileName);
		}

		/// <summary>Let "Sound Exchange" perform a band pass filter on the sound</summary>
		/// <param name="tempDirectory">Path of the directory for temporary files</param>
		/// <param name="soxPath">Path and Name of sox.exe</param>
		/// <param name="frequency">Frequency that may pass the filter</param>
		/// <returns>Path of the output file</returns>
		public String FindFrequency(String tempDirectory, String soxPath, int frequency)
		{
			String inFileName = Path.Combine(tempDirectory, "in.wav");
			String outFileName = Path.Combine(tempDirectory, "out.wav");
			int fileLength = this.WaveSound.SaveToFile(inFileName);

			String soxArguments = String.Format(
				"-t wav \"{0}\" -t .wav -c 1 -s -w \"{1}\" band {2} 10",
				inFileName,
				outFileName,
				frequency);

			RunSox(soxPath, soxArguments);

			return outFileName;
		}

	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -