Encode MemoryStream directly to MP3 using NAudio - c#

I'm trying to use one of the methods below of the NAudio nuget package:
MediaFoundationEncoder.EncodeToMp3(new RawSourceWaveStream(_audioStream, _audioWriter.WaveFormat), "Test.mp3", 320000);
using (var reader = new WaveFileReader(_audioStream))
{
MediaFoundationEncoder.EncodeToMp3(reader, "Test.mp3", 320000);
}
This should encode the wav stream directly to mp3. Well it does, but the result only consists of noise.
When I save the same MemoryStream(WaveFileWriter) with
File.WriteAllBytes("Test.wav", _audioStream.ToArray());
MediaFoundationEncoder.EncodeToMp3(new WaveFileReader("Test.wav"), "Test.mp3", 320000);
the created wav file is fine and I can use this wav file to encode it to mp3. But I want to avoid saving the wav file first.
Is there a way to encode the stream directly to mp3 without only getting noise? I have no clue why the one method works and the other isn't. Google didn't help me as well. So maybe you guys have any idea.
Thanks for your effort.
Further infos:
.NET Core 3.0 Windows Application
NAudio Nuget Package (Version: 1.9.0)
Complete class:
using NAudio.MediaFoundation;
using NAudio.Wave;
using System;
using System.IO;
namespace Recorder.NAudio
{
public class Recorder
{
private WasapiLoopbackCapture _audioCapture;
private WaveFileWriter _audioWriter;
private MemoryStream _audioStream;
public bool IsRecording { get; private set; }
public Recorder()
{
MediaFoundationApi.Startup();
}
public void Init()
{
_audioCapture = new WasapiLoopbackCapture();
_audioStream = new MemoryStream(1024);
_audioWriter = new WaveFileWriter(_audioStream, new WaveFormat(44100, 24, 2));
_audioCapture.DataAvailable += DataReceived;
_audioCapture.RecordingStopped += RecordingStopped;
}
private void RecordingStopped(object sender, StoppedEventArgs e)
{
_audioCapture.DataAvailable -= DataReceived;
_audioCapture.RecordingStopped -= RecordingStopped;
_audioCapture.Dispose();
_audioCapture = null;
_audioWriter.Flush();
_audioStream.Flush();
_audioStream.Position = 0;
//This works the mp3 file is fine, but I want to avoid the workaround of first saving a wav file to my hard drive
File.WriteAllBytes("Test.wav", _audioStream.ToArray());
MediaFoundationEncoder.EncodeToMp3(new WaveFileReader("Test.wav"), "Test.mp3", 320000);
//Try 1: This doesn't work the mp3 file consists only of noise
MediaFoundationEncoder.EncodeToMp3(new RawSourceWaveStream(_audioStream, _audioWriter.WaveFormat), "Test.mp3", 320000);
//Try 2: This doesn't work the mp3 file consists only of noise
using (var reader = new WaveFileReader(_audioStream))
{
MediaFoundationEncoder.EncodeToMp3(reader, "Test.mp3", 320000);
}
_audioWriter.Close();
_audioWriter.Dispose();
_audioWriter = null;
_audioStream.Close();
_audioStream.Dispose();
_audioStream = null;
GC.Collect();
}
private void DataReceived(object sender, WaveInEventArgs e)
{
_audioWriter.Write(e.Buffer, 0, e.BytesRecorded);
}
public void Start()
{
Init();
_audioCapture.StartRecording();
IsRecording = true;
}
public void Stop()
{
_audioCapture.StopRecording();
IsRecording = false;
}
}
}

I found a solution. When I change constructor of the WaveFileWriter to this:
_audioWriter = new WaveFileWriter(_audioStream, _audioCapture.WaveFormat);
And then change the audio settings of my device in the windows sound settings dialog to:
2 Channels, 24 Bit, 44100Hz instead of 2 Channels, 24 Bit, 96000Hz
it works for some reason...

Related

High Memory Usage playing MP3's with NAudio on Key Press

I'm using C#, WPF, and NAudio.
I play an embedded resource mp3 in the application exe when a key is pressed.
If a key is pressed repeatedly, RAM usage continues to climb past 400MB and never drops.
Using Flush() and Dispose() on the objects doesn't seem to free memory even when GC is called.
This did not used to happen when I played from external resource on the hard drive using string path instead of MemoryStream. It used to stay around 50MB RAM.
public static MemoryStream ms = null;
public static WaveStream wav = null;
public static WaveOutEvent output = null;
// Embedded Resource sound1.mp3
MemoryStream sound1 = new MemoryStream(Properties.Resources.sound1);
// Key Press
//
if (e.Key == Key.Space) {
ms = new MemoryStream(StreamToBytes(sound1));
wav = new Mp3FileReader(ms);
output = new WaveOutEvent();
output.PlaybackStopped += new EventHandler<StoppedEventArgs>(Media_Ended);
output.Init(wav);
output.Play();
}
// MP3 Playback Ended
//
public static void Media_Ended(object sender, EventArgs e)
{
if (output.PlaybackState == PlaybackState.Stopped)
{
ms.Flush();
ms = null;
wav.Close();
output.Dispose();
}
}
// Convert Stream to Byte Array
//
public static byte[] StreamToBytes(MemoryStream stream)
{
...
}
Stream to Byte Array
https://stackoverflow.com/a/1080445/6806643
I convert to Byte Array back to a new Stream or the playback will not layer and will crash if 2 sounds play at once.
It's because you clicking space bar too fast :)
Each key click overwrites variables with new values. So when you click space bar 10 times in few seconds it will create 10 resources. But you keep reference to only last one created. When Media_Ended will start incoming, it will try to dispose only latest created resource.

NAudio WasapiLoopbackCapture intermittent sound

Part 1
I have some NAudio related code
private void InitAudioOut(DateTime dtNow)
{
_pathOut = string.Format(BaseDirectory + #"\({0:HH-mm-ss dd-MM-yyyy} OUT).wav", dtNow);
_waveOut = new WasapiLoopbackCapture();
_waveOut.DataAvailable += WaveOutDataAvailable;
_waveOut.RecordingStopped += WaveOutRecordStopped;
_waveOutFileStream = new WaveFileWriter(_pathOut, _waveOut.WaveFormat);
_waveOut.StartRecording();
}
With this initialization of the sound recording process I have the followind WaveOutDataAvailable method:
private void WaveOutDataAvailable(object sender, WaveInEventArgs e)
{
var buf= e.Buffer;
_waveOutFileStream.Write(buf, 0, buf.Length);
_waveOutFileStream.Flush();
}
The sound in the resulting file is intermittent and slow, like having "blank" sections between the sound chunks, any ideas are appreciated.
End of part 1
Part 2
There is another version of this code where i'm trying to convert the WAV stream to mp3 stream on the fly and then write it to file, it looks like this:
private void InitAudioIn(DateTime dtNow)
{
_pathIn = string.Format(BaseDirectory + #"\({0:HH-mm-ss dd-MM-yyyy} IN).mp3", dtNow);
_waveIn = new WaveInEvent();
_waveIn.WaveFormat = new WaveFormat(44100, 2);
_waveIn.DataAvailable += WaveInDataAvailable;
_waveIn.RecordingStopped += WaveInRecordStopped;
_waveInFileStream = File.Create(_pathIn);
_waveIn.StartRecording();
}
With the WaveInDataAvailable method as follows:
private void WaveInDataAvailable(object sender, WaveInEventArgs e)
{
var wavToMp3Buffer = ConvertWavToMp3(e.Buffer, _waveIn.WaveFormat);
_waveInFileStream.Write(wavToMp3Buffer, 0, wavToMp3Buffer.Length);
_waveInFileStream.Flush();
}
The ConvertWavToMp3 method:
public byte[] ConvertWavToMp3(byte[] wavContent, WaveFormat waveFormat)
{
using (var baseMemoryStream = new MemoryStream())
using (var wavToMp3Writer = new LameMP3FileWriter(baseMemoryStream, waveFormat, 64))
{
wavToMp3Writer.Write(wavContent, 0, wavContent.Length);
wavToMp3Writer.Flush();
return baseMemoryStream.ToArray();
}
}
If i don't try to convert it to MP3 and just write it as a WAV file that it's absolutely fine, but if i try the MP3 conversion through the ConvertWavToMp3 method then the sound gets slow and intermittent, what is wrong with this implementation?
First part, you are making an invalid assumption about the buffer length being the same as the number of valid bytes in the buffer. Try:
private void WaveOutDataAvailable(object sender, WaveInEventArgs e)
{
_waveOutFileStream.Write(e.Buffer, 0, e.BytesRecorded);
}
Let the output stream handle flushing automatically. Trying to force data to disk like that will either not work or in some cases can cause unexpected results like partial block writes that can interfere with your data. Flush at the end of the recording, not during.
As to the second part...
Your code is creating a file that is the concatenation of a series of MP3 files, one for each buffer passed to your WaveInDataAvailable method, and including all the blank space at the end of those buffers. Of course it's not going to play back properly.
If you want to write an MP3 then do it directly. Make your _waveInFileStream an instance of LameMP3FileWriter and let it handle the work itself. Not only is this going to produce a much more useful output but you save yourself a lot of inefficient messing around with setting up and tearing down the encoder for every data block you receive.

Digital Persona U.R.U 4500 Issue with ZKSoftware

I am using digital persona URU 4500 with C# SDK. evrything is fine, Image received function is working properly.
But image capture event is not working with ZKFinger SDK for C#
private void axZKFPEngX1_OnImageReceived(object sender, AxZKFPEngXControl.IZKFPEngXEvents_OnImageReceivedEvent e)
{
}
Image received function is working properly but the below image capture function is not working.
private void axZKFPEngX1_OnCapture(object sender, AxZKFPEngXControl.IZKFPEngXEvents_OnCaptureEvent e)
{
MessageBox.Show("ON Capture");
}
What i further need to do this, i heard i need Biokey.lic file. but after adding that to System32 its not working.
The same code works fine with URU4000 Scanner.
http://eu.zksoftware.com/product.do?id=156
I ended up using digital persona own SDK to enroll and to verify.
Here is the code to save template data to DB if anyone having issues.
switch (Enroller.TemplateStatus)
{
case DPFP.Processing.Enrollment.Status.Ready: // report success and stop capturing
OnTemplate(Enroller.Template);
MemoryStream fingerprintData = new MemoryStream();
Enroller.Template.Serialize(fingerprintData);
fingerprintData.Position = 0;
BinaryReader br = new BinaryReader(fingerprintData);
Byte[] bytes = br.ReadBytes((Int32)fingerprintData.Length);
Enroller.Template.Serialize(ref bytes);
string basestring = Convert.ToBase64String(bytes);
string fingerprint_ = basestring;
Stop();
break;
}

Save and Load image from Isolated Storage (Windows Phone)

I have found a very usefull class on this link: images caching - that help me to make logic for caching images. But in my case I have this:
private void DetailView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SaveAndLoadImage(feedItem);
}
And in this method I save and load image from isolated storage. But I can't load file imidiately because of some permission (Operation not permitted on IsolatedStorageFileStream.). How can I correct my logic to save and load images immediately?
public void SaveAndLoadImage(MediaItemViewModel curItem)
{
string url = string.Empty;
if (!string.IsNullOrEmpty(curItem.ThumbUrl))
{
url = curItem.ThumbUrl;
}
if ((string.IsNullOrEmpty(curItem.ThumbUrl)) && (!string.IsNullOrEmpty(curItem.MediaUrl)))
{
url = curItem.MediaUrl;
}
if ((!string.IsNullOrEmpty(url)) && (CacheImageFile.GetInstance().IsOnStorage(new Uri(url)) == false))
{
CacheImageFile.DownloadFromWeb(new Uri(url));
}
curItem.ImageSource = CacheImageFile.ExtractFromLocalStorage(new Uri(url)) as BitmapImage;
}
have a look at below link
http://www.windowsphonegeek.com/tips/All-about-WP7-Isolated-Storage---Read-and-Save-Images
for loading images from isolated storage. using streams
BitmapImage bi = new BitmapImage();
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile("logo.jpg", FileMode.Open, FileAccess.Read))
{
bi.SetSource(fileStream);
this.img.Height = bi.PixelHeight;
this.img.Width = bi.PixelWidth;
}
}
this.img.Source = bi;
You are getting the image asynchronously from the web, but immediately go to the next line to read the file that hasn't even been written to isolated storage. This is what's causing you the exception.
You could try editing the caching library you found on github, using ManualResetEvent. Notice that you will have to make the method calls on another thread!
For example:
public class CacheImageFileConverter : IValueConverter
{
...
private static ManualResetEvent mre = new ManualResetEvent(true);
private static object DownloadFromWeb(Uri imageFileUri)
{
mre.Reset();
WebClient m_webClient = new WebClient(); //Load from internet
BitmapImage bm = new BitmapImage();
m_webClient.OpenReadCompleted += (o, e) =>
{
if (e.Error != null || e.Cancelled) return;
WriteToIsolatedStorage(IsolatedStorageFile.GetUserStoreForApplication(), e.Result, GetFileNameInIsolatedStorage(imageFileUri));
bm.SetSource(e.Result);
e.Result.Close();
mre.Set();
};
m_webClient.OpenReadAsync(imageFileUri);
return bm;
}
private static object ExtractFromLocalStorage(Uri imageFileUri)
{
mre.WaitOne();
string isolatedStoragePath = GetFileNameInIsolatedStorage(imageFileUri); //Load from local storage
using (var sourceFile = _storage.OpenFile(isolatedStoragePath, FileMode.Open, FileAccess.Read))
{
BitmapImage bm = new BitmapImage();
bm.SetSource(sourceFile);
return bm;
}
}
.... other methods
}
Notice the use of Reset, Set and WaitOne for signaling.
You can use JetImageLoader, I created it for application, where we need to load, cache and show big amount of logos, icons and so on.
It can be used as binding converter, so you should not even change your code! Just update your XAMLs!
Please, check out samples in repository, you'll love it ;)
Features:
Caching on disk
Caching in memory
Fully asynchronous
Available as binding converter or programmatically from your code
Fully open source, fork and improve it!
Here is the example:
<Image Source="{Binding ImageUrl, Converter={StaticResource MyAppJetImageLoaderConverter}}"/>
P.S. I am sorry, that I copying my answer from another questions, but image caching on windows phone is huge problem and I want to share my solution, so everybody can use it and improve for developers community

How To Play a Double array(includes the waves)

I have a double array includes the waves and I want to play it. First I tried this code(for just one wave):
private void DoPlaySound(double p)
{
double[] d=new double[1]{p};
Complex[] c=(DoubleToComplex(d)).ToArray();
FourierTransform.DFT(c,FourierTransform.Direction.Forward);
Stream s = Stream.Null;
StreamWriter w = new StreamWriter(s);
w.Write(c[0].Re);
w.Close();
System.Media.SoundPlayer sndp = new SoundPlayer(s);
sndp.PlayLooping();
}
but System.Media.SoundPlayer.PlayLooping() Needs Wave Header and I haven't any header and I don't know how to generate it.
I also tried it but I don't know how to play wave file using winmm
Try use NAduio

Categories

Resources