Acquisition de la voix avec visualisation du signal

Description

Mon programme récupère le signal provenant d'un micro. Il affiche sous forme de graphique l'état du signal en live. Le son est enregistrer au format WAVE

Source / Exemple :


using System;
using System.Runtime.InteropServices; // For the DLLImport 
using System.Text;      // For the SrtingBuilders
using System.Threading;     // To use threads
using System.IO;      // To use files
using AudioRepresentationClass;
using Softphone;

namespace VoiceStreamsFunctions 
{
 /// <summary>
 /// VoiceStreamsFunctions = it containes the functions related to retreive the voice and save it in a wave file
 /// </summary>
 
 #region Aliases
 using HWAVEIN = System.IntPtr; // Handler on Input Wave
 using HWAVEOUT = System.IntPtr; // Handler on Ouout Wave
 using WAVEHDR = System.IntPtr; // Pointer on a structure of a Wave file
 #endregion

 #region Wave Format Tag
 public enum WaveFormatTag : ushort
 {
  UNKNOWN                    = 0x0000, /* Microsoft Corporation */
  PCM         = 0x0001, /* Microsoft Corporation */

  EXTENSIBLE                 = 0xFFFE /* Microsoft */
 }
 #endregion

 public class VoiceFunction 
 {

  #region DLL Import
  /// <summary>
  /// DLL Import :
  /// There are many because we use the CLS Multimedia functions.
  /// </summary>
  
  [DllImport ("winmm.dll")]  
  public static extern int mciSendString(String s1, StringBuilder s2, int l1, int l2);  // Multimedia Control Interface : Command Line
  [DllImport ("winmm.dll")]  
  public static extern int mciGetErrorString(int l1, StringBuilder s1, int l2);    // Error Handler
  
  [DllImport ("winmm.dll")]                 // Open the communication with the MIC
  public static extern int waveInOpen(out IntPtr hWaveIn, int uDeviceID, ref WAVEFORMATEX lpFormat, FunctionDelegate dwCallback, IntPtr dwInstance, int dwFlags);
  // hWaveIn is a pointer to the handler
  // lpFormat is a pointer to WAVEFORMATEX structure
  // dwCallback specifies the address of the callback function
  // dwInstance specifies user-instance data passed to the callback mechanism
  // dwFlags is a flag for opening the device
  
  [DllImport ("winmm.dll")]
  public static extern int waveInStart( IntPtr hWaveIn );          // Starts recording
  [DllImport ("winmm.dll")] 
  public static extern int waveInAddBuffer (IntPtr hWaveIn, ref WAVEHDR pWaveHDR, uint cbwh); // Buffer Handler
  [DllImport ("winmm.dll")] 
  public static extern int waveInStop( IntPtr hWaveIn );          // Stop recording
  [DllImport ("winmm.dll")] 
  public static extern int waveInClose(IntPtr hWaveIn);          // Close the communication with the MIC
  [DllImport ("winmm.dll")] 
  public static extern int waveInPrepareHeader(IntPtr hWaveIn, ref WAVEHDR pWaveHDR, uint cbwh );  // Prepare the header to create a wave file
  [DllImport ("winmm.dll")] 
  public static extern int waveInUnprepareHeader( IntPtr hWaveIn, ref WAVEHDR pWaveHDR, uint cbwh );  // Cleans up the header
  #endregion
  
  // Variables
  private string WorkingDir;     // Working directory
  private HWAVEOUT hWaveIn;     // Handler to WAVE IN
  
  private IntPtr dwInstance;     // Pointeur on callback instance
  private int dwFlags;      // Openning Flag
  private WAVEFORMATEX StrcutWaveFMT;   // Wave format
  private WAVEHDR StructWaveHeader;   // Buffer handle
  private StringBuilder errorBuffer;   // Error Buffer
  private bool StillRecording;    // To check if we are currently recording
  private object MyLock;      // Lock for variable acces in mutlithread environment
  private GCHandle HeaderDataHandle;   // To recover data from unmanaged memory
  private int BufSize;      // Recording buffer
  private AudioRepresentation DFunct;   // To call the audio representation class
  private System.Windows.Forms.TextBox textBoxVFunct;
  private byte[] ByteArray;     // Array which contains A DATA CHUNK sent by the MIC
  private byte[] TempArray;     // Array which contains ALL THE DATA sent by the MIC
  private int n;        // index for storing in TempArray
  private int Index;       // index for storing in TempArray
  private char FileSize1;
  private char FileSize2;
  private char FileSize3;
  private char FileSize4;

  // Callback
  public delegate void FunctionDelegate(IntPtr hWaveIn, uint Msg, int dwInstance, ref WAVEHDR dwParam1, int dwParam2);
  private FunctionDelegate dwCallback;
  

  #region ThreadHandle
  Thread RecordingThread;   // Thread which handles the recording
  Thread RecordHandleThread;  // It launches the record function
  #endregion
  
  #region Constants
   private const int WAVE_MAPPER   = -1;     // ID of the default MIC
   private const int WHDR_PREPARED  = 0;     // States that buffers are prepared by windows multimedia function
   private const int MM_WIM_OPEN   = 0x3BE;    // Message send by the audio device when it is opened by the application
   private const int MM_WIM_CLOSE  = 0x3BF;    // Message send by the audio device when it is closed by the application
   private const int MM_WIM_DATA   = 0x3C0;    // Message send by the audio device when a buffer is full
   private const int CALLBACK_FUNCTION = 0x30000;    // I specify that the callback is handled by a function
   private const int MaxRecord   = 512000;    // FOR DEBUG ONLY !
  #endregion

  #region Struct WAVEHDR 
  public struct WAVEHDR
  {
   public IntPtr  lpData; 
   public int   dwBufferLength; 
   public int   dwBytesRecorded; 
   public IntPtr    dwUser;
   public int   dwFlags; 
   public int   dwLoops; 
   public IntPtr  lpNext;     // Reserved
   public IntPtr  reserved;    // Reserved

   public static WAVEHDR Empty
   {
    get{return new WAVEHDR();}
   }
   public static int SizeOfEmptyWAVEHDR ()
   {
    return 28;
   }
  }
  #endregion 

  #region Struct WAVEFORMATEX
  public struct WAVEFORMATEX
  {
   public WaveFormatTag formatTag;
   public short   nChannels;
   public int    nSamplesPerSec;
   public int    nAvgBytesPerSec;
   public short   nBlockAlign;
   public short   wBitsPerSample;
   public short   cbSize;
   public byte    extraInfo;

   public WAVEFORMATEX(int rate, int bits, int channels)
   {
    formatTag  = WaveFormatTag.PCM;
    nChannels  = (short)channels;
    nSamplesPerSec = rate;
    wBitsPerSample = (short)bits;
    cbSize   = 0;
    nBlockAlign  = (short)(channels * (bits / 8));
    nAvgBytesPerSec = nSamplesPerSec * nBlockAlign;
    extraInfo  = 0;
   }

   public static WAVEFORMATEX Empty
   {
    get{return new WAVEFORMATEX();}
   }

   public static int SizeOfEmpty
   {
    get{return 18;}
   }

   public int SizeOf
   {
    get{return (SizeOfEmpty + cbSize);}
   }

  }
  #endregion
  
  // Initialise
  public VoiceFunction(string wdir, int BufferSize, System.Windows.Forms.TextBox textBox, AudioRepresentation DisplayFunct) 
  {
   this.WorkingDir       = wdir;        // I set the working directory for my functions
   this.errorBuffer      = new StringBuilder(128,128);   // Juste in case ;)
   this.hWaveIn       = IntPtr.Zero;
   this.dwCallback       = new FunctionDelegate(waveInProc); // Pointeur on callback function
   this.dwInstance       = IntPtr.Zero;
   this.dwFlags       = CALLBACK_FUNCTION;
   this.RecordingThread     = new Thread(new ThreadStart( StartRec ));
   this.RecordingThread.Name    = "PickUpSound";
   this.RecordHandleThread     = new Thread(new ThreadStart( RecordHandle ));
   this.RecordHandleThread.Name   = "RecordManager";
   this.StillRecording      = false;
   this.BufSize       = BufferSize;
   this.MyLock        = new object();  // Lock for variable access in mutlithread environment
   this.TempArray       = new byte[MaxRecord]; // I store everything and I will write in a file afterwards
   this.n         = -1;     // index for recording
   this.Index        = 0;
   this.textBoxVFunct      = textBox;
   this.DFunct        = DisplayFunct;
  }

  // Record Launcher, called by the main
  public void RecordLauncher()
  {
   this.RecordHandleThread.Start(); // I launch the thread which will handle all the recording process
  }

  // Record Handle
  private void RecordHandle()
  {
   int RetVal = 0;

   PrepareFormat();
   RetVal = OpenComm();
   //this.DFunct = new AudioRepresentation(this.BufSize);
   if(RetVal ==1)
   {
    Record();
   }

   // The end of the recording is handled by the user, when she clicks on the appropriate button
  }

  // Prepare the format
  private void PrepareFormat()
  {
   this.StrcutWaveFMT     = WAVEFORMATEX.Empty;

   // Init wave format
   this.StrcutWaveFMT.formatTag  = WaveFormatTag.PCM;
   this.StrcutWaveFMT.nChannels  = 1;    // MONO
   this.StrcutWaveFMT.nSamplesPerSec = 11025;   // Samples per Second  
   this.StrcutWaveFMT.wBitsPerSample = 8;    // Bits per Sample
   this.StrcutWaveFMT.nBlockAlign  = (short)(StrcutWaveFMT.nChannels * (StrcutWaveFMT.wBitsPerSample/8));
   this.StrcutWaveFMT.nAvgBytesPerSec = StrcutWaveFMT.nSamplesPerSec * StrcutWaveFMT.nBlockAlign;
   this.StrcutWaveFMT.cbSize   = 0;
  }

  // Open the communication with the audio input device, error ID N°1
  private int OpenComm ()
  {
   int ReturnVal = 0;

   // WAVE_MAPPER is a constant defined in mmsystem.h at -1, it take the default MIC
   int err = waveInOpen(out this.hWaveIn, WAVE_MAPPER, ref this.StrcutWaveFMT, this.dwCallback, this.dwInstance, this.dwFlags);
   if (err != 0) 
   {    
    int retval = mciGetErrorString(err, errorBuffer, 128);
    this.textBoxVFunct.Text = "WaveIn error 1";
   } 
   else ReturnVal =1;
   return(ReturnVal);
  }

  // Record, error ID N°3, 4 and 5
  private void Record ()
  {
   int RetVal = 0;

   Monitor.Enter(MyLock);
   RetVal = PrepareHeader ();  // Error 3
   if(RetVal == 1)
   {
    PrepareBuffer ();   // Error 4
   }
     
   Monitor.Exit(MyLock);

   // We create a thread to handle all the recording
   this.StillRecording = true;
   this.RecordingThread.Priority = ThreadPriority.Highest;
   this.RecordingThread.Start();
   this.RecordingThread.Join();
  } 

  // Prepare Header
  private int PrepareHeader ()
  {
   int RetVal = 0;

   // Prepare the buffer
   this.StructWaveHeader     = WAVEHDR.Empty;
   this.StructWaveHeader.dwBufferLength = BufSize;
   this.StructWaveHeader.dwFlags   = WHDR_PREPARED;

   this.ByteArray = new byte[BufSize];
   HeaderDataHandle = GCHandle.Alloc(this.ByteArray, GCHandleType.Pinned);
   this.StructWaveHeader.lpData = HeaderDataHandle.AddrOfPinnedObject();
   this.StructWaveHeader.dwUser = (IntPtr)GCHandle.Alloc(this);

   int IntTmp = Marshal.SizeOf(this.StructWaveHeader);
   uint UIntTmp = Convert.ToUInt32(IntTmp);

   int err = waveInPrepareHeader(this.hWaveIn, ref this.StructWaveHeader, UIntTmp);
   if (err != 0) 
   {    
    int retval = mciGetErrorString(err, errorBuffer, 128);
    this.textBoxVFunct.Text = "WaveIn error 3";
   }
   else RetVal = 1;
   return(RetVal);
  }

  // Unprepare Header
  private void UnPrepareHeader()
  {
   int IntTmp = BufSize + WAVEHDR.SizeOfEmptyWAVEHDR();
   uint UIntTmp = Convert.ToUInt32(IntTmp);

   int err = waveInUnprepareHeader(this.hWaveIn, ref this.StructWaveHeader, UIntTmp);
   if (err != 0) 
   {    
    int retval = mciGetErrorString(err, errorBuffer, 128);
    this.textBoxVFunct.Text = "WaveIn error 6";
   }
  }

  // Prepare Buffer
  private void PrepareBuffer ()
  {
   int IntTmp = BufSize + WAVEHDR.SizeOfEmptyWAVEHDR();
   uint UIntTmp = Convert.ToUInt32(IntTmp);

   int err = waveInAddBuffer(this.hWaveIn, ref this.StructWaveHeader, UIntTmp);
   if (err != 0) 
   {    
    int retval = mciGetErrorString(err, errorBuffer, 128);
    this.textBoxVFunct.Text = "WaveIn error 4";
   }
  }

  // Start Recording
  private void StartRec ()
  {
   Monitor.Enter(MyLock);
    int err = waveInStart(this.hWaveIn);
    if (err != 0) 
    {    
     int retval = mciGetErrorString(err, errorBuffer, 128);
     this.textBoxVFunct.Text = "WaveIn error 5";
    }
   Monitor.Exit(MyLock);
  }

  // Listens to the message that the audio device sends
  // It enables to retreive the data when a buffer has been filled
  private void waveInProc(IntPtr hWaveIn, uint Msg, int dwInstance, ref WAVEHDR dwParam1, int dwParam2)
  {
   /*
    ATTENTION:
    Don't call any system-function in waveInProc


*/
   int correcting_factor = 0;

   switch(Msg)
   {
    case MM_WIM_DATA:

     if (StillRecording == true) // we are still recording
     {
      // File recording
      this.n++;
      for (int i = 0; i<dwParam1.dwBytesRecorded; i++)
      {
       this.Index = (BufSize*this.n)+i;
       correcting_factor = (int)this.ByteArray[i];
       correcting_factor = (int)(correcting_factor/2);
       this.TempArray[this.Index] = (byte)correcting_factor;
       DFunct.GetDataToDisplay(correcting_factor);
      }        
 
      // Prepare the next stage of recording
      UnPrepareHeader();
      dwParam1.lpData = IntPtr.Zero;
      dwParam1.dwBytesRecorded = 0;
      HeaderDataHandle.Free();

      // We now sent another buffer to capture another chunk of data
      PrepareHeader ();
      PrepareBuffer ();            
     }
     break;

    case MM_WIM_OPEN:
     break;

    case MM_WIM_CLOSE:
     this.HeaderDataHandle.Free();
     break;

    default:
     this.textBoxVFunct.Text = "Unknonw message sent by the MIC";
     break;
   } 
  }

  // Close communication, error ID N°2 and 6
  public int CloseComm ()
  {
   int RetVal = 0;

   // Stop Recording
   this.StillRecording = false;

   // We Stop the recording, error ID N°6
   int err = waveInStop(this.hWaveIn);
   if (err != 0) 
   {    
    int retval = mciGetErrorString(err, errorBuffer, 128);
    this.textBoxVFunct.Text = "WaveIn error 7";
   }
   else
   {
    // We close the communication with the MIC, error ID N°2
    err = waveInClose(this.hWaveIn);
    if (err != 0) 
    {    
     int retval = mciGetErrorString(err, errorBuffer, 128);
     this.textBoxVFunct.Text = "WaveIn error 2";
    }
    else
    {
     RetVal = 1;
     // free the allocated memory
     this.errorBuffer   = null;
     this.dwCallback    = null;
     this.MyLock     = null;
     
     // Now I write the file with the data I have just recorded
     PrepareFile();
    }
   }
   return(RetVal);
  }

  #region ConvertToHexa
  private void ConvertToHexa(int IntToConvert)
  {
   int Current = IntToConvert;
   char[] rank = new char[8];
   int i = 0;
   char temp;
   int b = 0;

   do
   {
    rank[i] = (char)(Current%16);
    Current = (int)(Current/16);
    i++;
   }while(Current>16);
   rank[i] = (char)(Current);
   if (i<7)
   {
    do{
     rank[i] = (char)0;
    }while(i==7);
   }

   // invert the values in rank[]
   for(int a=i;a>=b;a--)
   {
    temp = rank[a];
    rank[a] = rank[b];
    rank[b] = temp;
    b++;
   }

   // The expression is inverted in the File :)
   this.FileSize1 = (char)(rank[6]*16 + rank[7]);
   this.FileSize2 = (char)(rank[4]*16 + rank[5]);
   this.FileSize3 = (char)(rank[2]*16 + rank[3]);
   this.FileSize4 = (char)(rank[0]*16 + rank[1]);
  }
  #endregion
  
  #region Write the recorded WAVE
  private void PrepareFile()
  {
   

   // File handling
   Directory.CreateDirectory(this.WorkingDir);
   string FileToCreate  = this.WorkingDir + "/AudioFile.wav"; 
   StreamWriter AudioFile = new StreamWriter(FileToCreate,false,ASCIIEncoding.Default,1024); 
   this.WorkingDir   = null;

   char FormatLen1 = (char)0x10;  // size of the header chunk
   char FormatLen2 = (char)0x0;
   char FormatLen3 = (char)0x0;
   char FormatLen4 = (char)0x0;
   char AudioFormat1 = (char)0x1;  // PCM
   char AudioFormat2 = (char)0x0;
   char channel1 = (char)0x1;   // channel
   char channel2 = (char)0x0;
   char SampleRate1 = (char)0x11;   // sample rate
   char SampleRate2 = (char)0x2B;
   char SampleRate3 = (char)0x0;   
   char SampleRate4 = (char)0x0;
   char BlockAlign1 = (char)0x1;
   char BlockAlign2 = (char)0x0;
   char BitsPerSample1 = (char)0x8;
   char BitsPerSample2 = (char)0x0;

   // copy of the header
   //RIFF CHUNK
   AudioFile.Write("RIFF");
   ConvertToHexa(this.Index+44);
   AudioFile.Write(this.FileSize1);  // I will replace these 4 bits by the total size of the Wave File
   AudioFile.Write(this.FileSize2);
   AudioFile.Write(this.FileSize3);
   AudioFile.Write(this.FileSize4);

   AudioFile.Write("WAVE");
   // Format Chunk
   AudioFile.Write("fmt ");
   AudioFile.Write(FormatLen1);
   AudioFile.Write(FormatLen2);
   AudioFile.Write(FormatLen3);
   AudioFile.Write(FormatLen4);
   AudioFile.Write(AudioFormat1);
   AudioFile.Write(AudioFormat2);
   AudioFile.Write(channel1);
   AudioFile.Write(channel2);
   AudioFile.Write(SampleRate1);
   AudioFile.Write(SampleRate2);
   AudioFile.Write(SampleRate3);
   AudioFile.Write(SampleRate4);
   AudioFile.Write(SampleRate1); 
   AudioFile.Write(SampleRate2);
   AudioFile.Write(SampleRate3);
   AudioFile.Write(SampleRate4);
   AudioFile.Write(BlockAlign1);
   AudioFile.Write(BlockAlign2);
   AudioFile.Write(BitsPerSample1);
   AudioFile.Write(BitsPerSample2);
   // data chunk
   AudioFile.Write("data");
   ConvertToHexa(this.Index);
   AudioFile.Write(this.FileSize1);
   AudioFile.Write(this.FileSize2);
   AudioFile.Write(this.FileSize3);
   AudioFile.Write(this.FileSize4);

   // data
   for(int i=0; i<this.Index;i++)
   {
    AudioFile.Write((char)this.TempArray[i]);
   }
     
   AudioFile.Close();
   
  }
  #endregion

  // destructor
  ~VoiceFunction()
  {   

  }
 } // End of class VoiceFunction 
} // End of namespace

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Softphone; // To draw the graph

namespace AudioRepresentationClass
{
 public class AudioRepresentation
 {
  // Variables
  private int AudioSigSize;
  private short[] GetAudioSig;
  private short[] DisplayAudioSig;
  private int n;
  private Graphics dc;
  private Form1 MainHandler;
  
  
  public AudioRepresentation(int BufSize, Graphics graph, Form1 Handler)
  {
   this.AudioSigSize = BufSize;         // Audio chunks
   this.GetAudioSig = new short[this.AudioSigSize];
   this.DisplayAudioSig = new short[this.AudioSigSize];
   this.n = 0;
   this.dc = graph;
   this.MainHandler = Handler;
  }

  public void GetDataToDisplay(int DataValue)
  {
   this.GetAudioSig[this.n] = (short)(DataValue-60);
   this.n++;
   if(this.n == this.AudioSigSize) // A buffer has been filled !
   {
    this.GetAudioSig.CopyTo(DisplayAudioSig,0);
    DefineGraph();
    this.n = 0;
   }
  }

  
  private void DefineGraph()
  {
   // Init the graph
   int X  = 55;
   int Y  = 10;
   int var1 = 0;  // Y position
   int var2 = 0;  // X2
   int j  = 0;
   
   this.MainHandler.Refresh();
   while(j < this.AudioSigSize)
   {
    var1 = Y + (int)(j/2);
    var2 = X - (int)((this.DisplayAudioSig[j]*50)/128);  // *50/128 is to adapt to the 50 px space I have
    // Draw a line > TOOL.color, Y1 = Position from the left side, X1 = position from the top side, Y2, X2
    this.dc.DrawLine(Pens.Red,var1,X,var1,var2);
    j++;
   }
  } // end of DefineGraph()

 } // end of class AudioRepresentation
} //end of namespace AudioRepresentationClass

Conclusion :


C'est là base d'une application de VoIP. Là c'est l'acquisition et le début du traitement de la voix. On trouve beaucoup de sources en C++ mais pas en C#. Je me suis dit que ça pourrait être utile à certain.
Je continue à travailler dessus, donc n'hésitez pas à me contacter.
Voici mon site Web, je mettrai à jour mon projet de temps en temps: http://www.ece.fr/~gbonnet

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.