I have been trying to record and play the audio simultaneously with out using a temporary wav file. And later I would like to create a VOIP chat program.
I have used the Naudio library to capture and play audio in C# and it seems to work quite well.
below is the c # code that i have written:
using System.IO.Ports;
using NAudio.Wave;
using System.IO;
namespace VOIP
{
public partial class Form1 : Form
{
WaveIn wab = new WaveIn();
MemoryStream s;
int k;
public Form1()
{
InitializeComponent();
wab.BufferMilliseconds = 100;
wab.NumberOfBuffers=5;
wab.DataAvailable += new EventHandler<WaveInEventArgs>(wa_DataAvailable);
}
void wa_DataAvailable(object sender, WaveInEventArgs e)
{
Play(e.Buffer);
}
private void Play(byte[] p)
{
WaveOut ou = new WaveOut();
s = new MemoryStream(p);
RawSourceWaveStream r = new RawSourceWaveStream(s, wab.WaveFormat);
ou.Init(r);
ou.Play();
ou.Stop();
ou.Dispose();
s.Dispose();
r.Dispose();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text == "Stop")
{
wab.StopRecording();
button1.Text = "Record";
}
else if (button1.Text == "Record")
{
wab.StartRecording();
button1.Text = "Stop";
}
}
}
}
The Problem is at the "Play" method .Since the waveout object is created and disposed every time the data is available : i can hear some clicking sound. Is there a way to avoid this way of creating and disposing object and instead just create one object and then initialize with the new data. I also observed that the memory consumed by this program keeps increasing.
Thanks in advance.
sanatan
Use a BufferedWaveProvider, and as audio arrives, decompress it and add it to the BufferedWaveProvider. Then have a single instance of WaveOut that is playing constantly from the BufferedWaveProvider.
The source code for NAudioDemo shows how to do this in the chat example.
Related
I'm trying to play OGG file stream with NVorbis and NAudio, as described in the documention, I'm trying to run this code when on a Button click, but I get an exception:
System.ArgumentException: 'Could not initialize container!'
I'm targeting .Net Framework 4.5
Here is my code:
private void button1_Click(object sender, EventArgs e)
{
using (var vorbisStream = new NAudio.Vorbis.VorbisWaveReader(#"OGG file path"))
using (var waveOut = new NAudio.Wave.WaveOutEvent())
{
waveOut.Init(vorbisStream);
waveOut.Play();
// wait here until playback stops or should stop
}
}
Two issues here:
The first is that you cannot declare with using statements either the WaveOutReader or WaveOut object, since the latter is playing the sound asynchronously and it needs the WaveOutReader's Stream open
You were targeting .Net Framework 4.5, which is not suitable to handle the current version of both NAudio and NVorbis
I assume you have already successfully installed the NAudio 2.1.0 and NAudio.Vorbis 1.5.0 packages in a new .Net 6+ Project.
You just need to add references to NAudio.Vorbis and NAudio.Wave
Targeting .Net 6+, nullable enabled.
Add two Button Controls to a Form, one named btnPlayAudio and the other btnStopPlayback, initially disabled; subscribe to their Click event using the event handlers shown here.
The WaveOutReader and the WaveOut object that acts as player are declared as instance Fields, initially set to null.
When you press the Play Audio Button, the audio Stream and the Player are initialized, calling the WaveOutInit() method, which also subscribes to the WaveOut object's PlaybackStopped event.
When you call the WaveOut object's Stop() method or the OGG playback terminates, this event is raised. Here, the Stop() method is called in the btnStopPlayback click handler.
When this happens, another method, WaveOutReset(), is called; this method disposes of the WaveOut object and the Stream of the WaveOutReader. The event handler is also removed.
using NAudio.Vorbis;
using NAudio.Wave;
public partial class MainForm : Form {
private VorbisWaveReader? vorbis = null;
private WaveOut? oggPlayer = null;
private void btnPlayAudio_Click(object sender, EventArgs e)
{
btnPlayAudio.Enabled = false;
btnStopPlayback.Enabled = true;
vorbis = new VorbisWaveReader("OGG File path"));
oggPlayer = WaveOutInit(vorbis);
oggPlayer.Play();
}
private void btnStopPlayback_Click(object sender, EventArgs e)
{
oggPlayer?.Stop();
btnStopPlayback.Enabled = false;
}
private void WaveOut_PlaybackStopped(object? sender, StoppedEventArgs e)
{
WaveOutReset(oggPlayer, vorbis);
btnPlayAudio.Enabled = true;
}
private WaveOut WaveOutInit(IWaveProvider reader)
{
var waveOut = new WaveOut();
waveOut.PlaybackStopped += WaveOut_PlaybackStopped;
waveOut.Init(reader);
return waveOut;
}
private void WaveOutReset(WaveOut? player, VorbisWaveReader? reader)
{
if (player != null) {
player.PlaybackStopped -= WaveOut_PlaybackStopped;
player.Dispose();
}
reader?.Dispose();
}
}
I am having some trouble playing a 4second long wave... What I am currently doing is running a timer....
So the timer is set to a second intervals... So every second, I run off and check something... If this check fails.. I play a wav file saying "Get back to work!"...
Currently, it pauses the timer.... So I hear "Get back to work" but while it is playing, I have lost 4 seconds of count time, because it is still finishing playing the sound.... Here is my call and my function...
playSimpleSound();
private void playSimpleSound()
{
SoundPlayer simpleSound = new SoundPlayer(#"c:\Windows\Media\shortwav.wav");
simpleSound.PlaySync();
}
If I switch them out, so that it actually plays everytime it hits.... I only hear the beginning of the wav file....
playSimpleSound();
private void playSimpleSound()
{
SoundPlayer simpleSound = new SoundPlayer(#"c:\Windows\Media\shortwav.wav");
simpleSound.Play();
}
So my question is...
How can I continue counting, and play the whole wav file?
Should I figure out how long the wav file is and then go ahead and do some kind of count with a mod on it?
So that I basically only play the file every x amount of seconds or something?
So basically just call the playsound function everytime, but inside that function count how many times it has been visited and only play it on the 4th second?
You could do something like this...play the sound on a different thread and toggle a flag:
public partial class Form1 : Form
{
private SoundPlayer simpleSound;
private bool SoundPlaying = false;
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
}
void Form1_Load(object sender, EventArgs e)
{
simpleSound = new SoundPlayer(#"c:\Windows\Media\shortwav.wav");
simpleSound.LoadAsync();
}
private void timer1_Tick(object sender, EventArgs e)
{
Console.WriteLine("Tick");
if (true) // check your condition
{
this.PlaySound();
}
}
private void PlaySound()
{
if (!this.SoundPlaying)
{
Console.WriteLine("Starting Sound");
this.SoundPlaying = true;
Task.Factory.StartNew(() => {
simpleSound.PlaySync();
this.SoundPlaying = false;
});
}
}
}
I just want to play 4 sounds after each other (sounds1->sound2->sound3), but without stopping the flow in my code during each play or without waiting for each sound to finish.
I have searched for this about everywhere, but every direction I read, gets stuck in some other problem.
My best bet so far was: using my already used SoundPlayer from System.Media and make my own queue function, but Soundplayer doesn't have a "finished playing" event so I have no idea of knowing when to start the next sound. (Really, Microsoft?)
Other solution and problems:
DirectSound seems complicated to get working in .NET (c#).
Win Playsound doesn't really help because it can't queue either.
You can try to use PlaySync on a thread outside UI, eg: Background thread, as some people have commented.
Here is a sample (untested) using a thread-safe* BlockingCollection for the queue
* which you can use in and outside the thread
You may want to make your own class or methods that rises an event every time the sounds ends. Or you can just loop the queue in the thread since PlaySync will just wait by itself.
using System.Threading;
using System.Collections.Concurrent;
namespace PlaySound
{
public partial class Form1 : Form
{
private Thread soundPlayThread;
private BlockingCollection<string> speakQueue = new BlockingCollection<string>();
private CancellationTokenSource cancelSoundPlay;
private int soundPlayCount = 0;
public Form1()
{
InitializeComponent();
cancelSoundPlay = new CancellationTokenSource();
}
private void btnStartSoundPlay_Click(object sender, EventArgs e)
{
StartSoundPlay();
}
private void btnStopSoundPlay_Click(object sender, EventArgs e)
{
cancelSoundPlay.Cancel();
Console.WriteLine("Sound play cancelled.");
}
private void btnAddToQueue_Click(object sender, EventArgs e)
{
speakQueue.Add("MyFile.wav");
}
private void queueAndPlay(string loc)
{
if (!File.Exists(loc=loc+".wav"))
loc=configPath+"soundnotfound.wav";
speakQueue.Add(loc);
StartSoundPlay();
}
private void StartSoundPlay()
{
//Sound Player Loop Thread
if (this.soundPlayThread == null || !this.soundPlayThread.IsAlive)
{
this.soundPlayThread = new Thread(SoundPlayerLoop);
this.soundPlayThread.Name = "SoundPlayerLoop";
this.soundPlayThread.IsBackground = true;
this.soundPlayThread.Start();
Console.WriteLine("Sound play started");
}
}
//Method that the outside thread will use outside the thread of this class
private void SoundPlayerLoop()
{
var sound = new SoundPlayer();
foreach (String soundToPlay in this.speakQueue.GetConsumingEnumerable(cancelSoundPlay.Token))
{
//http://msdn.microsoft.com/en-us/library/system.media.soundplayer.playsync.aspx
speaker.SoundLocation=soundToPlay;
//Here the outside thread waits for the following play to end before continuing.
sound.PlaySync();
soundPlayCount++;
Console.WriteLine("Sound play end. Count: " + soundPlayCount);
}
}
}
}
I an new in emgu cv c#. I want to create a camera only for simple photocapture from my laptop camera and other camera device connected to my laptop.I dont want video capture only simple photo capture.with one start and one capture button.and will save in particular location.helped would be appreciable.
namespace camera
{
public partial class cameracaps : Form
{
Capture capturecam=null;
bool capturingprocess=false;
Image<Bgr,Byte>imgOrg;
Image<Gray,Byte>imgproc;
public cameracaps()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
Capture cam = new Capture();
}
catch (NullReferenceException exception)
{
MessageBox.Show(exception.Message);
return;
}
Application.Idle += new EventHandler(processFunction);
capturingprocess=true;
}
void processFunction(object sender,EventArgs e)
{
imgOrg=capturecam.QueryFrame();
if(imgOrg ==null)return;
imgproc=imgOrg.InRange(new Bgr(50,50,50),new Bgr(250,250,250));
imgproc = imgproc.SmoothGaussian(9);
original.Image=imgOrg;
processed.Image=imgproc;
}
private void Button1_Click(object sender, EventArgs e)
{
if(capturingprocess==true)
{
Application.Idle-=processFunction;
capturingprocess = false;
Button1.Text="play";
}
else
{
Application.Idle+= processFunction;
capturingprocess= true;
Button1.Text="pause";
}
}
}
}
showing..The type initializer for 'Emgu.CV.CvInvoke' threw an exception. error..indicating error in
Capture cam = new Capture();
help me.
This is one of the biggest problem almost all the time we face in emgu cv.First of all copy dll file name opencv_core290.dll,open_cvhighgui290.dll to your project bin,try to use dependency tracker for dll files.Install dependency from this link-http://www.emgu.com/wiki/index.php/Download_And_Installation..If its not work then you should understand that application is getting connection with camera.Then go to My computer->right click->property->advanced system setting->system property->advance->environment variables->system variables->path->edit->and paste C:\Emgu\emgucv-windows-universal-cuda 2.9.0.1922\bin.and press ok.restart your system and it will work.
I am a starter who is stuck very badly on this initially my main aim is to control robots using speech. Initially I started with making grammar for my speech with this code I was even successful my code is this I made this in windows form application:
using System.Speech.Recognition;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Create a new SpeechRecognizer instance.
sr = new SpeechRecognizer();
// Create a simple grammar that recognizes "red", "green", or "blue".
Choices colors = new Choices();
colors.Add("red");
colors.Add("green");
colors.Add("blue");
colors.Add("white");
GrammarBuilder gb = new GrammarBuilder();
gb.Append(colors);
// Create the actual Grammar instance, and then load it into the speech recognizer.
Grammar g = new Grammar(gb);
sr.LoadGrammar(g);
// Register a handler for the SpeechRecognized event.
sr.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(sr_SpeechRecognized);
}
// Simple handler for the SpeechRecognized event.
private void sr_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
MessageBox.Show(e.Result.Text);
}
private SpeechRecognizer sr;
}
Now from this code when I speak red , I get red in message box now I want to control motors therefore i need to communicate with my robots therefore i MADE ONE CONSOLE APPLICATION from help from internet FOR SENDING DATA TO MY SERVO CONTROLLER -SSC 32 THE CODE FOR ABOVE IS:
using System.IO.Ports;
using System.Threading;
namespace cConsoleAppMonitorServoCompletion
{
class Program
{
static SerialPort _serialPort;
static void Main(string[] args)
{
try
{
_serialPort = new SerialPort();
_serialPort.PortName = "COM3";
_serialPort.Open();
_serialPort.Write("#27 P1600 S750\r");
Console.WriteLine("#27 P1500 S750\r");
string output;
output = "";
//Example: "Q <cr>"
//This will return a "." if the previous move is complete, or a "+" if it is still in progress.
while (!(output == ".")) //loop until you get back a period
{
_serialPort.Write("Q \r");
output = _serialPort.ReadExisting();
Console.WriteLine(output);
Thread.Sleep(10);
}
_serialPort.Close();
}
catch (TimeoutException) { }
}
}
}
Now I want like when I speak red instead of giving a text box I want get serial command like _serialPort.Write("#27 P1600 S750\r");
Please help I have tried but I was not successful , it is my humble request please answer in more detailed manner , I am a just starter so it will be easy for me thanks in advance.
Controlling a robot using voice recognition... an ambitious project for a starter! There could be a million things going wrong here.
Just as important as the ability to write code is the ability to debug it. What can you tell us further - which parts work, which parts don't? Have you single-stepped through the code to see what happens and when, to diagnose where things start to go wrong?
You could also try some debugging output - Console.WriteLine for example - so we you can see the state of variables and flow of the code as it's running.
It looks like you need to use System.Diagnostics.Process.Start
This page has an example - how to execute console application from windows form?
// Simple handler for the SpeechRecognized event.
private void sr_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
System.Diagnostics.Process.Start( #"cmd.exe", #"/k c:\path\my.exe" );
}
An ambitious starter project indeed!
Update
private bool LaunchApp(String sAppPath, String sArg)
{
bool bSuccess = false;
try
{
//create a new process
Process myApp = new Process();
myApp.StartInfo.FileName = sAppPath;
myApp.StartInfo.Arguments = sArg;
bSuccess = myApp.Start();
}
catch (Win32Exception e)
{
MessageBox.Show("Error Details: {0}", e.Message);
}
return bSuccess;
}
if Now I want like when I speak red instead of giving a text box I want get serial command means - just to _serialPort.Write("#27 P1600 S750\r"); instead of showing messagebox (i.e. MessageBox.Show(e.Result.Text);) then task is really simple. just copy-paste that code. and add using System.IO.Ports; so that u can work with ports.
so prolly ur code will look like this:
private void sr_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
//MessageBox.Show(e.Result.Text);
try
{
_serialPort = new SerialPort();
_serialPort.PortName = "COM3";
_serialPort.Open();
_serialPort.Write("#27 P1600 S750\r");
Console.WriteLine("#27 P1500 S750\r");
string output;
output = "";
//Example: "Q <cr>"
//This will return a "." if the previous move is complete, or a "+" if it is still in progress.
while (!(output == ".")) //loop until you get back a period
{
_serialPort.Write("Q \r");
output = _serialPort.ReadExisting();
Console.WriteLine(output);
Thread.Sleep(10);
}
_serialPort.Close();
}
catch (TimeoutException) { }
}
p.s.
if you don't understand how SerialPort Class works go to MSDN