AxWindowsMediaPlayer Clip Will Sometimes Not Play - c#

I have a simple one-threaded windows forms .NET 4.5 app where user listens to spoken words (wav files) and then selects the correct picture that represents the word.
The problem is that the clip will sometimes (very rarely - about 1% of the time and completelly at random) not play...
This is the method for playing clips:
public static void PlayWordAudio(Word word, AxWMPLib.AxWindowsMediaPlayer player)
{
string tempFile = Path.GetTempFileName() + ".wav";
MemoryStream stream = new MemoryStream(word.Audio);
using (Stream fileStream = File.OpenWrite(tempFile))
{
stream.WriteTo(fileStream);
}
player.URL = tempFile;
File.Delete(tempFile);
}
Can someone please suggest a solution to this problem? Maybe I shouldn't delete the file at the end of the method? But then temp files would pile up...
I am on Windows 7...

I guess the file is being deleted quicker than it can get played.
Can you try this in stead of File.Delete(tempFile); utilizing the PlayStateChange event
player.PlayStateChange += (snd, psce) => {
switch (psce.newState)
{
case 1: // Stopped (maybe use 12 => Last )
File.Delete(tempFile);
break;
default:
Debug.WriteLine(psce.newState);
break;
}
};
You might have to unsubscribe the event if you keep the player object around a long time.

It seems that I solved the problem... it was in fact the deletion of file that caused this...
solution:
public static void PlayWordAudio(Word word, AxWMPLib.AxWindowsMediaPlayer player)
{
string tempFile = Path.GetTempFileName() + ".wav";
MemoryStream stream = new MemoryStream(word.Audio);
using (Stream fileStream = File.OpenWrite(tempFile))
{
stream.WriteTo(fileStream);
}
player.URL = tempFile;
RunDelayed(5000, File.Delete, tempFile); //if we delete file immediately then clip sometimes would not be played
}
public delegate void DelayedFuncion(string param);
public static void RunDelayed(int delay, DelayedFuncion function, string param = null)
{
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
DelayedArgs args = new DelayedArgs() { delayedFunction = function, param = param };
timer.Tag = args;
timer.Tick += TimerElapsed;
timer.Interval = delay;
timer.Start();
}
private static void TimerElapsed(object sender, EventArgs e)
{
System.Windows.Forms.Timer timer = sender as System.Windows.Forms.Timer;
timer.Stop();
DelayedArgs args = timer.Tag as DelayedArgs;
args.delayedFunction(args.param);
}
class DelayedArgs
{
public Util.DelayedFuncion delayedFunction;
public string param;
}

Related

Read text file line by line using timer

StreamReader sr = new StreamReader("C:/CR EZ Test/Log.txt"); //use with IF
private void timer2_Tick(object sender, EventArgs e)
{
if ((line = sr.ReadLine()) != null)
{
//FileStream fs = File.Open("C:/CR EZ Test/Log.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
//StreamReader sr = new StreamReader(fs); //use with While can't use with }else{
//while ((line = sr.ReadLine()) != null)
//{
string[] dataLog = line.Split(new[] { ',' }, StringSplitOptions.None);
mpa = (dataLog[1]);
ml = (dataLog[2]);
lph = (dataLog[3]);
elapsedTime = float.Parse(dataLog[4]) / 1000;
if (testStatus > 0) time = elapsedTime.ToString("0.0");
tb2.Value = int.Parse(dataLog[6]);
if (chart1.Series[0].Points.Count > tb1.Value && tb1.Value > 0)
{
chart1.Series[0].Points.RemoveAt(0);
chart1.Series[1].Points.RemoveAt(0);
}
chart1.Series[0].Points.AddXY(dataLog[5], int.Parse(dataLog[1]));
chart1.Series[1].Points.AddXY(dataLog[5], int.Parse(dataLog[6]));
//}
}
else
{
sr.DiscardBufferedData();
sr.BaseStream.Seek(0, SeekOrigin.Begin);
sr.BaseStream.Position = 0;
//sr.Close();
//alertTB.Text = "";
timer2.Enabled = false;
}
alertTB.ForeColor = Color.Red;
alertTB.Text = "Data Log Viewing In Progress";
}
The issue is I am reading a text file full of variables back through a GUI, like replaying a video. As the code is shown, it works and I can control the timer tick to change the replay speed. The issue is the file is in use, so I can't write to or delete the text while the file is in use, without closing it first. I would like to either be able to find a workaround of the Streamreader, or use the Filestream to Streamreader code that will allow me to edit the file while it is in use. The issue there is, I can't figure out how to make it work with the timer, it just reads the entire file very quickly. Any help or ideas are greatly appreciated.
The issue here is how to have the commented out code to:
read a line of the text file,
have the timer to tick
then read the next line of the text file, and so on. Obviously handling the data as it arrives.
Opening a file while it is in use
I think what you are looking for is FileStream with FileShare.ReadWrite for the instance of your StreamReader (not the instance you have commented out),
var fs = new FileStream("C:\foo.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var sr = new StreamReader(fs);
Setting the position of the stream
It also seems like based on your comments, you are having trouble with positioning the stream, this is how you could do that...
fs.Position = 0; // note this is the FileStream not the StreamReader!
// alternatively, you could use Seek
Difference between sequential and random access
Lastly, you might want to take a look below to see the difference between sequential and random access
A Potential Solution
Here is a class called FileMonitor that will check the file and update the list whenever the file is changed / updated.
I understand that you want a timer to poll the data in the text file, but in case the timer is very fast, I have optimized the FileMonitor to watch the file for changes and only extract when there is a change.
Please note that this only continues to read where it was left off, based on the position of the stream. So, it will not work if lines are deleted or modified prior to getting "extracted". This means it only functions based on your requirements and is not improved to handle a lot of other scenarios, but it should adequately cover your requirements.
public class FileMonitor : IDisposable
{
private readonly FileStream _file;
private readonly StreamReader _reader;
private long _position;
private List<string> _lines;
public FileMonitor(string file)
{
if (String.IsNullOrEmpty(nameof(file))) throw new ArgumentNullException(nameof(file));
_lines = new List<string>();
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = Path.GetDirectoryName(file);
watcher.Filter = Path.GetFileName(file);
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Changed += new FileSystemEventHandler(OnChanged);
//watcher.Created += new FileSystemEventHandler(OnCreated);
//watcher.Deleted += new FileSystemEventHandler(OnDeleted);
//watcher.Renamed += new RenamedEventHandler(OnRenamed);
// begin watching.
watcher.EnableRaisingEvents = true;
// begin reading
_file = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
_reader = new StreamReader(_file);
_lines = ReadLines(_reader).ToList();
_position = _file.Position;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
List<string> update = ReadLines(_reader).ToList();
// fix to remove the immidate newline
if (update.Count() > 0 && String.IsNullOrEmpty(update[0])) update.RemoveAt(0);
_lines.AddRange(update);
_position = _file.Position;
// just for debugging, you should remove this
Console.WriteLine($"File: {e.FullPath} [{e.ChangeType}]");
}
public IEnumerable<string> Lines { get { return _lines; } }
public void Reset()
{
_file.Position = 0;
_position = _file.Position;
_lines.Clear();
}
private static IEnumerable<string> ReadLines(StreamReader reader)
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
public void Dispose()
{
_reader.Dispose();
_file.Dispose();
}
}
Here is how you could use it with your timer
private IEnumerable<string> _lines; // holds all the lines "extracted"
void Main()
{
string file = #"C:\Data\foo.txt";
using (var timer = new System.Timers.Timer())
{
timer.Interval = 2000; // 2 second interval
timer.Elapsed += OnTimedEvent; // attach delegate
timer.Enabled = true; // start the timer
// open the file
using (var monitor = new FileMonitor(file))
{
_lines = monitor.Lines;
// loop forever, remove this
while (true) { }
}
}
}
public void OnTimedEvent(object sender, EventArgs e)
{
// just for debugging, you should remove this
Console.WriteLine($"current count: {_lines.Count()}");
}
If it isn't clear, the data extracted is held in a list of strings. Above, you can grab the "extracted" data from the monitor using the monitor.Line property.
A Proven Working Solution
string line;
if (!File.Exists(logFile))
{
viewLog.Text = "Play";
alertTB.ForeColor = Color.Red;
alertTB.Text = "File Does Not Exist | Log Data To Create File";
chart.Text = "Scope On";
}
if (File.Exists(logFile))
{
var lineCount = File.ReadLines(logFile).Count();//read text file line count to establish length for array
if (lineCount < 2)
{
viewLog.Text = "Play";
alertTB.ForeColor = Color.Red;
alertTB.Text = "File Exists | No Data Has Been Recorded";
chart.Text = "Scope On";
}
if (counter < lineCount && lineCount > 0)//if counter is less than lineCount keep reading lines
{
line = File.ReadAllLines(logFile).Skip(counter).Take(lineCount).First();
string[] dataLog = line.Split(new[] { ',' }, StringSplitOptions.None);
//-----------------------------------------Handling my data
counter++;
}
else
{
counter = 0;
timer2.Enabled = false;
}
}
This is the fix I arrived at, it allows editing the file or deleting the contents of the file. I get the line count before trying to load the file. I then use the counter to iterate through the lines. I can change the delay between the next line read based upon the timer tick interval, pause it, or stop it.

C# Waiting for a file to be closed by an external application

Is there an Event I can capture for when a known file has been closed by an external application?
For example, a user is editing a workbook in Excel and I want to read that file as soon as the user finishes working on it and closes the file.
My current solution is to use a combination of FileSystemWatcher and Timer. The FileSystemWatcher will detect when changes have been made to a file, and start a new thread running a Timer to check when the file has closed (via try-catch) However I don't feel as though this is a good solution. If the user forgot to close the file and heads home for the weekend, it feels wasteful for my Timer to be running the whole time. If I increase the interval on my Timer, then my program won't be as responsive. Is there a solution that doesn't involve polling?
EDIT: updated with code example of what I have
private System.Windows.Forms.Timer processTimer;
private string blockedFile;
// Starts here. File changes were detected.
private void OnFileSystemWatcher_Changed(object source, FileSystemEventArgs e)
{
FileSystemWatcher fsw = (FileSystemWatcher)source;
string fullpath = Path.Combine(fsw.Path, fsw.Filter);
StartFileProcessing(fullpath);
}
private void StartFileProcessing(string filePath)
{
if (isFileOpen(new FileInfo(filePath)))
{
blockedFile = filePath;
processTimer = new System.Windows.Forms.Timer();
processTimer.Interval = 1000; // 1 sec
processTimer.Tick += new EventHandler(processTimer_Elapsed);
processTimer.Enabled = true;
processTimer.Start();
}
else
ProcessFile(filePath);
}
private void ProcessFile(string filePath)
{
// Do stuff, read + writes to the file.
}
// GOAL: Without polling, how can I get rid of this step just know right away when the file has been closed?
private void processTimer_Elapsed(object sender, EventArgs e)
{
if (isFileOpen(new FileInfo(blockedFile)) == false)
{
// The file has been freed up
processTimer.Enabled = false;
processTimer.Stop();
processTimer.Dispose();
ProcessFile(blockedFile);
}
}
// Returns true if the file is opened
public bool isFileOpen(FileInfo file)
{
FileStream str = null;
try
{
str = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException)
{
return true;
}
finally
{
if (str != null)
str.Close();
}
return false;
}

System audio to Skype using Naudio

I've been at this for a while, but made no progress. So this is my last resort!
I am trying to send the system-audio (the audio I hear in my headphones) to Skype (making the persons in my call hear what I hear basically). And I thought I would do this using the Skype4comlib and naudio.
What I've done is to create a class which uses the WasapiLoopbackCapture and WaveFileWriter to write temporary data to a .wav file, and redirect audio using the SkypeSystemAudio.set_InputDevice method. But when I'm talking to somebody and I try to start recording, the person doesn't hear me anymore. I just go completely quiet and no sound is being played to the person.
I thought it would be best if I posted the whole class since it's easier to understand everything.
public class SkypeSystemAudio
{
public NAudio.Wave.WasapiLoopbackCapture capture;
NAudio.CoreAudioApi.MMDevice device;
NAudio.Wave.WaveFileWriter writer;
private Call CurrentCall = null;
private Skype SkypeApplet;
private const int SkypeProtocol = 9;
private bool IsRecording = false;
public string tempfilepath = System.IO.Directory.GetCurrentDirectory() + #"\temp.wav";
#region Public
public void Initialize()
{
device = NAudio.Wave.WasapiLoopbackCapture.GetDefaultLoopbackCaptureDevice();
Init();
}
public void Initialize(NAudio.CoreAudioApi.MMDevice device)
{
this.device = device;
Init();
}
public void StartRecording()
{
capture.StartRecording();
if (CurrentCall != null)
{
CurrentCall.set_OutputDevice(TCallIoDeviceType.callIoDeviceTypeFile, tempfilepath);
IsRecording = true;
}
}
public void StopRecording()
{
capture.StopRecording();
if (CurrentCall != null)
{
CurrentCall.set_OutputDevice(TCallIoDeviceType.callIoDeviceTypeFile, "");
}
}
#endregion
private void Init()
{
capture = new WasapiLoopbackCapture(device);
capture.ShareMode = NAudio.CoreAudioApi.AudioClientShareMode.Shared;
capture.DataAvailable += capture_DataAvailable;
capture.RecordingStopped += capture_RecordingStopped;
WaveFormat format = new WaveFormat(16000, 1); // skype wants 16 Bit samples, 16khz, mono WAV file
//tried using the standard waveformat in the device object too. Didn't work though.
writer = new WaveFileWriter(tempfilepath, format );
SkypeApplet = new Skype();
SkypeApplet.Attach(SkypeProtocol, true);
SkypeApplet.CallStatus += SkypeApplet_CallStatus;
}
void SkypeApplet_CallStatus(Call pCall, TCallStatus Status)
{
if (Status == TCallStatus.clsRinging)
{
CurrentCall = pCall;
pCall.Answer();
}
}
void capture_DataAvailable(object sender, WaveInEventArgs e)
{
if (writer != null)
writer.Write(e.Buffer, 0, e.BytesRecorded);
}
void capture_RecordingStopped(object sender, StoppedEventArgs e)
{
IsRecording = false;
}
}
Does anyone know why this isn't working? I have no clues anymore what to do next.
Any help will greatly appreciated!
I have actually done some something similar, but not using Skype4COM.
What I did was using "Virtual Cables" just like Sebastian L suggested, this way you can control whats going in and out off skype, the downside is that you need to install the virtual cables and configure Skype to use them.
The cables will appear in audio devices as standard input/output.
I have used these cables VAC and VB cable
Hope it helps.

Serial port event handler creates an object which is NUll in main code

I am trying to write a simple serial port data logging app which monitors the serial port and writes the data to a file.
If there is a long gap between the data the the file is closed and a new one started.
I use a timer to determine when to close the file. and the serial event handler to open the file.
The problem is that when the file is closed the file object seems to be null; The file itself is created by another object as it does other stuff deleted from my example code.
For some reason the object created by the serial event handler is valid but the file within the object is null.
I am a very experienced C and linux kernal programmer but new to C# so the syntax and object nature still catches me out
but I cannot see any reason why this should not work unless I am losing an object context somewhere.
That might be a problem as there are four serial port objects open at the same time writing to four different files.
I already solved my timer problem which wouldn't work when called from the serial handler by chaning from a FOrms.timer to Timers.timer.
so I am wondering if its a threading issue.
Any light you can shed would be welcome.
class CommGroup
{
private SerialPort _comPort;
private System.Timers.Timer myTimer;
private ResultsLog logFile = null;
public CommGroup()
{
_comPort = new SerialPort(string name);
_comPort.PortName = name;
_comPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
_comPort.Open();
myTimer = new System.Timers.Timer();
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(TimerEventProcessor);
}
private void TimerEventProcessor(Object myObject,
EventArgs myEventArgs)
{
System.Timers.Timer timer = (System.Timers.Timer) myObject ;
timer.Stop();
if (logFile != null)
{
logFile.Close(); /* this fails due to null object */
}
}
private void DataReceivedHandler(object sender,SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
if (myTimer.Enabled == false)
{
myTimer.Interval = 5000;
myTimer.Start();
}
else
{
myTimer.Stop();
myTimer.Interval = 5000;
myTimer.Start();
}
if (logFile == null)
{
logFile = new ResultsLog("Filename");
}
logFile.LogResult(sp.ReadExisting());
}
}
public class ResultsLog
{
private StreamWriter resultFile;
public ResultsLog(string filename)
{
StreamWriter resultFile = new StreamWriter(filename, true);
}
public void Close(string errorname)
{
if (this.resultFile != null)
{
resultFile.Flush();
resultFile.Close();
}
else
{
MessageBox.Show("File NULL Error " + errorname, "File Close");
}
}
public void LogResult(string result)
{
if (resultFile != null)
{
resultFile.Write(result);
}
}
}
Is your resultFile the troublesome null?
You are re-declaring locally in your ResultsLog constructor. Should be:
public ResultsLog(string filename)
{
resultFile = new StreamWriter(filename, true);
}
Definately could be threading because you have multiple threads accessing the log object without syncronization.
Use lock around your accesses to the varibale and see if it helps.

FileSystemWatcher triggers for filestream open

I have a filesystemwatcher that will trigger an event when a file is modified. I want to read from that file once the lock has been removed. At the moment I am just trying to open the file once the event is triggered, when A large file is being copied the file lock stays on for a while after the events have been sent, preventing the file from being opened for read access.
Any suggestions?
This one's actually a bit of a doozie, unless the problem space has changed significantly since I last had to deal with it.
The easiest way is to simply try to open the file, catch the resulting IOException, and if the file is locked, add it to a queue to be checked later. You can't just try to process every file that comes in because there are all kinds of cases where multiple events will be generated for the same file, so setting up a retry loop on every single received event can turn into a disaster, fast. You need to queue them up instead and check the queue at a regular interval.
Here is a basic class template that should help you out with this problem:
public class FileMonitor : IDisposable
{
private const int PollInterval = 5000;
private FileSystemWatcher watcher;
private HashSet<string> filesToProcess = new HashSet<string>();
private Timer fileTimer; // System.Threading.Timer
public FileMonitor(string path)
{
if (path == null)
throw new ArgumentNullException("path");
watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.NotifyFilter = NotifyFilters.FileName;
watcher.Created += new FileSystemEventHandler(FileCreated);
watcher.EnableRaisingEvents = true;
fileTimer = new Timer(new TimerCallback(ProcessFilesTimer),
null, PollInterval, Timeout.Infinite);
}
public void Dispose()
{
fileTimer.Dispose();
watcher.Dispose();
}
private void FileCreated(object source, FileSystemEventArgs e)
{
lock (filesToProcess)
{
filesToProcess.Add(e.FullPath);
}
}
private void ProcessFile(FileStream fs)
{
// Your code here...
}
private void ProcessFilesTimer(object state)
{
string[] currentFiles;
lock (filesToProcess)
{
currentFiles = filesToProcess.ToArray();
}
foreach (string fileName in currentFiles)
{
TryProcessFile(fileName);
}
fileTimer.Change(PollInterval, Timeout.Infinite);
}
private void TryProcessFile(string fileName)
{
FileStream fs = null;
try
{
FileInfo fi = new FileInfo(fileName);
fs = fi.OpenRead();
}
catch (IOException)
{
// Possibly log this error
return;
}
using (fs)
{
ProcessFile(fs);
}
lock (filesToProcess)
{
filesToProcess.Remove(fileName);
}
}
}
(Note - I'm recalling this from memory here so it might not be perfect - let me know if it's buggy.)

Categories

Resources