Record live audio from mic input and save simultanously - c#

I was trying to develop a Voice recorder in C#. I have tried many ways, like NAudio, DirectX, Microsoft.Xna.Framework.Audio, etc.
Everything gives the same result. After we stop the recording, the output file mp3/wav get saved.
The mp3/wav file get created at the beginning itself (without and content - 0 bytes)
I am trying to create an application which can save audio live/simultaneously.
private void StartRecording() {
this.WaveSource = new WaveInEvent { WaveFormat = new WaveFormat(44100, 1) };
this.WaveSource.DataAvailable += this.WaveSourceDataAvailable;
this.WaveSource.RecordingStopped += this.WaveSourceRecordingStopped;
this.WaveFile = new WaveFileWriter(#"C:\Sample.wav", this.WaveSource.WaveFormat);
this.WaveSource.StartRecording();
}
private void StopRecording() {
this.WaveSource.StopRecording();
}
void WaveSourceDataAvailable(object sender, WaveInEventArgs e) {
if (this.WaveFile != null) {
this.WaveFile.Write(e.Buffer, 0, e.BytesRecorded);
this.WaveFile.Flush();
}
}
void WaveSourceRecordingStopped(object sender, StoppedEventArgs e) {
if (this.WaveSource != null) {
this.WaveSource.Dispose();
this.WaveSource = null;
}
if (this.WaveFile != null) {
this.WaveFile.Dispose();
this.WaveFile = null;
}
}

I have solved the problem with NAudio library itself.
Few modification to the existing code.
public class Recorder {
WaveIn sourceStream;
WaveFileWriter waveWriter;
readonly String FilePath;
readonly String FileName;
readonly int InputDeviceIndex;
public Recorder(int inputDeviceIndex, String filePath, String fileName) {
InitializeComponent();
this.InputDeviceIndex = inputDeviceIndex;
this.FileName = fileName;
this.FilePath = filePath;
}
public void StartRecording(object sender, EventArgs e) {
sourceStream = new WaveIn {
DeviceNumber = this.InputDeviceIndex,
WaveFormat =
new WaveFormat(44100, WaveIn.GetCapabilities(this.InputDeviceIndex).Channels)
};
sourceStream.DataAvailable += this.SourceStreamDataAvailable;
if (!Directory.Exists(FilePath)) {
Directory.CreateDirectory(FilePath);
}
waveWriter = new WaveFileWriter(FilePath + FileName, sourceStream.WaveFormat);
sourceStream.StartRecording();
}
public void SourceStreamDataAvailable(object sender, WaveInEventArgs e) {
if (waveWriter == null) return;
waveWriter.Write(e.Buffer, 0, e.BytesRecorded);
waveWriter.Flush();
}
private void RecordEnd(object sender, EventArgs e) {
if (sourceStream != null) {
sourceStream.StopRecording();
sourceStream.Dispose();
sourceStream = null;
}
if (this.waveWriter == null) {
return;
}
this.waveWriter.Dispose();
this.waveWriter = null;
recordEndButton.Enabled = false;
Application.Exit();
Environment.Exit(0);
}
}

You can do this with DirectShow.
Take a look at Microsoft's documentation and the project's code samples to learn the best way to configure it according to your needs.

Wanna try to BASS NET?
There is a complete code for you task How to record sound and encode it to mp3? (C#!)

Related

Recording audio with NAudio cuts me off

I am recording an audio to send it to send it to Google speech to text but when I make the audio with naudio it records me only 5 seconds and from there it stops recording. I copy the code in C #, this is my first time using this API, but I don't know why it cuts me, if it should stop recording when I press the save button, the application is a simple form with 2 buttons, one for recording and the other for stop.
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private BufferedWaveProvider bwp;
WaveIn waveIn;
WaveOut waveOut;
WaveFileWriter writer;
WaveFileReader reader;
string output = "audio.raw";
public Form1()
{
InitializeComponent();
waveOut = new WaveOut();
waveIn = new WaveIn();
waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(16000, 1);
bwp = new BufferedWaveProvider(waveIn.WaveFormat);
bwp.DiscardOnBufferOverflow = true;
btnRecordVoice.Enabled = true;
btnSave.Enabled = false;
//btnSpeechInfo.Enabled = false;
}
private void btnRecordVoice_Click(object sender, EventArgs e)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No se encuentra un microfono!");
return;
}
waveIn.StartRecording();
btnRecordVoice.Enabled = false;
btnSave.Enabled = true;
}
private void btnSave_Click(object sender, EventArgs e)
{
waveIn.StopRecording();
if (File.Exists("audio.raw"))
File.Delete("audio.raw");
writer = new WaveFileWriter(output, waveIn.WaveFormat);
btnRecordVoice.Enabled = false;
btnSave.Enabled = false;
byte[] buffer = new byte[bwp.BufferLength];
int offset = 0;
int count = bwp.BufferLength;
var read = bwp.Read(buffer, offset, count);
if (count > 0)
{
writer.Write(buffer, offset, read);
}
waveIn.Dispose();
waveIn = null;
writer.Close();
writer = null;
reader = new WaveFileReader("audio.raw"); // (new MemoryStream(bytes));
waveOut.Init(reader);
waveOut.PlaybackStopped += new EventHandler<StoppedEventArgs>(waveOut_PlaybackStopped);
waveOut.Play();
}
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
bwp.AddSamples(e.Buffer, 0, e.BytesRecorded);
}
private void waveOut_PlaybackStopped(object sender, StoppedEventArgs e)
{
waveOut.Stop();
reader.Close();
reader = null;
}
}
}
You get a 5-second audio clip not only because you do a one-time BufferLength read from the BufferedWaveProvider...
int count = bwp.BufferLength;
var read = bwp.Read(buffer, offset, count);
...and 5 seconds is the default value of that property, but because BufferedWaveProvider uses a circular buffer, so BufferLength is all the data it has available.
What worked for me was to skip the BufferedWaveProvider and write new data to the WaveFileWriter as soon as it becomes available...
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
writer.Write(e.Buffer, 0, e.BytesRecorded);
}
To support that change, the button event handlers get changed to the following...
private void btnRecordVoice_Click(object sender, EventArgs e)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No se encuentra un microfono!");
return;
}
writer = new WaveFileWriter(output, waveIn.WaveFormat);
waveIn.StartRecording();
btnRecordVoice.Enabled = false;
btnSave.Enabled = true;
}
private void btnSave_Click(object sender, EventArgs e)
{
waveIn.StopRecording();
writer.Close();
writer = null;
btnRecordVoice.Enabled = false;
btnSave.Enabled = false;
reader = new WaveFileReader("audio.raw"); // (new MemoryStream(bytes));
waveOut.Init(reader);
waveOut.PlaybackStopped += new EventHandler<StoppedEventArgs>(waveOut_PlaybackStopped);
waveOut.Play();
}
This appears to be the same approach used in Recording a WAV file in a WinForms app with WaveIn.

How can i extract frames from video using directshowlib-2005?

With this code i can play video files from my hard disk and show the video in pictureBox1. But i wonder how can i save all the frames of the video to images files on the hard disk ? While playing the video or without playing i need somehow to extract the frames and save them.
This is my used code so far:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
IGraphBuilder m_graphBuilder = null;
IMediaControl m_mediaCtrl = null;
IMediaEventEx m_mediaEvt = null;
IMediaPosition m_mediaPos = null;
IMediaSeeking m_mediaSeeking = null;
public Form1()
{
InitializeComponent();
}
void InitInterfaces()
{
try
{
m_graphBuilder = (IGraphBuilder)new FilterGraph();
m_mediaCtrl = (IMediaControl)m_graphBuilder;
m_mediaEvt = (IMediaEventEx)m_graphBuilder;
m_mediaPos = (IMediaPosition)m_graphBuilder;
m_mediaSeeking = (IMediaSeeking)m_graphBuilder;
}
catch (Exception)
{
MessageBox.Show("Couldn't start directshow graph");
}
}
void CloseInterfaces()
{
if (m_mediaCtrl != null)
{
m_mediaCtrl.StopWhenReady();
}
m_mediaCtrl = null;
m_mediaEvt = null;
m_mediaPos = null;
m_mediaSeeking = null;
if (m_graphBuilder != null)
Marshal.ReleaseComObject(this.m_graphBuilder);
m_graphBuilder = null;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void SetuupVideoRenderer()
{
IBaseFilter vmrFilter = null;
vmrFilter = (IBaseFilter)new VideoMixingRenderer();
m_graphBuilder.AddFilter(vmrFilter, "Video Renderer");
IVMRFilterConfig FilterConfig = (IVMRFilterConfig)vmrFilter;
FilterConfig.SetRenderingMode(VMRMode.Windowless);
IVMRWindowlessControl windowlessCtrl = (IVMRWindowlessControl)vmrFilter;
windowlessCtrl.SetVideoClippingWindow(this.pictureBox1.Handle);
windowlessCtrl.SetVideoPosition(null, DsRect.FromRectangle(pictureBox1.ClientRectangle));
windowlessCtrl.SetAspectRatioMode(VMRAspectRatioMode.LetterBox);
}
private void buttonLoad_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "All Files (*.*)|*.*|mp4 (*.mp4)|*.mp4|mov (*.mov)|*.mov||";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
CloseInterfaces();
InitInterfaces();
SetuupVideoRenderer();
m_graphBuilder.RenderFile(openFileDialog1.FileName, null);
textBoxDur.Text = ( getDuration() * 0.0000001).ToString();
m_mediaCtrl.Run();
timer1.Enabled = true;
}
}
private void GetPosition(out long CurrentPos,out long StopPos)
{
m_mediaSeeking.GetPositions(out CurrentPos, out StopPos);
}
private long getDuration()
{
long duration;
m_mediaSeeking.GetDuration(out duration);
return duration;
}
private void SetPos(double fPos)
{
DsLong startPosition = (DsLong)(10000000 * fPos);
m_mediaSeeking.SetPositions(startPosition, AMSeekingSeekingFlags.AbsolutePositioning, null, AMSeekingSeekingFlags.NoPositioning);
}
private void buttonPause_Click(object sender, EventArgs e)
{
m_mediaCtrl.Pause();
}
private void buttonPlay_Click(object sender, EventArgs e)
{
m_mediaCtrl.Run();
}
private void OnVideoCompleted()
{
MessageBox.Show("Video Playback Completed");
}
private void timer1_Tick(object sender, EventArgs e)
{
long iCurPos, iStopPos;
GetPosition(out iCurPos, out iStopPos);
if (iCurPos >= iStopPos)
{
timer1.Enabled = false;
OnVideoCompleted();
return;
}
textBoxCurPos.Text = (iCurPos * 0.0000001 ).ToString();
}
private void buttonGo_Click(object sender, EventArgs e)
{
SetPos(Convert.ToDouble(textBoxNewPos.Text));
timer1.Enabled = true;
}
}
}
I think this is excatly what you are looking for:
extract frames of a video
Have a look as well at this SO question and the links provided on this webpage.
The easiest way to do it is indeed using an FFMPEG, since its alredy includes some of the most common codecs (if you dont mind extra 30+Mb added to your app). As for wrappers, i used AForge wrapper in the past and really liked it, because of how simple it is to work with. Here is an example from its docs:
// create instance of video reader
VideoFileReader reader = new VideoFileReader();
// open video file
reader.Open( "test.avi");
// read 100 video frames out of it
for ( int i = 0; i < 100; i++)
{
Bitmap videoFrame = reader.ReadVideoFrame();
videoFrame.Save(i + ".bmp")
// dispose the frame when it is no longer required
videoFrame.Dispose( );
}
reader.Close();

C# textfile only contains last string written to file with StreamWriter

I'm writing a Chat Program in C# (Windows Forms Application), the solution contains to projects which both consist of one form ( see picture ). While sending messages to each other works, I'm trying to record the conversation session in a .txt file named dateTime.txt using StreamWriter. Creating the file if it does not exist yet works, but whenever I open the text file, it only contains the last string that was written to it instead of containing the whole "conversation".
Does anybody know how to fix this?
This is the code of one of the forms, but since the forms do exactly the same, the code is the same too so i'm only posting the code of one Form. Would be great if somebody knows what I have to change so the whole conversation is recorded in the text file.
namespace Assignment3Client
{
public partial class Chat : Form
{
NamedPipeClientStream clientPipe = new NamedPipeClientStream("pipe2");
NamedPipeServerStream serverPipe = new NamedPipeServerStream("pipe1");
string msg = String.Empty;
string msgStr;
string name;
byte[] ClientByte;
public Chat()
{
InitializeComponent();
}
private void btnStartChat_Click(object sender, EventArgs e)
{
this.Text = "Waiting for a connection....";
if (txtBoxName.Text.Length == 0)
{
MessageBox.Show("please enter a name first.");
}
else
{
name = txtBoxName.Text;
clientPipe.Connect();
serverPipe.WaitForConnection();
if (serverPipe.IsConnected)
{
this.Text = "You are connected, " + name + "!";
btnStartChat.Enabled = false;
btnSend.Enabled = true;
txtBoxMsg.Enabled = true;
txtBoxMsg.Focus();
receiveWorker.RunWorkerAsync();
}
}
}
private void btnSend_Click(object sender, EventArgs e)
{
msg = "[" + name + ": " + DateTime.Now + "] " + txtBoxMsg.Text;
txtBoxChat.AppendText(msg + "\n");
FileWriter(msg);
sendWorker.RunWorkerAsync(msg); //start backgroundworker and parse msg string to the dowork method
txtBoxMsg.Clear();
txtBoxMsg.Focus();
}
private void sendWorker_DoWork(object sender, DoWorkEventArgs e)
{
Byte[] msgByte = System.Text.Encoding.GetEncoding("windows-1256").GetBytes(msg);
serverPipe.Write(msgByte, 0, msg.Length);
}
private void receiveWorker_DoWork(object sender, DoWorkEventArgs e)
{
ClientByte = new Byte[1000];
int i;
for (i = 0; i < ClientByte.Length; i++)
{
ClientByte[i] = 0x20;
}
clientPipe.Read(ClientByte, 0, ClientByte.Length);
msgStr = System.Text.Encoding.GetEncoding("windows-1256").GetString(ClientByte);
receiveWorker.ReportProgress(i, msgStr);
}
private void receiveWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if ((string)e.UserState == String.Empty)
{ MessageBox.Show("no message"); }
else
{
string message = (string)e.UserState;
txtBoxChat.AppendText(message);
FileWriter(message);
txtBoxChat.BackColor = System.Drawing.Color.DarkBlue;
txtBoxChat.ForeColor = System.Drawing.Color.White;
}
}
private void receiveWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (clientPipe.IsConnected)
{
receiveWorker.RunWorkerAsync();
}
else
{
txtBoxMsg.Enabled = false;
btnSend.Enabled = false;
MessageBox.Show("Connection lost");
}
}
private void Chat_Activated(object sender, EventArgs e)
{
txtBoxChat.BackColor = new System.Drawing.Color();
txtBoxChat.ForeColor = new System.Drawing.Color();
}
private void exitMenuStrip_Click(object sender, EventArgs e)
{
this.Close();
}
private void conMenuSrip_Click(object sender, EventArgs e)
{
}
private void errMenuStrip_Click(object sender, EventArgs e)
{
}
public void FileWriter(string message)
{
string path = #"C:\Users\selin\Documents\TAFE\Term 3\dateTime.txt";
FileStream conLog;
if (!File.Exists(path))
{
conLog = new FileStream(path, FileMode.Create);
}
else
{
conLog = new FileStream(path, FileMode.Open);
}
StreamWriter writer = new StreamWriter(conLog);
writer.WriteLine(message);
writer.AutoFlush = true;
writer.Close();
MessageBox.Show("written to file" + message);
}
}
}
in FileWriter(string message) change
conLog = new FileStream(path, FileMode.Open);
to
conLog = new FileStream(path, FileMode.Append);

automatic speech detection and recording

I am new to coding in c#. I am building an application to detect the speech and automatically record it. All the information available is based on windows speech recognition engine but my requirement is, the program should detect the speech and record it.
after recording that file is used for processing using hidden markov model(HMM).
namespace Application
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button_1_Click(object sender, EventArgs e)
{
List<NAudio.Wave.WaveInCapabilities> sources = new List<NAudio.Wave.WaveInCapabilities>();
for (int i = 0; i < NAudio.Wave.WaveIn.DeviceCount; i++)
{
sources.Add(NAudio.Wave.WaveIn.GetCapabilities(i));
}
sourceList.Items.Clear();
foreach (var source in sources)
{
ListViewItem item = new ListViewItem(source.ProductName);
item.SubItems.Add(new ListViewItem.ListViewSubItem(item, source.Channels.ToString()));
sourceList.Items.Add(item);
}
}
NAudio.Wave.WaveIn sourceStream = null;
NAudio.Wave.DirectSoundOut waveOut = null;
NAudio.Wave.WaveFileWriter waveWriter = null;
private void button_2_click(object sender, MouseEventArgs e)
{
if (sourceList.SelectedItems.Count == 0) return;
int deviceNumber = sourceList.SelectedItems[0].Index;
sourceStream = new NAudio.Wave.WaveIn();
sourceStream.DeviceNumber = deviceNumber;
sourceStream.WaveFormat = new NAudio.Wave.WaveFormat(16000, NAudio.Wave.WaveIn.GetCapabilities(deviceNumber).Channels);
sourceStream.DataAvailable += new EventHandler<NAudio.Wave.WaveInEventArgs>(sourceStream_DataAvailable);
waveWriter = new NAudio.Wave.WaveFileWriter("test.wav", sourceStream.WaveFormat);
sourceStream.StartRecording();
}
private void sourceStream_DataAvailable(object sender, NAudio.Wave.WaveInEventArgs e)
{
if (waveWriter == null) return;
waveWriter.WriteData(e.Buffer, 0, e.BytesRecorded);
waveWriter.Flush();
}
private void button_3_click(object sender, EventArgs e)
{
if (waveOut != null)
{
waveOut.Stop();
waveOut.Dispose();
waveOut = null;
}
if (sourceStream != null)
{
sourceStream.StopRecording();
sourceStream.Dispose();
sourceStream = null;
}
if (waveWriter != null)
{
waveWriter.Dispose();
waveWriter = null;
}
speechprocessing.Model_test.speech();
}
private void button4_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
this suits my requirement but it has two button to initiate and terminate the recording process which is not under my requirement..
the program should be running and when the speech is detected it must be recorded and further processed. the words which it should detect is also not too much only a few set of words for which we have made the models using hidden markov model..
so please help me out from this problem.

Hearing the Incoming audio from mic

i just want to hear what i say to microphone using NAudio and this is my code so far but the problem is i can't hear anything. any help would be appreciated.
public partial class frmMain : Form
{
private WaveIn waveIn; // Gets an audio from microphone
private WaveOut waveOut; // Sends audio to speaker
private BufferedWaveProvider waveProvider; // Gets an audio from stream
public frmMain()
{
InitializeComponent();
}
private void frmMain_Load(object sender, EventArgs e)
{
waveOut = new WaveOut();
waveIn = new WaveIn();
waveProvider = new BufferedWaveProvider(waveIn.WaveFormat);
waveOut.Init(waveProvider);
waveIn.DataAvailable += waveIn_DataAvailable;
waveOut.Play();
}
private void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
waveProvider.Read(e.Buffer, 0, e.BytesRecorded);
}
private void btnStop_Click(object sender, EventArgs e)
{
waveIn.StopRecording();
waveIn.Dispose();
}
private void btnStart_Click(object sender, EventArgs e)
{
waveIn.StartRecording();
}
}
i will use this scenario in network programming on which i send the data from microphone to the socket then on the client side the BufferedWaveProvider will read the data then send it to the speaker. Please put also some comment if what is the better way to do it.
TIA
Sample code as promised. Code is for a form with two buttons (named StartBtn and StopBtn).
public partial class Form1 : Form
{
private WaveIn waveIn = null;
private BufferedWaveProvider waveProvider = null;
private WaveOut waveOut = null;
public Form1()
{
InitializeComponent();
}
private void StartBtn_Click(object sender, EventArgs e)
{
if (waveIn != null)
return;
// create wave input from mic
waveIn = new WaveIn(this.Handle);
waveIn.BufferMilliseconds = 25;
waveIn.RecordingStopped += waveIn_RecordingStopped;
waveIn.DataAvailable += waveIn_DataAvailable;
// create wave provider
waveProvider = new BufferedWaveProvider(waveIn.WaveFormat);
// create wave output to speakers
waveOut = new WaveOut();
waveOut.DesiredLatency = 100;
waveOut.Init(waveProvider);
waveOut.PlaybackStopped += wavePlayer_PlaybackStopped;
// start recording and playback
waveIn.StartRecording();
waveOut.Play();
}
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
// add received data to waveProvider buffer
if (waveProvider != null)
waveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
}
private void StopBtn_Click(object sender, EventArgs e)
{
if (waveIn != null)
waveIn.StopRecording();
}
void waveIn_RecordingStopped(object sender, StoppedEventArgs e)
{
// stop playback
if (waveOut != null)
waveOut.Stop();
// dispose of wave input
if (waveIn != null)
{
waveIn.Dispose();
waveIn = null;
}
// drop wave provider
waveProvider = null;
}
void wavePlayer_PlaybackStopped(object sender, StoppedEventArgs e)
{
// stop recording
if (waveIn != null)
waveIn.StopRecording();
// dispose of wave output
if (waveOut != null)
{
waveOut.Dispose();
waveOut = null;
}
}
}
Note especially the waveIn.BufferMilliseconds and waveOut.DesiredLatency settings to reduce the lag times.
For compressing the data for network transmission I suggest using a different library to process the data blocks. You might need to tune the BufferMilliseconds value to reduce the overheads and get better compression ratios.
The Opus Codec looks like a reasonable option, with Opus.NET for C#.

Categories