Resampling raw audio with NAudio - c#

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).

Related

Reading a MemoryStream containing opus audio with NAudio

I'm trying to play opus audio files from web which I try to buffer with a MemoryStream. I'm aware of NAudio's ability to take urls however I need to set cookies and user agent before I access the file so this eliminates that option.
My latest approach was to buffer 30~ seconds of stream, feed it to StreamMediaFoundationReader and write to the same MemoryStream when needed, however NAudio ends up playing the initial buffered segment and stop after that is completed. What would be the correct approach for this?
Here's my current code if needed. (I have no idea how audio streaming works so please go easy on me)
bufstr.setReadStream(httpreq.GetResponse().GetResponseStream()); //bufstr is a custom class which creates a memorystream I can write to.
StreamMediaFoundationReader streamread = new StreamMediaFoundationReader(bufstr.getStream());
bufstr.readToStream(); //get the initial 30~ seconds of content
waveOut.Init(streamread);
waveOut.Play();
int seconds = 0;
while(waveOut.PlaybackState == PlaybackState.Playing) {
Thread.Sleep(1000);
seconds++;
if (secs % 30 > 15) bufstr.readToStream();
}
bufstr's readToStream method
public void readToStream() {
int prevbufcount = totalbuffered; //I keep track of how many bytes I fetched from remote url.
while (stream.CanRead && prevbufcount + (30 * (this.bitrate / 8)) > totalbuffered && totalbuffered != contentlength) { //read around 30 seconds of content;
Console.Write($"Caching {prevbufcount + (30 * (this.bitrate / 8))}/{totalbuffered}\r");
byte[] buf = new byte[1024];
bufferedcount = stream.Read(buf, 0, 1024);
totalbuffered += bufferedcount;
memorystream.Write(buf, 0, bufferedcount);
}
}
While debugging I found out that content length I get from server does not match with the actual size of stream, so I ended up calculating content length via other details I get from server.
The issue might also be a race condition due to the fact that it stopped after I manually kept track of where I write on memory stream

how to decode mp3 file in c# using NAudio

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);

Problems with playing AAC Raw data on Silverlight (Windows Phone)

I need to play AAC LC audio that comes from live stream.
To achive that i've implemented MediaStreamSource.
When i receive first packets of stream, i set MediaElement's source to my MediaStreamSource.
It seems that everything works fine: OpenMediaAsync is called -> reported with ReportOpenMediaCompleted, then GetSampleAsync is called -> reported with ReportGetSampleCompleted, BUT, on 10th call of GetSampleAsync, ReportGetSampleCompleted is throws NullReferenceException.
Here is my CodecPrivateData:
var waveFormat = new AACWaveFormat();
waveFormat.FormatTag = 0xFF;
waveFormat.Channels = 2; // For my stream is always stereo
waveFormat.Frequency = 44100; //For my stream is always 44Khz
waveFormat.BitsPerSample = 16; //For my stream is always 16bit
waveFormat.BlockAlign = waveFormat.Channels * waveFormat.BitsPerSample / 8; //is this true formula?
waveFormat.AverageBytesPerSecond = waveFormat.Frequency * waveFormat.BlockAlign; //is this true formula? because usually this value is 176400 or 1411Kbps is this real value for sound?
waveFormat.ExtraDataSize = 2; //usually, but i read these values from first packet of stream
waveFormat.ExtraData = AudioSpecificConfig; //AudioSpecificConfig usually 2 bytes length, readed from stream.
First packet of the stream is always AACSequenceHeader - where i read my CodecPrivateData and AudioSpecificConfig. All the rest is AACRaw.
My CodecPrivateData is looks like
FF00020044AC000010B102000400100002001210.
My GetSampleAsync
protected override void GetSampleAsync(MediaStreamType mediaStreamType)
{
var audioStreamDescription = new MediaStreamDescription(MediaStreamType.Audio, AudioStreamAttibutes); //AudioStreamAttibutes is field that contains data filled on OpenMediaAsync step.
//using (var memoryStream = new MemoryStream(AudioPackets[0].Data))
var memoryStream = new MemoryStream(AudioPackets[0].Data);
ReportGetSampleCompleted(new MediaStreamSample(audioStreamDescription, memoryStream, 0, AudioPackets[0].Data.Length, TimeSpan.FromMilliseconds(GetAudioSampleCalls++ * 32).Ticks, new Dictionary<MediaSampleAttributeKeys, String>())); //throws NullReferenceException, when debugger stops i can be see that all passed params is not null!
}
The problem here is that i don't know any timestamp and i don't know whether this could be a problem.
And finally what is Data field? Data field contains all received RawAAC audio as Byte[] that i extract from AudioTag. (See E.4.2.2 AACAUDIODATA at http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf)

analyse the data chunck from WAVE file with NAUDIO in C#

I would like to draw out a little period of the waveform from a .wav file to the screen. Here is the code i created so far:
NAudio.Wave.WaveFileReader wave = new NAudio.Wave.WaveFileReader(#"C:\test.wav");
long le = wave.Length;
byte[] data = new byte[le];
wave.Read(data, 0, (int)le);
for (int i = 0; i < 100; i++)
{
System.Console.WriteLine(data[i]);
}
System.Console.ReadKey();
I just tried to get the first 100 sample of the datachunk but i dont fully understand the results. Are these numbers the amplitude values of the voice wave?
It is likely that your WAV file is 16 bit (you can check this by looking at the WaveFileReader's WaveFormat property and looking at the BitDepth). In that case, every two bytes represents a single sample. You can use BitConverter.ToInt16 to examine the value of each sample one by one. So for example, you could modify your code to be something like this:
NAudio.Wave.WaveFileReader wave = new NAudio.Wave.WaveFileReader(#"C:\test.wav");
byte[] data = new byte[200];
int read = wave.Read(data, 0, data.Length);
for (int i = 0; i < read; i+=2)
{
System.Console.WriteLine(BitConverter.ToInt16(data,i));
}
System.Console.ReadKey();
They are amplitude, but that 'amplitude' changes 44100 times per second for each channel.
Try this article: http://en.wikipedia.org/wiki/Pulse-code_modulation
If it fails, just remember this. Sound is change in air pressure. Air pressure change is produced by speakers by voltage change. Voltage change is produced by rapidly activating various input levels on digital to analog converters. Input levels (numbers) are what your are getting when you read your PCM data from a file.

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);
}
}
}

Categories