Recording the Soundcard Output to WAV in NAudio
Suppose you want to not just play back some audio, but record what you are playing to a WAV file. This can be achieved in NAudio by creating an IWaveProvider
whose read method reads from another IWaveProvider
but also writes to disk as it goes. This is very easy to implement, and the WaveRecorder
class I present here will be added to NAudio shortly. Our WaveRecorder
also needs to be disposable, as we will want to close the WAV file when we are finished.
/// <summary>
/// Utility class to intercept audio from an IWaveProvider and
/// save it to disk
/// </summary>
public class WaveRecorder : IWaveProvider, IDisposable
{
private WaveFileWriter writer;
private IWaveProvider source;
/// <summary>
/// Constructs a new WaveRecorder
/// </summary>
/// <param name="destination">The location to write the WAV file to</param>
/// <param name="source">The source Wave Provider</param>
public WaveRecorder(IWaveProvider source, string destination)
{
this.source = source;
this.writer = new WaveFileWriter(destination, source.WaveFormat);
}
/// <summary>
/// Read simply returns what the source returns, but writes to disk along the way
/// </summary>
public int Read(byte[] buffer, int offset, int count)
{
int bytesRead = source.Read(buffer, offset, count);
writer.WriteData(buffer, offset, bytesRead);
return bytesRead;
}
/// <summary>
/// The WaveFormat
/// </summary>
public WaveFormat WaveFormat
{
get { return source.WaveFormat; }
}
/// <summary>
/// Closes the WAV file
/// </summary>
public void Dispose()
{
if (writer != null)
{
writer.Dispose();
writer = null;
}
}
}
Now we have our WaveRecorder
, we can insert it anywhere in the chain we like. The most obvious place is right at the end of the chain. So we wrap the WaveStream
or IWaveProvider
we would normally pass to the Init
method of our IWavePlayer
with the WaveRecorder
class. To demonstrate, I will extend the sine wave generating code I created recently, to save the sine wave you are playing to disk. Only three extra lines of code are required:
IWavePlayer waveOut;
WaveRecorder recorder;
private void button1_Click(object sender, EventArgs e)
{
StartStopSineWave();
}
void StartStopSineWave()
{
if (waveOut == null)
{
var sineWaveProvider = new SineWaveProvider16();
sineWaveProvider.SetWaveFormat(16000, 1); // 16kHz mono
sineWaveProvider.Frequency = 500;
sineWaveProvider.Amplitude = 0.1f;
recorder = new WaveRecorder(sineWaveProvider, @"C:\Users\Mark\Documents\sine.wav");
waveOut = new WaveOut();
waveOut.Init(recorder);
waveOut.Play();
}
else
{
waveOut.Stop();
waveOut.Dispose();
waveOut = null;
recorder.Dispose();
recorder = null;
}
}
Comments
I have a problem with this.
n2o"LineOutRecorder.WaveRecorder' does not implement interface member 'NAudio.Wave.IWaveProvider.Read(NAudio.Wave.IWaveBuffer)"
Source:
http://www.paste.lt/paste/879522bf8c12bb7e6c6a7ab7eaad9d22
Sollution explorer screenshot:
http://i647.photobucket.com/albums/uu198/n2oo/sollution.png
hi n20,
Mark Hhave you got the very latest NAudio source code out from Codeplex?
Version that I tried I downloaded today from Codeplex.
n2oYou need to go to the source code tab and download the latest and build it. NAudio 1.3 has not yet been released yet.
Mark HHere's the code for the latest IWaveProvider:
https://github.com/naudio/NAudio/blob/e359ca0566e9f9b14fee1ba6e0ec17e4482c7844/NAudio/Wave/WaveOutputs/IWaveProvider.cs
Hi Mark,
JelleI would like to record only the audio from one single application that plays music. is that possible with the NAUDIO lib ?
thanks!
Jelle
Thank You for replay Mark,
n2oHere I found nice recording application example:
http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html
Is it possible to programmicaly select the recording device? (choose between mic, line-in and wave etc.)
BTW I am using XP, seems that newest NAudio Demo has that function but as exeption said "This functionality is only supported on Windows Vista or newer."
n2oOne more question about recorder buffer, it contais bytes, so each audio sample is 8bit or divided into several array elements?
hi n2o, you can't use WASAPI capture on XP. Use WaveIn instead. See my voice recorder article on the Coding4Fun website to learn more about recording audio in NAudio.
Mark Hhttp://blogs.msdn.com/coding4fun/archive/2009/10/08/9905168.aspx
Hi Mark! I'm on Windows 7 and am struggling to get NAudio to record the stereo mix - is this possible, maybe using WASAPI as opposed to WaveIn? I'm not sure how to do this with NAudio if it is.
JCI've used the tutorial example 5 from Codeplex and clicked the "Record all output from my soundcard" button, but it only records what is coming in from the microphone. Any pointers would be greatly appreciated!! Many thanks.
hi JC, it probably depends on your sound-card drivers whether any of the WaveIn options can do this programatically. You might need to get the windows mixer up to select your recording source to make it work.
Mark HHI,
JIbinNow i am able to capture sound and at the same time i send it over network like this
private void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
byte[] buffer = e.Buffer;
byte[] dataToWrite = ALawEncoder.ALawEncode(buffer);
if (socket_Audio != null)
socket_Audio.SendTo(dataToWrite, new IPEndPoint(IPAddress.Parse(RemoteIpv6), SoundPort));
}
Now the problem that i am facing is how to get back this audio at receiving end and and send it to the speaker of the system
I am just starting a new thread for rxving data from network
myAudioThread = new Thread(new ThreadStart(AudioListener));
myAudioThread.Start();
And in the AudioListener() i use the following code
#region for Audio socket Its rxving audio always
try
{
socket_Audio = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
socket_Audio.Bind(new IPEndPoint(IPAddress.IPv6Any, SoundPort));
IPEndPoint remoteEP = new IPEndPoint(IPAddress.IPv6Any, SoundPort);
//Receive data.
byte[] byteData;
while (true)
{
byteData = new byte[2048];
//Receive data.
socket_Audio.Receive(byteData);
//G711 compresses the data by 50%, so we allocate a buffer of double
//the size to store the decompressed data.
byte[] byteDecodedData = new byte[byteData.Length * 2];
//Decompress data using the proper vocoder.
ALawDecoder.ALawDecode(byteData, out byteDecodedData);
}
Now the decoded audio is in the byte array byteDecodedData.How i send it to speaker of that system.
I found only waveOut.Play() and
public override int Read(byte[] buffer, int offset, int count)
{
//code to process data
}
in the examples
Pls guide me to the next step....Pls let me know if i am in wrong path.
Thanks
JIbin
[email protected]
@Jlbin - have a look at the BufferedWaveProvider in the latest code. This can be used to buffer up your data read from the network and can be used to feed the soundcard
Mark HHi Mark,
SeingaltGreat lib by the way.
I just want to ask if you have any ideas about recording conversations?
Is it possible to capture it using WASAPI or, in my case since I'm using win XP, WaveIn? Or do you know any alternatives to that?
[email protected]
@Seingalt what do you mean by conversations? conversations on what program? if you mean skype, then you could look into using the Skype4Com object
Mark HHi Mark,
SeingaltI'd take a look at skype4com objects.
Basically, I have a web application that should allow the user to call (for example a Call Center Rep.) from that page and record that conversation.
I have done some research on skype4com already but somehow I'm still having a hard time on how I could use it on my application.
If you know or have any articles about it just post it here. Like "Using skype4com objects in your web application for biginners"
Thanks!
@Seingalt - check my Coding4Fun article and project - SkypeFx (https://github.com/markheath/skypevoicechanger)
Mark HThanks for the great example. I already noticed it at NAudio but since it was not actually used in tehir code, the sample was very helpful.
AnonymousOne question: I would like to use the WaveRecorder combined with the ASIO4All. The NADIO Asio only has AsioOut.cs. Is there a sample of how the Wave Recorder is used for ASIO?
Thanks
HM
sadly NAudio ASIO support is very basic and doesn't work with all soundcards at the moment
Mark HThanks Mark. I'm still hopefull, and struggeling with adding the ASIO-in interface by myself. I'm trying to figure out where to fit the WaveRecorder class.
AnonymousSince WaveRecorder is actually an IWaveProvider, I plan to add AsioIn.cs which has "recordStream" of class WaveRecord (similar to AsioOut). I'm jyust confused wehther or not I need to add IWaveRecorder, similar to IWavePlayer.
Thanks you very much!
HM
@HM, an AsioIn class should implement IWaveIn
Mark HCan I use that source code on windows mobile? Or do you have a code that record a wave file format on windows mobile? (records in a wave file format ..)
AnonymousI want to change the frequency of this sinus wave without interrupting it and quite quickly . You said in your previous posts that I need to do it smoothly , how can I do that ? Sorry but I'm a beginner
Anonymous"SineWaveProvider16" is not working. May be because of this tutorial is dated. Please update it.
Durdanto Rafi