Issues with BufferedWaveProvider - c#

I've been wracking my brain at this for awhile now. I've looked all over on SO already and I'm not finding any answers to my problem here. What I'm attempting to accomplish is a function that will allow my to select an input card for a microphone and output card to go to a radio. This code works on the initial try, but once i stop "Transmitting" you can hear what sounds like a doubled up audio stream and it becomes laggy and eventually crashes with a buffer full exception. I'm not sure what i'm doing wrong here.
public WaveOutEvent outputDevice = new WaveOutEvent() { DeviceNumber = -1 };
public WaveInEvent inputDevice = new WaveInEvent() { DeviceNumber = -1 };
public bool transmit = false;
public bool markerActive = false;
public bool alert1Active = false;
public SerialPort port = new SerialPort();
public string[] ports = SerialPort.GetPortNames();
private BufferedWaveProvider bufferedWaveProvider;
public string keyTransmitter()
{
string label;
if (transmit)
{
transmit = false;
port.DtrEnable = false;
port.RtsEnable = false;
label = "Transmit";
bufferedWaveProvider.ClearBuffer();
inputDevice.StopRecording();
inputDevice.Dispose();
outputDevice.Dispose();
outputDevice.Stop();
}
else
{
transmit = true;
port.DtrEnable = true;
port.RtsEnable = true;
label = "Transmitting";
bufferedWaveProvider = new BufferedWaveProvider(inputDevice.WaveFormat);
inputDevice.DataAvailable += OnDataAvailable;
inputDevice.StartRecording();
outputDevice.Init(bufferedWaveProvider);
outputDevice.Play();
}
return label;
}
public void OnDataAvailable(object sender, WaveInEventArgs args)
{
bufferedWaveProvider.AddSamples(args.Buffer, 0, args.BytesRecorded);
//bufferedWaveProvider.DiscardOnBufferOverflow = true;
}

A BufferedWaveProvider has a size limit, so it must be changed if it wants more audio, but it will eventually overload, so we'll use "DiscardOnBufferOverflow", so here's the code example:
NOTE: if you are using a single reproduction, it is not necessary to change BufferedWaveProvider to a new one.
BufferedWaveProvider waveChannel = new BufferedWaveProvider(INFO HERE);
waveChannel.BufferDuration = new TimeSpan(0, 1, 0); //BufferDuration, 1 minute
waveChannel.DiscardOnBufferOverflow = true; //The name says
It will not be necessary to use "init" or "play" again, because BufferedWaveProvider, when not buffered, it generates silence for WaveOut
English from google translator

Related

c# thread safety socket confusion

I have what I believe to be a thread safety issue.
I have two printers, one is loaded with a small label and the other a large. I want to send 1 print job over a socket to each printer.
I have attempted to thread/background the first request (large label) as it can take a long time to complete.
99% of the time the script works as expected. The small and big labels come out of their respective printers.
However, every now and then both the big and small labels are sent to the same printer! Either both to the small or large.
I think it's related to thread safety but I am finding it very hard to track down and understand whats happening. I've tried to add a lock and to close the sockets after use, but whatever I try the issue persists.
I've attempted to reduce my code to the bare minimum but am aware this post is still very code heavy. Any advice would be greatly appreciated.
// stores the printer info
class PrinterBench
{
public PrinterBench(string sArg_PostageLabelIP, string sArg_SmallLabelIP)
{
PostageLabelIP = sArg_PostageLabelIP;
SmallLabelIP = sArg_SmallLabelIP;
}
public string PostageLabelIP;
public string SmallLabelIP;
}
// main entry point
class HomeController{
PrintController oPrintController;
List<string> lsLabelResults = new List<string>("label result");
PrinterBench pbBench = new PrinterBench("192.168.2.20","192.168.2.21");
void Process(){
oPrintController = new PrintController(this);
if(GetLabel()){
// should always come out of the big printer (runs in background)
oPrintController.PrintBySocketThreaded(lsLabelResults, pbBench.PostageLabelIP);
// should always come out of the small printer
oPrintController.PrintWarningLabel();
}
}
}
class PrintController{
HomeController oHC;
public EndPoint ep { get; set; }
public Socket sock { get; set; }
public NetworkStream ns { get; set; }
private static Dictionary<string, Socket> lSocks = new Dictionary<string, Socket>();
private BackgroundWorker _backgroundWorker;
static readonly object locker = new object();
double dProgress;
bool bPrintSuccess = true;
public PrintController(HomeController oArg_HC)
{
oHC = oArg_HC;
}
public bool InitSocks()
{
// Ensure the IP's / endpoints of users printers are assigned
if (!lSocks.ContainsKey(oHC.pbBench.PostageLabelIP))
{
lSocks.Add(oHC.pbBench.PostageLabelIP, null);
}
if (!lSocks.ContainsKey(oHC.pbBench.SmallLabelIP))
{
lSocks.Add(oHC.pbBench.SmallLabelIP, null);
}
// attempt to create a connection to each socket
foreach (string sKey in lSocks.Keys.ToList())
{
if (lSocks[sKey] == null || !lSocks[sKey].Connected )
{
ep = new IPEndPoint(IPAddress.Parse(sKey), 9100);
lSocks[sKey] = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
lSocks[sKey].Connect(ep);
}
}
return true;
}
public bool PrintBySocketThreaded(List<string> lsToPrint, string sIP)
{
// open both the sockets
InitSocks();
bBatchPrintSuccess = false;
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.WorkerSupportsCancellation = true;
object[] parameters = new object[] { lsToPrint, sIP, lSocks };
_backgroundWorker.RunWorkerAsync(parameters);
return true;
}
// On worker thread, send to print!
public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
object[] parameters = e.Argument as object[];
double dProgressChunks = (100 / ((List<string>)parameters[0]).Count);
int iPos = 1;
Dictionary<string, Socket> dctSocks = (Dictionary<string, Socket>)parameters[2];
foreach (string sLabel in (List<string>)parameters[0] )
{
bool bPrinted = false;
// thread lock print by socket to ensure its not accessed twice
lock (locker)
{
// get relevant socket from main thread
bPrinted = PrintBySocket(sLabel, (string)parameters[1], dctSocks[(string)parameters[1]]);
}
iPos++;
}
while (!((BackgroundWorker)sender).CancellationPending)
{
((BackgroundWorker)sender).CancelAsync();
((BackgroundWorker)sender).Dispose();
//Thread.Sleep(500);
}
return;
}
// Back on the 'UI' thread so we can update the progress bar (have access to main thread data)!
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null) MessageBox.Show(e.Error.Message);
if (bPrintSuccess) oHC.WriteLog("Printing Complete");
bBatchPrintSuccess = true;
((BackgroundWorker)sender).CancelAsync();
((BackgroundWorker)sender).Dispose();
}
/// sends to printer via socket
public bool PrintBySocket(string sArg_ToPrint, string sIP, Socket sock = null)
{
Socket sTmpSock = sock;
if (sTmpSock == null)
{
InitSocks();
if (!lSocks.ContainsKey(sIP)){
throw new Exception("Sock not init");
}else{
sTmpSock = lSocks[sIP];
}
}
using (ns = new NetworkStream(sTmpSock))
{
byte[] toSend = Encoding.ASCII.GetBytes(sEOL + sArg_ToPrint);
ns.BeginWrite(toSend, 0, toSend.Length, OnWriteComplete, null);
ns.Flush();
}
return true;
}
public bool PrintWarningLabel()
{
string sOut = sEOL + "N" + sEOL;
sOut += "LL411" + sEOL;
sOut += "R40,0" + sEOL;
sOut += "S5" + sEOL;
sOut += "D15" + sEOL;
sOut += "A0,0,0,4,4,3,N,\"!!!!!!!!!!!!!!!!!!!!!!!\"" + sEOL;
sOut += "A0,150,0,4,3,3,N,\"WARNING MESSAGE TO PRINT\"" + sEOL;
sOut += "A0,280,0,4,4,3,N,\"!!!!!!!!!!!!!!!!!!!!!!!\"" + sEOL;
sOut += "P1";
sOut += sEOL;
if (PrintBySocket(sOut, oHC.pbBench.SmallLabelIP))
{
oHC.WriteLog("WARNING LABEL PRINTED");
return true;
}
return false;
}
}
You've got this field in PrintController:
public NetworkStream ns { get; set; }
It's only used here:
using (ns = new NetworkStream(sTmpSock))
{
byte[] toSend = Encoding.ASCII.GetBytes(sEOL + sArg_ToPrint);
ns.BeginWrite(toSend, 0, toSend.Length, OnWriteComplete, null);
ns.Flush();
}
If two threads execute this at the same time, one can change ns to a different NetworkStream when the other is about to write to it.
Since ns is used and disposed right here, there doesn't seem to be any reason to declare it as a field, which means multiple threads can overwrite it. Instead, delete the field and change your code to this:
using (var ns = new NetworkStream(sTmpSock))
Then multiple threads executing this will create their own NetworkStream as a local variable instead of fighting over one.
I'd check all the other fields too and see if they need to be fields or if they can just be declared as local variables.
Unintentional shared state is bad for multithreaded code. It will behave exactly as you described. It works, it works, it works, and then it doesn't work, and reproducing the problem when you want to see it will be nearly impossible.

How to record and playback audio from default audio device

I can't seem to record the audio from default audio device, and play it on another audio device..
I don't want to record the microphone, but the audio device..
When I play a movie, I can hear sound, through my headphones, I want to copy that sound to any audio device I choose..
If you have any suggestions, it doesn't have to be with NAudio..
As far as I can tell, NAudio can't do this..
This is the code I use for the task at this time, but it only takes input from my Microphone: Code snippet with NAudio..
void playSoundCopy(int dv0)
{
disposeWave0();// stop previous sounds before starting
var waveOut0 = new WaveOut();
waveOut0.DeviceNumber = dv0;
wave0 = waveOut0;
Defaultwave0 = new WaveIn();
Defaultwave0.DeviceNumber = (int)GetDefaultDevice(Defaultdevice.FriendlyName);
var waveinReader0 = new WaveInProvider(Defaultwave0);
wave0.Init(waveinReader0);
play0 = false;
Thread.Sleep(1000);
play0 = true;
t0 = new Thread(() => { timeline0(); });
t0.IsBackground = true;
t0.Start();
Defaultwave0.StartRecording();
wave0.Play();
}
The real problem is actually that I can't record from a WaveOut device, only WaveIn..
Working Result:
void playSoundCopy(int dv0)
{
disposeWave0();// stop previous sounds before starting
var waveOut0 = new WaveOut();
waveOut0.DeviceNumber = dv0;
wave0 = waveOut0;
var format0 = Defaultdevice.AudioClient.MixFormat;
buffer0 = new BufferedWaveProvider(format0);
wave0.Init(buffer0);
capture = new WasapiLoopbackCapture(Defaultdevice);
capture.ShareMode = AudioClientShareMode.Shared;
capture.DataAvailable += CaptureOnDataAvailable;
play0 = false;
Thread.Sleep(1000);
play0 = true;
t0 = new Thread(() => { timeline0(); });
t0.IsBackground = true;
t0.Start();
capture.StartRecording();
wave0.Play();
}
void CaptureOnDataAvailable(object sender, WaveInEventArgs waveInEventArgs)
{
try
{
var itm = new byte[waveInEventArgs.BytesRecorded];
Array.Copy(waveInEventArgs.Buffer, itm, waveInEventArgs.BytesRecorded);
buffer0.AddSamples(itm, 0, itm.Length);
}
catch { }
}
You can capture audio being sent to a device using WasapiLoopbackCapture. Then you could pipe that into a BufferedWaveProvider and use that to feed another output device. There would be some latency introduced though, so don't expect the two devices to be in sync.

How to continuously ping and result changes an image

Hoping you can help me out here.
I am having a issue figuring out how to loop a piece of ping code and result ( if true or false ) changes an image.
here is my code running on a button click,
My aim here is to basically automise the app so the user does not have to click the refresh icon,
I have read a bit and am thinking I could use a while loop, But am unsure on how to use sleep with it.
The other method would be to use a thread with sleep?
I have also asked on MSDN to get the most feedback possible.
//Declaration of Global Variables (IP's)
public static string IP1, IP2, IP3;
//Method Setting Variables(IP's)
public static void setIP()
{
IP1 = Properties.Settings.Default.settingIP1;
IP2 = Properties.Settings.Default.settingIP2;
IP3 = Properties.Settings.Default.settingIP3;
}
//Method running Ping command
public static bool PingTest(string toPing)
{
string host = toPing;
bool result = false;
Ping p = new Ping();
try
{
PingReply reply = p.Send(host, 3000);
if (reply.Status == IPStatus.Success)
return true;
}
catch { }
return result;
}
//Refreshes IP1 then changes label to result
private void btnRefreshIP1_Click(object sender, RoutedEventArgs e)
{
setIP();
bool isConnected = false;
isConnected = PingTest(IP1);
if(isConnected == true)
{
lblsIP1.Foreground = Brushes.White;
lblsIP1.Content = "Online";
}
else
{
lblsIP1.Foreground = Brushes.Red;
lblsIP1.Content = "Offline";
}
}

UnauthorizedAccessException on MediaElement in DatagramSocket.ReceivedMessage

I want start my mediaElement (instance of MediaElement class) at when first pack of data came. So I wrote it like that.
private void SocketOnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
if (isPaused) return;
if (!isStarted)
{
mediaElement.Play();
isStarted = true;
}
var r = args.GetDataReader();
var l = r.UnconsumedBufferLength;
var buff = new byte[l];
r.ReadBytes(buff);
if (System.Text.Encoding.UTF8.GetString(buff, 0, buff.Length) != "stop")
{
AudioSteam.AddBytes(buff);
}
else
{
mediaElement.Pause();
isStarted = false;
Debug.WriteLine("stop");
}
buff = null;
}
Of course elier I set source for mediaElement (MediaStreamSource).
var socket = new DatagramSocket();
socket.MessageReceived += SocketOnMessageReceived;
await socket.BindServiceNameAsync("4444");
HostName multicast = new HostName("230.0.0.1");
socket.JoinMulticastGroup(multicast);
isPaused = true;
isStarted = false;
AudioSteam = new Sine440AudioStreamSource(44100);
mdiaElement.SetSource(AudioSteam);
At first I get System.IO.FileNotFoundException then System.UnauthorizedAccessException at mediaElement.Start(); I have no idea why.
Any suggestion will be appreciated.
It's turns out that DatagramSocket.ReceivedMessage method is running at other thread so If you want access variables out of method scope you need to use Dispatcher.BeginInvoke()
Dispatcher.BeginInvoke(() =>{
//Code
});
All in all I resign for that approach.

How to play sound in many devices at the same time

I want to play sound in three external sound cards at the same time,I mean when I click in a button I can hear sound from three speakers which are related to my three sound cards.
I have a function but it plays sound only in one device,the first one it finds,I mean in this code the first device is number 0,so it play sound in it,but if you write device number 1 at first,it will play sound in it,as a conclusion it plays sound only in one device,it dont works for all the devices at the same time.
This is its code:
public void playAllAvailableDevices()
{
//create a new class for each wav file & output etc.
WaveOut waveOut1 = new WaveOut();
WaveFileReader waveReader1 = new WaveFileReader(fileName);
WaveOut waveOut2 = new WaveOut();
WaveFileReader waveReader2 = new WaveFileReader(fileName);
WaveOut waveOut3 = new WaveOut();
WaveFileReader waveReader3 = new WaveFileReader(fileName);
switch (waveOutDevices)
{
case 1:
waveOut1.Init(waveReader1);
waveOut1.DeviceNumber = 0;
waveOut1.Play();
break;
case 2:
waveOut1.Init(waveReader1);
waveOut1.DeviceNumber = 0;
waveOut1.Play();
waveOut2.Init(waveReader2);
waveOut2.DeviceNumber = 1;
waveOut2.Play();
break;
case 3:
waveOut1.Init(waveReader1);
waveOut1.DeviceNumber = 0;
waveOut1.Play();
waveOut2.Init(waveReader2);
waveOut2.DeviceNumber = 1;
waveOut2.Play();
waveOut3.Init(waveReader3);
waveOut3.DeviceNumber = 2;
waveOut3.Play();
break;
}}
fileName is the name of the sound file I want to play,in my code I get this name from a darabase:
private void btnAlarm1_Click(object sender, EventArgs e)
{
OdbcConnection cn = new OdbcConnection("DSN=cp1");
cn.Open();
OdbcCommand cmd1 = new OdbcCommand("select chemin from alarme where code_alarme=41", cn);
cmd1.Connection = cn;
fileName = cmd1.ExecuteScalar().ToString();
wave = new WaveOut();
playAllAvailableDevices();
}
Can you help me to find the solution please????
Thank you in advance.
Good day.
You need to set the DeviceNumber property on the WaveOut object before calling Init. You could clean up your code a lot by using a simple function:
private void PlaySoundInDevice(int deviceNumber, string fileName)
{
if (outputDevices.ContainsKey(deviceNumber))
{
outputDevices[deviceNumber].WaveOut.Dispose();
outputDevices[deviceNumber].WaveStream.Dispose();
}
var waveOut = new WaveOut();
waveOut.DeviceNumber = deviceNumber;
WaveStream waveReader = new WaveFileReader(fileName);
// hold onto the WaveOut and WaveStream so we can dispose them later
outputDevices[deviceNumber] = new PlaybackSession { WaveOut=waveOut, WaveStream=waveReader };
waveOut.Init(waveReader);
waveOut.Play();
}
private Dictionary<int, PlaybackSession> outputDevices = new Dictionary<int, PlaybackSession>();
class PlaybackSession
{
public IWavePlayer WaveOut { get; set; }
public WaveStream WaveStream { get; set; }
}
The dictionary holds onto the WaveOut so it doesn't get garbage collected during playback, and allows us to clean up properly. Before you exit your application, make sure you clean up properly:
private void DisposeAll()
{
foreach (var playbackSession in outputDevices.Values)
{
playbackSession.WaveOut.Dispose();
playbackSession.WaveStream.Dispose();
}
}
And now you can play in all available devices using a for loop instead of your switch statement that requires you to duplicate code:
public void PlayInAllAvailableDevices(string fileName)
{
int waveOutDevices = WaveOut.DeviceCount;
for (int n = 0; n < waveOutDevices; n++)
{
PlaySoundInDevice(n, fileName);
}
}
I think what you are looking for is the BASS audio library:
http://www.un4seen.com/
This might also work:
http://www.alvas.net/alvas.audio.aspx
I don't think C# is set up to do this without a third-party add-in like the ones listed above. Maybe someone smarter than I can help you get it working but if not, these libraries will help you down the path you want.

Categories