WasapiLoopbackCapture.DataAvailable reading silence bytes - c#

I am trying to record the speaker sound to a wave file using NAudio's WasapiLoopbackCapture by writing the stream of bytes available. The WasapiLoopbackCapture.DataAvailable BytesRecorded will be 0 is there is no sound. however in my case i am getting bytecount in BytesRecorded even though the speakers are silent. could you please let me know whats wrong here.
class CallResponse
{
private WaveFileWriter _writer;
private WasapiLoopbackCapture _waveIn;
private string _inFile;
private string _inFileCompressed;
private int _duration;
public bool _isRecording;
public bool _speechDetected;
public CallResponse()
{
_inFile = #"C:\Naresh\test.wav";
_inFileCompressed = #"C:\Naresh\test16Hz.wav";
_waveIn = new WasapiLoopbackCapture();
_waveIn.DataAvailable += (s, e) =>
{
Console.WriteLine(e.BytesRecorded);
_writer.Write(e.Buffer, 0, e.BytesRecorded);
if (_writer.Position > _waveIn.WaveFormat.AverageBytesPerSecond * _duration)
{
Console.Write("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bRecording stopped...");
_waveIn.StopRecording();
}
};
_waveIn.RecordingStopped += (s, e) =>
{
if (_writer != null)
{
_writer.Close();
_writer.Dispose();
_writer = null;
}
Console.Write("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bCompressing Audio...");
using (var reader = new AudioFileReader(_inFile))
{
var resampler = new WdlResamplingSampleProvider(reader, 16000);
WaveFileWriter.CreateWaveFile16(_inFileCompressed, resampler);
}
_isRecording = false;
};
}
public void DisposeObjects()
{
if (_waveIn != null)
{
_waveIn.Dispose();
_waveIn = null;
}
}
public void StartRecording(int duration = 5)
{
_writer = new WaveFileWriter(_inFile, _waveIn.WaveFormat);
this._duration = duration;
_speechDetected = false;
_isRecording = true;
Console.WriteLine("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bRecording....");
_waveIn.StartRecording();
}
}

if something is playing audio, then WasapiLoopbackCapture will capture that audio, even if it contains silence. So there's nothing particularly wrong or surprising that you are getting non-zero BytesRecorded values. In fact, if no applications are sending audio to the device being captured, then what typically happens is that you won't get any DataAvailable callbacks at all.

Related

How to fix download issues for some users using WebClient DownloadFileAsync?

I created a Windows Form Application in C# that users of my game modification can use to download updates automatically.
The 'Launcher', as I call it, uses WebClient to download the updates. But the first release of the mod is very big (2,7 GB zipped). The launcher works perfect for me and most users, but for some users the extraction of the zip file logs an error where the file is corrupted and not readable.
I searched already on stack, and it is possible that the file might be corrupted or truncated due to bad internet connection. But how do I build in a method that fix that problem?
//Start downloading file
using (WebClient webClient = new WebClient())
{
webClient.DownloadFileCompleted += new
AsyncCompletedEventHandler(Client_DownloadFileCompleted);
webClient.DownloadProgressChanged += new
DownloadProgressChangedEventHandler(Client_DownloadProgressChanged);
webClient.DownloadFileAsync(new Uri("http://www.dagovaxgames.com/api/downloads/+ patch.path), downloadPath);
}
private void Client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
//install the update
InstallUpdate();
}
private void InstallUpdate()
{
var file = currentPatchPath;
//get the size of the zip file
fileInfo = new FileInfo(file);
_fileSize = fileInfo.Length;
installBackgroundWorker = new BackgroundWorker();
installBackgroundWorker.DoWork += ExtractFile_DoWork;
installBackgroundWorker.ProgressChanged += ExtractFile_ProgressChanged;
installBackgroundWorker.RunWorkerCompleted += ExtractFile_RunWorkerCompleted;
installBackgroundWorker.WorkerReportsProgress = true;
installBackgroundWorker.RunWorkerAsync();
}
EDIT, just showing install code so that you know I am using a backgroundworker to extract the zip.
Common approach here is to download large file in small chunks and put them together on client after completion. Using this approach you can: 1. run few downloads in parallel and 2. in case of problem with network you don't have to download entire file again, just download incomplete chunks.
I faced a similar issue many years ago and created a subclass of WebClient that uses the DownloadProgressChanged event and a timer to abort downloads that get hung up and cancels the download smoother than the underlying internet transport layer does. The code also supports a callback to notify the calling code of progress. I found that sufficient to smoothly handle occasional hiccups downloading 1GB-ish files.
The idea of breaking your download into multiple pieces also has merit. You could leverage a library such as 7-Zip to both create the chunks and piece them back together (many compression libraries have that feature; I'm personally most familiar with 7-Zip).
Here's the code I wrote. Feel free to use and/or modify in any way that's helpful to you.
public class JWebClient : WebClient, IDisposable
{
public int Timeout { get; set; }
public int TimeUntilFirstByte { get; set; }
public int TimeBetweenProgressChanges { get; set; }
public long PreviousBytesReceived { get; private set; }
public long BytesNotNotified { get; private set; }
public string Error { get; private set; }
public bool HasError { get { return Error != null; } }
private bool firstByteReceived = false;
private bool success = true;
private bool cancelDueToError = false;
private EventWaitHandle asyncWait = new ManualResetEvent(false);
private Timer abortTimer = null;
private bool isDisposed = false;
const long ONE_MB = 1024 * 1024;
public delegate void PerMbHandler(long totalMb);
public delegate void TaggedPerMbHandler(string tag, long totalMb);
public event PerMbHandler NotifyMegabyteIncrement;
public event TaggedPerMbHandler NotifyTaggedMegabyteIncrement;
public JWebClient(int timeout = 60000, int timeUntilFirstByte = 30000, int timeBetweenProgressChanges = 15000)
{
this.Timeout = timeout;
this.TimeUntilFirstByte = timeUntilFirstByte;
this.TimeBetweenProgressChanges = timeBetweenProgressChanges;
this.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(MyWebClient_DownloadFileCompleted);
this.DownloadProgressChanged += new DownloadProgressChangedEventHandler(MyWebClient_DownloadProgressChanged);
abortTimer = new Timer(AbortDownload, null, TimeUntilFirstByte, System.Threading.Timeout.Infinite);
}
protected void OnNotifyMegabyteIncrement(long totalMb)
{
NotifyMegabyteIncrement?.Invoke(totalMb);
}
protected void OnNotifyTaggedMegabyteIncrement(string tag, long totalMb)
{
NotifyTaggedMegabyteIncrement?.Invoke(tag, totalMb);
}
void AbortDownload(object state)
{
cancelDueToError = true;
this.CancelAsync();
success = false;
Error = firstByteReceived ? "Download aborted due to >" + TimeBetweenProgressChanges + "ms between progress change updates." : "No data was received in " + TimeUntilFirstByte + "ms";
asyncWait.Set();
}
private object disposeLock = new object();
void MyWebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
if (cancelDueToError || isDisposed) return;
long additionalBytesReceived = e.BytesReceived - PreviousBytesReceived;
PreviousBytesReceived = e.BytesReceived;
BytesNotNotified += additionalBytesReceived;
if (BytesNotNotified > ONE_MB)
{
OnNotifyMegabyteIncrement(e.BytesReceived);
OnNotifyTaggedMegabyteIncrement(Tag, e.BytesReceived);
BytesNotNotified = 0;
}
firstByteReceived = true;
try
{
lock (disposeLock)
{
if (!isDisposed) abortTimer.Change(TimeBetweenProgressChanges, System.Threading.Timeout.Infinite);
}
}
catch (ObjectDisposedException) { } // Some strange timing issue causes this to throw now and then
}
public string Tag { get; private set; }
public bool DownloadFileWithEvents(string url, string outputPath, string tag = null)
{
Tag = tag;
asyncWait.Reset();
Uri uri = new Uri(url);
this.DownloadFileAsync(uri, outputPath);
asyncWait.WaitOne();
return success;
}
void MyWebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error != null) success = false;
if (cancelDueToError || isDisposed) return;
asyncWait.Set();
}
protected override WebRequest GetWebRequest(Uri address)
{
var result = base.GetWebRequest(address);
result.Timeout = this.Timeout;
return result;
}
void IDisposable.Dispose()
{
lock (disposeLock)
{
isDisposed = true;
if (asyncWait != null) asyncWait.Dispose();
if (abortTimer != null) abortTimer.Dispose();
base.Dispose();
}
}
}
I fixed it using a combination of a BackgroundWorker and downloading in small chunks (following up mtkachenko's solution). It also checks if the length of the downloaded file is the same as the one on the server. Using that, it can continue the download where the connection was interupted.
private void DownloadPatch(Patch patch){
//using background worker now!
downloadBackgroundWorker = new BackgroundWorker();
downloadBackgroundWorker.DoWork += (sender, e) => DownloadFile_DoWork(sender, e, patch);
downloadBackgroundWorker.ProgressChanged += DownloadFile_ProgressChanged;
downloadBackgroundWorker.RunWorkerCompleted += DownloadFile_RunWorkerCompleted;
downloadBackgroundWorker.WorkerReportsProgress = true;
downloadBackgroundWorker.RunWorkerAsync();
}
private void DownloadFile_DoWork(object sender, DoWorkEventArgs e, Patch patch)
{
string startupPath = Application.StartupPath;
string downloadPath = Path.Combine(Application.StartupPath, patch.path);
string path = ("http://www.dagovaxgames.com/api/downloads/" + patch.path);
long iFileSize = 0;
int iBufferSize = 1024;
iBufferSize *= 1000;
long iExistLen = 0;
System.IO.FileStream saveFileStream;
// Check if file exists. If true, then check amount of bytes
if (System.IO.File.Exists(downloadPath))
{
System.IO.FileInfo fINfo =
new System.IO.FileInfo(downloadPath);
iExistLen = fINfo.Length;
}
if (iExistLen > 0)
saveFileStream = new System.IO.FileStream(downloadPath,
System.IO.FileMode.Append, System.IO.FileAccess.Write,
System.IO.FileShare.ReadWrite);
else
saveFileStream = new System.IO.FileStream(downloadPath,
System.IO.FileMode.Create, System.IO.FileAccess.Write,
System.IO.FileShare.ReadWrite);
System.Net.HttpWebRequest hwRq;
System.Net.HttpWebResponse hwRes;
hwRq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(path);
hwRq.AddRange((int)iExistLen);
System.IO.Stream smRespStream;
hwRes = (System.Net.HttpWebResponse)hwRq.GetResponse();
smRespStream = hwRes.GetResponseStream();
iFileSize = hwRes.ContentLength;
//using webclient to receive file size
WebClient webClient = new WebClient();
webClient.OpenRead(path);
long totalSizeBytes = Convert.ToInt64(webClient.ResponseHeaders["Content-Length"]);
int iByteSize;
byte[] downBuffer = new byte[iBufferSize];
while ((iByteSize = smRespStream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
if (stopDownloadWorker == true)
{
autoDownloadReset.WaitOne();
}
saveFileStream.Write(downBuffer, 0, iByteSize);
long downloadedBytes = new System.IO.FileInfo(downloadPath).Length;
// Report progress, hint: sender is your worker
int percentage = Convert.ToInt32(100.0 / totalSizeBytes * downloadedBytes);
(sender as BackgroundWorker).ReportProgress(percentage, null);
}
}
As you can see, I report the progress, so that I have a working progress bar as well.
private void DownloadFile_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusTextLabel.Text = "Downloading updates for version " + currentDownloadingPatch + " (" + e.ProgressPercentage + "%)";
progressBar.Value = e.ProgressPercentage;
}

Realtime audio stream from outpud device to input device ( from system mixer to microphone )

I need to stream my OS mixer sound to the microphone. (to transfer them both via skype).
Important thing is -- I need to have ability to configure delay/latency of the stream.
(VAC) Virtual Audio Cable is not solution for me: it doesn't work in my case. I have an external USB sound device an app doesn't work for me. I have tried it. Truly.
I google it more than 2 days and found nothing.
So I want to try wrote my own app for this. Have no experience with NAudio.
At the moment all I have is:
using System;
using System.Collections.Generic;
using NAudio.Wave;
namespace SoundApp
{
public class AudioCable
{
private WaveOut _wvOut = null;
private DirectSoundOut _output = null;
private WaveIn _sourceStreamIn = null;
private WaveOut _sourceStreamOut = null;
public void InitDevices(string fromDevice, string toDevice)
{
Dispose();
_sourceStreamIn = new WaveIn();
_sourceStreamOut = new WaveOut();
_output = new DirectSoundOut();
if (fromDevice.Contains("INPUT"))
{
var tmp = fromDevice.Split('|')[0].Replace("INPUT: ", "").Trim(' ');
int tmpDeviceNumber = int.Parse(tmp);
_sourceStreamIn.DeviceNumber = tmpDeviceNumber;
}
else
{
var tmp = toDevice.Split('|')[0].Replace("OUTPUT: ", "").Trim(' ');
int tmpDeviceNumber = int.Parse(tmp);
_sourceStreamOut.DeviceNumber = tmpDeviceNumber;
}
Guid deviceGuid;
if (toDevice.Contains("INPUT"))
{
var tmp = toDevice.Split('|')[0].Replace("INPUT: ", "").Trim(' ');
int tmpDeviceNumber = int.Parse(tmp);
WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(tmpDeviceNumber);
deviceGuid = deviceInfo.ManufacturerGuid;
}
else
{
var tmp = toDevice.Split('|')[0].Replace("OUTPUT: ", "").Trim(' ');
int tmpDeviceNumber = int.Parse(tmp);
WaveOutCapabilities deviceInfo = WaveOut.GetCapabilities(tmpDeviceNumber);
deviceGuid = deviceInfo.ManufacturerGuid;
}
_output = new DirectSoundOut(deviceGuid);
}
public void PlaySound()
{
WaveInProvider waveIn = null;
if (_sourceStreamIn != null)
{
waveIn = new WaveInProvider(_sourceStreamIn);
}
else if (_sourceStreamOut != null)
{
throw new Exception("Sorry, not supported right now");
//waveIn = new WaveInProvider(_sourceStreamOut);
}
_output.Init(waveIn);
_output.Play();
}
public void Dispose()
{
_sourceStreamIn = null;
_sourceStreamOut = null;
if (_output != null)
{
if (_output.PlaybackState == PlaybackState.Playing)
{
_output.Stop();
}
_output.Dispose();
_output = null;
}
}
public string[] GetAudioDevices()
{
var devices = new List<string>();
for (int i = 0; i < WaveIn.DeviceCount; i++)
{
WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(i);
devices.Add($"INPUT: {i} | {deviceInfo.ProductName}");
}
for (int i = 0; i < WaveOut.DeviceCount; i++)
{
WaveOutCapabilities deviceInfo = WaveOut.GetCapabilities(i);
devices.Add($"OUTPUT: {i} | {deviceInfo.ProductName}");
}
return devices.ToArray();
}
}
}
And the form source:
namespace SoundApp
{
public partial class Form1 : Form
{
AudioCable _ac = new AudioCable();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
cBoxSource.DropDownStyle = ComboBoxStyle.DropDownList;
cBoxTarget.DropDownStyle = ComboBoxStyle.DropDownList;
cBoxSource.Items.AddRange(_ac.GetAudioDevices());
cBoxTarget.Items.AddRange(_ac.GetAudioDevices());
}
private void btnStartStop_Click(object sender, EventArgs e)
{
_ac.InitDevices(cBoxSource.SelectedItem.ToString(), cBoxTarget.SelectedItem.ToString());
_ac.PlaySound();
}
}
}
So at the first I'm trying to stream microphone to the sound output device.... And it does not work at all.
So My questions is:
There are no microphone sound and I'm do not understand why so. Code is valid and dont throw any errors.
Looks like there is no ability to stream outputDevice sound stream to the inputDevice with NAudio. Is there exist some hack for this?

Play Real Time from Byte array

Im sending audio from micro to PC using dma(LAN-TCP):
while (1) {
U32 max;
int r,i;
main_TcpNet ();
if(tcpSend & sendBuffer)
{
if(selectBuffer)
{
send_datalog(ADC_RegularConvertedValueTab2,sizeof(ADC_RegularConvertedValueTab2));
sendBuffer = 0;
}
else
{
send_datalog(ADC_RegularConvertedValueTab,sizeof(ADC_RegularConvertedValueTab));
sendBuffer = 0;
}
main_TcpNet ();
}
}
}
I need to play it in real time .this is what Ive done so far using NAudio:
byte[] recBuff = new byte[1400];
public void OnDataReceived(IAsyncResult asyn)
{
try
{
SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
int iRx = theSockId.thisSocket.EndReceive(asyn);
recBuff [count]= theSockId.dataBuffer[0];
count++;
if (count >= 1400)
{
//--------------------------------------------------------------------
for (int i = 0; i < 1400; i += 2)
recieveSound[i / 2] = recBuff[i] + (recBuff[i + 1] * 256); //turn back to 16bit
//--------------------------------------------------------------------
foreach(int data in recieveSound)
sound.Add(data);
//----------------------------------
if (playStauts)
{
if (firstplay)
{
IWaveProvider provider = new RawSourceWaveStream(
new MemoryStream(recBuff), new WaveFormat());
_waveOut.Init(provider);
_waveOut.Play();
//playThread.Start();
//firstplay = false;
}
}
else
{
player.Stop();
}
count = 0; //RESET THE RecBuff
}
//---------------------------------------------------------------
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
if (m_clientSocket != null)
{
m_clientSocket.Close();
m_clientSocket = null;
}
Close();
}
private void frmMain_Load(object sender, EventArgs e)
{
playThread = new Thread(new ThreadStart(play));
player = new SoundPlayer(filePath);
toolStriplbIP.Text = "Your IP: " + GetIP();
btnDisconnect.Enabled = false;
}
#region Palying Sound
private void btnPlay_Click(object sender, EventArgs e)
{
try
{
//Array.Clear(sound, 0, sound.Count);
buffCount = 0;
offsetSound = 0;
sound.Clear();
Object objData = "7";
byte[] byData = System.Text.Encoding.ASCII.GetBytes(objData.ToString());
if (m_clientSocket != null)
m_clientSocket.Send(byData);
playStauts = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btnPause_Click(object sender, EventArgs e)
{
playStauts = false;
}
#endregion
public void play()
{
while(true){
using (SoundPlayer player = new SoundPlayer(filePath))
{
//????????
}
}
}
I just hear like a bijilion Buzzes in sec. But When I save it and then play it I hear the song very clear and loud.
What is wrong? How can I play my byte array when its growing?
does this even work for me?
byte[] bytes = new byte[1400];
IWaveProvider provider = new RawSourceWaveStream(
new MemoryStream(bytes), new WaveFormat());
_waveOut.Init(provider);
_waveOut.Play();
For one thing, you're using the default WaveFormat, which may or may not be correct. Disagreement between source and destination formats will definitely cause you problems.
Once you're sure the WaveFormat is correct, I would suggest using a BufferedWaveProvider as the input to your wave player rather than the MemoryStream, something like this:
WaveFormat Format = new WaveFormat(/* fill in the right parameters here */);
BufferedWaveProvider Provider = new BufferedWaveProvider(Foramt);
Then, whenever you're happy with your recBuff, you just call Provider.AddSamples to drop the data into the BufferedWaveProvider, which will then be picked up by your WaveOut player.
There's some other strangeness going on. Are you receiving only one byte at a time? It looks like that's what your asynchronous handler is doing. This might not be the best thing, since that will result in lots and lots of context switching. If you're receiving more than one byte at a time, then you're only grabbing the first one and ignoring the rest. That will undoubtedly result in "unexpected" sounds during playback.

(Pause) Stop download without dropping connection

I want to be able to pause a download. I can stop them by dropping the existing connections.
What I'm referring to is almost similar to what's described here: https://superuser.com/questions/170509/whats-the-difference-in-using-pause-stop-in-%C2%B5torrent
My download class:
public class Download
{
public event EventHandler<DownloadStatusChangedEventArgs> DownloadStatusChanged;
public event EventHandler<DownloadProgressChangedEventArgs> DownloadProgressChanged;
public event EventHandler DownloadCompleted;
public bool stop = true; // by default stop is true
public void DownloadFile(string DownloadLink, string Path)
{
stop = false; // always set this bool to false, everytime this method is called
long ExistingLength = 0;
FileStream saveFileStream;
if (File.Exists(Path))
{
FileInfo fileInfo = new FileInfo(Path);
ExistingLength = fileInfo.Length;
}
if (ExistingLength > 0)
saveFileStream = new FileStream(Path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
else
saveFileStream = new FileStream(Path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
var request = (HttpWebRequest)HttpWebRequest.Create(DownloadLink);
request.Proxy = null;
request.AddRange(ExistingLength);
try
{
using (var response = (HttpWebResponse)request.GetResponse())
{
long FileSize = ExistingLength + response.ContentLength; //response.ContentLength gives me the size that is remaining to be downloaded
bool downloadResumable; // need it for sending empty progress
if ((int)response.StatusCode == 206)
{
Console.WriteLine("Resumable");
var downloadStatusArgs = new DownloadStatusChangedEventArgs();
downloadResumable = true;
downloadStatusArgs.ResumeSupported = downloadResumable;
OnDownloadStatusChanged(downloadStatusArgs);
}
else // sometimes a server that supports partial content will lose its ability to send partial content(weird behavior) and thus the download will lose its resumability
{
Console.WriteLine("Resume Not Supported");
ExistingLength = 0;
var downloadStatusArgs = new DownloadStatusChangedEventArgs();
downloadResumable = false;
downloadStatusArgs.ResumeSupported = downloadResumable;
OnDownloadStatusChanged(downloadStatusArgs);
// restart downloading the file from the beginning because it isn't resumable
// if this isn't done, the method downloads the file from the beginning and starts writing it after the previously half downloaded file, thus increasing the filesize and corrupting the downloaded file
saveFileStream.Dispose(); // dispose object to free it for the next operation
File.WriteAllText(Path, string.Empty); // clear the contents of the half downloaded file that can't be resumed
saveFileStream = saveFileStream = new FileStream(Path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite); // reopen it for writing
}
using (var stream = response.GetResponseStream())
{
byte[] downBuffer = new byte[4096];
int byteSize = 0;
long totalReceived = byteSize + ExistingLength;
var sw = new Stopwatch();
sw.Start();
while ((byteSize = stream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
saveFileStream.Write(downBuffer, 0, byteSize);
totalReceived += byteSize;
var args = new DownloadProgressChangedEventArgs();
args.BytesReceived = totalReceived;
args.TotalBytesToReceive = FileSize;
float currentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds;
args.CurrentSpeed = currentSpeed;
if (downloadResumable == true)
{
args.ProgressPercentage = ((float)totalReceived / (float)FileSize) * 100;
long bytesRemainingtoBeReceived = FileSize - totalReceived;
args.TimeLeft = (long)(bytesRemainingtoBeReceived / currentSpeed);
}
else
{
//args.ProgressPercentage = Unknown;
//args.TimeLeft = Unknown;
}
OnDownloadProgressChanged(args);
if (stop == true)
return;
}
sw.Stop();
}
}
var completedArgs = new EventArgs();
OnDownloadCompleted(completedArgs);
saveFileStream.Dispose();
}
catch (WebException e)
{
string filename = System.IO.Path.GetFileName(Path);
Console.WriteLine(e.Message);
saveFileStream.Dispose();
return; //not needed because this is the last line of the method, but let's keep it here
}
}
public void StopDownload()
{
stop = true;
}
protected virtual void OnDownloadStatusChanged(DownloadStatusChangedEventArgs e)
{
EventHandler<DownloadStatusChangedEventArgs> handler = DownloadStatusChanged;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnDownloadProgressChanged(DownloadProgressChangedEventArgs e)
{
EventHandler<DownloadProgressChangedEventArgs> handler = DownloadProgressChanged;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnDownloadCompleted(EventArgs e)
{
EventHandler handler = DownloadCompleted;
if (handler != null)
{
handler(this, e);
}
}
}
public class DownloadStatusChangedEventArgs : EventArgs
{
public bool ResumeSupported { get; set; }
}
public class DownloadProgressChangedEventArgs : EventArgs
{
public long BytesReceived { get; set; }
public long TotalBytesToReceive { get; set; }
public float ProgressPercentage { get; set; }
public float CurrentSpeed { get; set; } // in bytes
public long TimeLeft { get; set; } // in seconds
}
Can anybody help me with this?
Note: I can already resume downloads, that's not what I want, if you were wondering.
In your code (you should copy it into your question, by the way), you have a loop that reads bytes from a stream. You have a flag that will stop the loop.
You do the exact same thing except for pausing instead of stopping. It could be another flag, but you will also need to know when to resume, so you need something for the loop to wait on. One fairly neat way to do this would be with a lock (mutex).
I don't know C# so I can't give you working code, but here is pseudocode for it. With your pointer to the reference docs, it looks like SemaphoreSlim is the simplest thing suitable for this purpose.
bool stop = false
bool paused = false
SemaphoreSlim pauseLock = new SemaphoreSlim(1)
method download():
while (stuff to read):
read from stream
write to file
if stop:
break
// This will do nothing if not paused, or will block if paused
pauseLock.Wait()
pauseLock.Release()
method stop():
stop = true
self.unpause() // stop waiting on lock if needed
method pause()
if not paused:
paused = true
// Note this cannot block for more than a moment
// since the download thread doesn't keep the lock held
pauseLock.Wait()
method unpause()
if paused:
paused = false
pauseLock.Release()
Of course, doing this may cause the server to drop your connection eventually (at which point you need to resume anyway, and you'll only notice this after unpausing).

Unity with Naudio RadioStreaming

I'm working in a script based on the NAudio Demo modified for streaming a Shoutcast inside my Unity game.
I have tried to remove the original while loop using the update from the MonoBehvaiour class, I only get some noises but not music during the time I'm streaming with this script.
I usually get an error while the execution related with the format
MmException: AcmNotPossible calling acmStreamConvert
NAudio.MmException.Try (MmResult result, System.String function)
NAudio.Wave.Compression.AcmStreamHeader.Convert (Int32 bytesToConvert,
System.Int32& sourceBytesConverted)
NAudio.Wave.Compression.AcmStream.Convert (Int32 bytesToConvert,
System.Int32& sourceBytesConverted)
NAudio.Wave.AcmMp3FrameDecompressor.DecompressFrame
(NAudio.Wave.Mp3Frame frame, System.Byte[] dest, Int32 destOffset)
I have tried with different radios online, but I always get that error. I don't know what is happening... Any help?
public class NAudioStreamer : MonoBehaviour {
private IWavePlayer mWaveOutDevice;
private WaveStream mMainOutputStream;
private WaveChannel32 mVolumeStream;
private VolumeWaveProvider16 volumeProvider;
private string m_Url = "http://37.59.32.115:8122/";
enum StreamingPlaybackState
{
Stopped,
Playing,
Buffering,
Paused
}
private volatile StreamingPlaybackState playbackState = StreamingPlaybackState.Stopped;
private bool fullyDownloaded = false;
public bool m_Play = false;
float timer;
void Update()
{
if (m_Play)
{
playbackState = StreamingPlaybackState.Buffering;
StreamMP3(m_Url);
m_Play = false;
}
switch (playbackState)
{
case StreamingPlaybackState.Buffering:
case StreamingPlaybackState.Playing:
StreamMP3(m_Url);
break;
default:
break;
}
}
HttpWebRequest webRequest;
BufferedWaveProvider bufferedWaveProvider = null;
byte[] buffer = new byte[16384 * 4];
private void StreamMP3(string lUrl)
{
this.fullyDownloaded = false;
webRequest = (HttpWebRequest)WebRequest.Create(lUrl);
int metaInt = 0; // blocksize of mp3 data
webRequest.Headers.Clear();
webRequest.Headers.Add("GET", "/ HTTP/1.0");
webRequest.Headers.Add("Icy-MetaData", "1");
webRequest.UserAgent = "WinampMPEG/5.09";
HttpWebResponse resp = null;
try
{
resp = (HttpWebResponse)webRequest.GetResponse();
}
catch(WebException e)
{
if (e.Status != WebExceptionStatus.RequestCanceled)
{
Debug.LogError(e.Message);
}
return;
}
// needs to be big enough to hold a decompressed frame
try
{
// read blocksize to find metadata block
metaInt = Convert.ToInt32(resp.GetResponseHeader("icy-metaint"));
}
catch
{
}
IMp3FrameDecompressor decompressor = null;
try
{
using (var responseStream = resp.GetResponseStream())
{
ReadFullyStream readFullyStream = new ReadFullyStream(responseStream);
//do
{
if (bufferedWaveProvider != null && bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes < bufferedWaveProvider.WaveFormat.AverageBytesPerSecond / 4)
{
Debug.LogError("Buffer getting full, taking a break");
Thread.Sleep(500);
}
else
{
Mp3Frame frame = null;
try
{
frame = Mp3Frame.LoadFromStream(readFullyStream, true);
}
catch (EndOfStreamException)
{
this.fullyDownloaded = true;
Debug.LogError("reached the end of the MP3 file / stream");
// reached the end of the MP3 file / stream
// break;
}
catch (WebException)
{
// probably we have aborted download from the GUI thread
// break;
}
if (decompressor == null && frame != null)
{
// don't think these details matter too much - just help ACM select the right codec
// however, the buffered provider doesn't know what sample rate it is working at
// until we have a frame
WaveFormat waveFormat = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2, frame.FrameLength, frame.BitRate);
decompressor = new AcmMp3FrameDecompressor(waveFormat);
if(bufferedWaveProvider == null)
{
this.bufferedWaveProvider = new BufferedWaveProvider(decompressor.OutputFormat);
this.bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(20); // allow us to get well ahead of ourselves
}
}
int decompressed = decompressor.DecompressFrame(frame, buffer, 0);
if(bufferedWaveProvider != null)
{
bufferedWaveProvider.AddSamples(buffer, 0, decompressed);
}
}
}
if (this.mWaveOutDevice == null && this.bufferedWaveProvider != null)
{
Debug.Log("Creating WaveOut Device");
this.mWaveOutDevice = new WaveOut();
this.volumeProvider = new VolumeWaveProvider16(bufferedWaveProvider);
this.volumeProvider.Volume = 100.0f;
mWaveOutDevice.Init(volumeProvider);
}
else if (bufferedWaveProvider != null)
{
double bufferedSeconds = bufferedWaveProvider.BufferedDuration.TotalSeconds;
if(bufferedSeconds > 0.2f && playbackState == StreamingPlaybackState.Buffering)
{
Debug.Log("PLaying music...");
mWaveOutDevice.Play();
playbackState = StreamingPlaybackState.Playing;
}
}
}
}
finally
{
if (decompressor != null)
{
decompressor.Dispose();
}
}
}
}
The ACM error either means there is no ACM MP3 decoder on the machine, or that possibly a corrupt frame has been received (or some album art misinterpreted as a frame). It it's the latter, you can just catch the error and ignore it. If the former, you'll need to install a decoder, or use a different MP3 frame decompressor. (Possibly the NLayer one).

Categories

Resources