Sunteți pe pagina 1din 4

A full-duplex audio player in C# using the

waveIn/waveOut APIs
By Ianier Munoz, 31 Aug 2003


4.74 (98 votes)



Rate this: vote 1vote 2vote 3vote 4vote 5

inShare0
Download source files - 15.1 Kb

Introduction
As I already mentioned in my article A low-level audio player in C#, there are no built-in
classes in the .NET framework for dealing with sound. This holds true not only for audio
playback, but also for audio capture.
It should be noted, though, that the Managed DirectX 9 SDK does include classes for high-
level and low-level audio manipulation. However, sometimes you dont want your
application to depend on the full DX 9 runtime, just to do basic sound playback and capture,
and there are also some areas where Managed DirectSound doesnt help at all (for example,
multi-channel sound playback and capture).
Nevertheless, I strongly recommend you to use Managed DirectSound for sound playback
and capture unless you have a good reason for not doing so.
This article describes a sample application that uses the waveIn and waveOut APIs in C#
through P/Invoke to capture an audio signal from the sound cards input, and play it back
(almost) at the same time.
Using the code
The sample code reuses the WaveOutPlayer class from my article A low-level audio player
in C#. The new classes in this sample are WaveInRecorder and FifoStream.
The FifoStream class extends System.IO.Stream to implement a FIFO (first-in first-out) of
bytes. The overridden Write method adds data to the FIFOs tail, and the Read method peeks
and removes data from the FIFOs head. The Length property returns the amount of buffered
data at any time. Calling Flush will clear all pending data.
The WaveInRecorder class is analogous to the WaveOutPlayer class. In fact, if you look at
the source files, youll notice that the implementations of these classes are very similar. As
with WaveOutPlayer, the interface of this class has been reduced to the strict minimum.
Creating an instance of WaveInRecorder will cause the system to start recording
immediately. Heres the code that creates the WaveOutPlayer and WaveInRecorder
instances.
Collapse | Copy Code
private void Start()
{
Stop();
try
{
WaveLib.WaveFormat fmt = new WaveLib.WaveFormat(44100, 16, 2);
m_Player = new WaveLib.WaveOutPlayer(-1, fmt, 16384, 3,
new WaveLib.BufferFillEventHandler(Filler));
m_Recorder = new WaveLib.WaveInRecorder(-1, fmt, 16384, 3,
new WaveLib.BufferDoneEventHandler(DataArrived));
}
catch
{
Stop();
throw;
}
}
The WaveInRecorder constructor takes five parameters. Except for the last parameter, their
meaning is the same as in WaveOutPlayer.
The first parameter is the ID of the wave input device that you want to use. The value -1
represents the default system device, but if your system has more than one sound card, then
you can pass any number from 0 to the number of installed sound cards minus one, to select a
particular device.
The second parameter is the format of the audio samples.
The third and forth parameters are the size of the internal wave buffers and the number of
buffers to allocate. You should set these to reasonable values. Smaller buffers will give you
less latency, but the captured audio may have gaps on it if your computer is not fast enough.
The fifth and last parameter is a delegate that will be called periodically as internal audio
buffers are full of captured data. In the sample application we just write the captured data to
the FIFO, like this:
Collapse | Copy Code
private void DataArrived(IntPtr data, int size)
{
if (m_RecBuffer == null || m_RecBuffer.Length < size)
m_RecBuffer = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(data, m_RecBuffer, 0,
size);
m_Fifo.Write(m_RecBuffer, 0, m_RecBuffer.Length);
}
Similarly, the Filler method is called every time the player needs more data. Our
implementation just reads the data from the FIFO, as shown below:
Collapse | Copy Code
private void Filler(IntPtr data, int size)
{
if (m_PlayBuffer == null || m_PlayBuffer.Length < size)
m_PlayBuffer = new byte[size];
if (m_Fifo.Length >= size)
m_Fifo.Read(m_PlayBuffer, 0, size);
else
for (int i = 0; i < m_PlayBuffer.Length; i++)
m_PlayBuffer[i] = 0;
System.Runtime.InteropServices.Marshal.Copy(m_PlayBuffer,
0, data, size);
}
Note that we declared the temporary buffers m_RecBuffer and m_PlayBuffer as member
fields in order to improve performance by saving some garbage collections.
To stop streaming, just call Dispose on the player and capture objects. We also need to flush
the FIFO so that the next time Start is called there is no residual data to play.
Collapse | Copy Code
private void Stop()
{
if (m_Player != null)
try
{
m_Player.Dispose();
}
finally
{
m_Player = null;
}
if (m_Recorder != null)
try
{
m_Recorder.Dispose();
}
finally
{
m_Recorder = null;
}
m_Fifo.Flush(); // clear all pending data
}
Conclusion
This sample demonstrates how to combine the waveIn and waveOut APIs in C#. As an
exercise, you may want to combine this code with the audio effect framework in the article
Programming Audio Effects in C#, to apply effects to a live audio input in real-time, although
latency may be an issue for certain applications.
License
This article has no explicit license attached to it but may contain usage terms in the article
text or the download files themselves. If in doubt please contact the author via the discussion
board below.
A list of licenses authors might use can be found here
About the Author

Ianier Munoz
Web Developer
Luxembourg
Ianier Munoz lives in France and works as a senior consultant and analyst for an international
consulting firm. His specialty is in multimedia applications, and he has authored some
popular software, such as American DJ's Pro-Mix, Chronotron and Adapt-X.

S-ar putea să vă placă și