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
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.