how to decode mp3 file in c# using NAudio - c#

My task is to decode a mp3 file, exclude its header, side information and the optional checksum. I just need the actual data of every frame of the mp3 file.
I have googled a lot but did't find a way !
Can any one tell me a direct way to do that. I am using NAudio to access frames using ReadNextFrame()
Any help will be appreciated.

As mentioned in http://mark-dot-net.blogspot.de/2010/11/merging-mp3-files-with-naudio-in-c-and.html you could change the code to something like that:
public static byte[] GetRawMp3Frames(string filename)
{
using(MemoryStream output = new MemoryStream()) {
Mp3FileReader reader = new Mp3FileReader(filename);
Mp3Frame frame;
while ((frame = reader.ReadNextFrame()) != null)
{
output.Write(frame.RawData, 0, frame.RawData.Length);
}
return output.ToArray();
}
}
Then you can process the frame-only bytes by doing this:
var btAllFrames = GetRawMp3Frames("MyMp3.mp3");

EDIT: Looks like this is a dupe question that was answered better here.
ORIGINAL:
Sounds like you want a full MP3 decoder that outputs the main_data chunks instead of decoding them.
Two options:
Build your own reader (complete with bit reservoir calculations for layer III), or
Remove the audio decode logic from an existing decoder and insert your output logic instead.
There are probably some tricks you can apply that would allow you to short-circuit decoding at least some of the header / side info, but that will take a thorough understanding of the specifications.
If you need a place to start for option #2, try searching for NLayer, JLayer, libmad, or "dist10 source".

Apparently NAudio uses NLayer.
This worked for me
byte[] data = File.ReadAllBytes("path/to/file.mp3");
var memStream = new System.IO.MemoryStream(results);
var mpgFile = new NLayer.MpegFile(memStream);
var samples = new float[mpgFile.Length];
mpgFile.ReadSamples(samples, 0, (int)mpgFile.Length);

Related

Mix audio read from two source files

I want to mix audio read from two files (1 mp3 and 1 wav(recorded voice) file) into one file (mp3 or wav).
I have read many relevant answers but non helped me to get what I wanted.
Like this code below generate a steam as output. I do not know how to call this function properly and how to get the Output stream as an mp3/wav file at the end.
public static void Combine(string[] inputFiles, Stream output)
{
foreach (string file in inputFiles)
{
Mp3FileReader reader = new Mp3FileReader(file);
if ((output.Position == 0) && (reader.Id3v2Tag != null))
{
output.Write(reader.Id3v2Tag.RawData, 0, reader.Id3v2Tag.RawData.Length);
}
Mp3Frame frame;
while ((frame = reader.ReadNextFrame()) != null)
{
output.Write(frame.RawData, 0, frame.RawData.Length);
}
}
}
It looks like you took the code from http://mark-dot-net.blogspot.com/2010/11/merging-mp3-files-with-naudio-in-c-and.html. Check out http://markheath.net/post/mixing-and-looping-with-naudio as well. This talks about looping as well as mixing. Since you're not looping, you don't need that part.
Note that the example there sends the audio to the speaker. However, you can always replace that output with a (e.g.) WaveFileWriter.
Note also that the MixingWaveProvider32 requires that all inputs be in the same WaveFormat. If that's not the case, then you'll need to use a resampler. Luckily, there's http://mark-dot-net.blogspot.com/2014/05/how-to-resample-audio-with-naudio.html to help you out there as well.

Playing ohLibSpotify pcm data stream in C# with NAudio

I'm trying to play raw pcm data delivered from ohLibSpotify c# library (https://github.com/openhome/ohLibSpotify).
I get the data in the following callback:
public void MusicDeliveryCallback(SpotifySession session, AudioFormat format, IntPtr frames, int num_frames)
{
//EXAMPLE DATA
//format.channels = 2, format.samplerate = 44100, format.sample_type = Int16NativeEndian
//frames = ?
//num_frames = 2048
}
Now i want to directly play the received data with NAudio (http://naudio.codeplex.com/). With the following code snippet i can play a mp3 file from disk. Is it possible to directly pass the data received from spotify to NAudio and play it in realtime?
using (var ms = File.OpenRead("test.pcm"))
using (var rdr = new Mp3FileReader(ms))
using (var wavStream = WaveFormatConversionStream.CreatePcmStream(rdr))
using (var baStream = new BlockAlignReductionStream(wavStream))
using (var waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
{
waveOut.Init(baStream);
waveOut.Play();
while (waveOut.PlaybackState == PlaybackState.Playing)
{
Thread.Sleep(100);
}
}
EDIT:
I updated my code. The program doesn't throw any errors, but i also can't hear music. Is anything wrong in my code?
This is the music delivery callback:
public void MusicDeliveryCallback(SpotifySession session, AudioFormat format, IntPtr frames, int num_frames)
{
//format.channels = 2, format.samplerate = 44100, format.sample_type = Int16NativeEndian
//frames = ?
//num_frames = 2048
byte[] frames_copy = new byte[num_frames];
Marshal.Copy(frames, frames_copy, 0, num_frames);
bufferedWaveProvider = new BufferedWaveProvider(new WaveFormat(format.sample_rate, format.channels));
bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(40);
bufferedWaveProvider.AddSamples(frames_copy, 0, num_frames);
bufferedWaveProvider.Read(frames_copy, 0, num_frames);
if (_waveOutDeviceInitialized == false)
{
IWavePlayer waveOutDevice = new WaveOut();
waveOutDevice.Init(bufferedWaveProvider);
waveOutDevice.Play();
_waveOutDeviceInitialized = true;
}
}
And these are the overwritten callbacks in the SessionListener:
public override int MusicDelivery(SpotifySession session, AudioFormat format, IntPtr frames, int num_frames)
{
_sessionManager.MusicDeliveryCallback(session, format, frames, num_frames);
return base.MusicDelivery(session, format, frames, num_frames);
}
public override void GetAudioBufferStats(SpotifySession session, out AudioBufferStats stats)
{
stats.samples = 2048 / 2; //???
stats.stutter = 0; //???
}
I think you can do this:
Create a BufferedWaveProvider.
Pass this to waveOut.Init.
In your MusicDeliveryCallback, use Marshal.Copy to copy from the native buffer into a managed byte array.
Pass this managed byte array to AddSamples on your BufferedWaveProvider.
In your GetAudioBufferStats callback, use bufferedWaveProvider.BufferedBytes / 2 for "samples" and leave "stutters" as 0.
I think that will work. It involves some unnecessary copying and doesn't accurately keep track of stutters, but it's a good starting point. I think it might be a better (more efficient and reliable) solution to implement IWaveProvider and manage the buffering yourself.
I wrote the ohLibSpotify wrapper-library, but I don't work for the same company anymore, so I'm not involved in its development anymore. You might be able to get more help from someone on this forum: http://forum.openhome.org/forumdisplay.php?fid=6 So far as music delivery goes, ohLibSpotify aims to have as little overhead as possible. It doesn't copy the music data at all, it just passes you the same native pointer that the libspotify library itself provided, so that you can copy it yourself to its final destination and avoid an unnecessary layer of copying. It does make it a bit clunky for simple usage, though.
Good luck!
First, your code snippet shown above is more complicated than it needs to be. You only need five, instead of two using statments. Mp3FileReader decodes to PCM for you. Second, use WaveOutEvent in preference to WaveOut with function callbacks. It is much more reliable.
using (var rdr = new Mp3FileReader("test.pcm"))
using (var waveOut = new WaveOutEvent())
{
//...
}
To answer you actual question, you need to use a BufferedWaveProvider. You create one of these and pass it to your output device in the Init method. Now, as you receive audio, decompress it to PCM (if it is compressed) and put it into the BufferedWaveProvider. The NAudioDemo application includes examples of how to do this, so look at the NAudio source code to see how its done.

Resampling raw audio with NAudio

I'd like to resample audio: change its sample rate from 44k to 11k. The input I've got is raw audio in bytes. It really is raw, it has no headers - if I try loading it into a WaveFileReader, I get an exception saying "Not a WAVE file - no RIFF header".
How I'm currently trying to achieve it is something like this (just a really simplified piece of code):
WaveFormat ResampleInputFormat = new WaveFormat(44100, 1);
WaveFormat ResampleOutputFormat = new WaveFormat(11025, 1);
MemoryStream ResampleInputMemoryStream = new MemoryStream();
foreach (var b in InputListOfBytes)
{
ResampleInputMemoryStream.Write(new byte[]{b}, 0, 1);
}
RawSourceWaveStream ResampleInputWaveStream =
new RawSourceWaveStream(ResampleInputMemoryStream, ResampleInputFormat);
WaveFormatConversionStream ResampleOutputStream =
new WaveFormatConversionStream(ResampleOutputFormat, ResampleInputWaveStream);
byte[] bytes = new byte[2];
while (ResampleOutputStream.Read(bytes, 0, 2) > 0)
{
OutputListOfBytes.Add(bytes[0]);
OutputListOfBytes.Add(bytes[1]);
}
My problem is: the last loop is an infinite loop. The Read() always gets the same values and never advances in the Stream. I even tried Seek()-ing to the right position after each Read(), but that doesn't seem to work either, I still always get the same values.
What am I doing wrong? And is this even the right way to resample raw audio? Thanks in advance!
First, you need to reset ResampleInputMemoryStream's position to the start. It may actually have been easier to create the memory stream based on the array:
new MemoryStream(InputListOfBytes)
Second, when reading out of the resampler, you need to read in larger chunks than two bytes at a time. Try at least a second's worth of audio (use ResampleOutputStream.WaveFormat.AverageBytesPerSecond).

Use NAudio to get Ulaw samples for RTP

I've been looking over the NAudio examples trying to work out how I can get ulaw samples suitable for packaging up as an RTP payload. I'm attempting to generate the samples from an mp3 file using the code below. Not surprisingly, since I don't really have a clue what I'm doing with NAudio, when I transmit the samples across the network to a softphone all I get is static.
Can anyone provide any direction on how I should be getting 160 bytes (8Khz # 20ms) ULAW samples from an MP3 file using NAudio?
private void GetAudioSamples()
{
var pcmStream = WaveFormatConversionStream.CreatePcmStream(new Mp3FileReader("whitelight.mp3"));
byte[] buffer = new byte[2];
byte[] sampleBuffer = new byte[160];
int sampleIndex = 0;
int bytesRead = pcmStream.Read(buffer, 0, 2);
while (bytesRead > 0)
{
var ulawByte = MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(buffer, 0));
sampleBuffer[sampleIndex++] = ulawByte;
if (sampleIndex == 160)
{
m_rtpChannel.AddSample(sampleBuffer);
sampleBuffer = new byte[160];
sampleIndex = 0;
}
bytesRead = pcmStream.Read(buffer, 0, 2);
}
logger.Debug("Finished adding audio samples.");
}
Here's a few pointers. First of all, as long as you are using NAudio 1.5, no need for the additional WaveFormatConversionStream - Mp3FileReader's Read method returns PCM.
However, you will not be getting 8kHz out, so you need to resample it first. WaveFormatConversionStream can do this, although it uses the built-in Windows ACM sample rate conversion, which doesn't seem to filter the incoming audio well, so there could be aliasing artefacts.
Also, you usually read bigger blocks than just two bytes at a time as the MP3 decoder needs to decode frames one at a time (the resampler also will want to deal with bigger block sizes). I would try reading at least 20ms worth of bytes at a time.
Your use of BitConverter.ToInt16 is correct for getting the 16 bit sample value, but bear in mind that an MP3 is likely stereo, with left, right samples. Are you sure your phone expects stereo.
Finally, I recommend making a mu-law WAV file as a first step, using WaveFileWriter. Then you can easily listen to it in Windows Media Player and check that what you are sending to your softphone is what you intended.
Below is the way I eventually got it working. I do lose one of the channels from the mp3, and I guess there's some way to combine the channels as part of a conversion, but that doesn't matter for my situation.
The 160 byte buffer size gives me 20ms ulaw samples which work perfectly with the SIP softphone I'm testing with.
var pcmFormat = new WaveFormat(8000, 16, 1);
var ulawFormat = WaveFormat.CreateMuLawFormat(8000, 1);
using (WaveFormatConversionStream pcmStm = new WaveFormatConversionStream(pcmFormat, new Mp3FileReader("whitelight.mp3")))
{
using (WaveFormatConversionStream ulawStm = new WaveFormatConversionStream(ulawFormat, pcmStm))
{
byte[] buffer = new byte[160];
int bytesRead = ulawStm.Read(buffer, 0, 160);
while (bytesRead > 0)
{
byte[] sample = new byte[bytesRead];
Array.Copy(buffer, sample, bytesRead);
m_rtpChannel.AddSample(sample);
bytesRead = ulawStm.Read(buffer, 0, 160);
}
}
}

How do I parse the gzip file header from a gzip stream in C#?

I am forced to use an older version of the SharpZipLib and the standard Microsoft libraries to perform this. I have a gziped file whose name is different than the filename inside the archive. I need to parse the gzip file header to return the original filename. Here is documentation on the gzip website:
http://www.gzip.org/zlib/rfc-gzip.html#conventions
And a java example that looks like it might be doing what I want. it looks like it checks for the file header, but doesn't actually read the file name.
(Sorry couldn't post more than 1 hyperlink)
(http://www).java2s.com/Open-Source/Java-Document/6.0-JDK-Modules/j2me/java/util/zip/GZIPInputStream.java.htm
Any help on this problem would be much appreciated. Thanks!
Well if finally figured it out. Its not the safest way or best but i needed a quick and dirty way to do it and this works. So if anyone else needs to know this or want to improve on it, here you go.
using (FileStream stream = File.OpenRead(filePath))
{
int size = 2048;
byte[] data = new byte[2048];
size = stream.Read(data,0,size);
if (data[3] == 8)
{
List<byte> byteList = new List<byte>();
int i = 10;
while (data[i] != 0)
{
byteList.Add(data[i]);
i++;
}
string test = System.Text.ASCIIEncoding.ASCII.GetString(byteList.ToArray());
Console.WriteLine(test);
}
}

Categories