Getting Audio Data From MP3 File using NAudio - c#

I want to be able to get audio data from an MP3 file with NAudio, average out the data in the left and right channels to create one dataset and then resample the averaged 44.1KHz audio data to 8Khz but I am having trouble understanding how data is represented in an NAudio Wavestream.
If I had 1 sec worth of MP3 audio, then how many bytes would I have in the WaveStream? By looking at a few code samples it seems one sample is 4 bytes and audio is sampled at 44100Hz and we have 2 different channels, so would that mean we would have (44100 * 4 * 2) bytes in the wavestream, is that right?
Which of the following 3 streams - AStream,PCM and inputStream - should I use to get audio data from? And how to I access left and right channel data separately?
var AStream = new MP3FileReader(myFilePath);
var PCM = new WaveConversionStream.Createpcm(AStream);
var inputStream = new WaveChannel32(new BlockAlignStream(PCM));
I have been thinking of converting the WaveStream using the WaveFormatConversionStream but the code below throws a NAudio.MmException with a message saying "AcmNotPossible calling Acmstreamopen".
var targetFormat = new WaveFormat(8000,1);
var resampled = new WaveFormatConversionStream(targetFormat, inputStream);
The above code doesn't even work if targetFormat is equal to inputStream's format, so I don't know what I am doing wrong here.
//Still throws NAudio.MmException
var resampled = new WaveFormatConversionStream(inputStream.WaveFormat, inputStream);
Other Info: VS2012, WPF, NAudio 1.6.

You seem to have copied a code sample that belongs to a much earlier version of NAudio. The Mp3FileReader class will emit 16 bit samples, and uses the ACM MP3 frame decompressor by default. If you'd prefer your samples directly in floating point, then you can make use of the AudioFileReader.
Resampling 44.1kHz straight down to 8kHz is not a particularly good idea, as you'd end up with a lot of aliasing, so a low pass filter would ideally be applied first. Left and right channels are stored interleaved, so you get a left sample, followed by a right sample, and so on.

Related

Audio stream for multiple outputs (single producer, multi-consumer)

I am attempting to propagate a single sound source to multiple outputs (such as one microphone input to multiple sound cards or channels). The output does not have to be sync'd (a few ms delay is acceptable) but it would be nice if it could be sync'd.
I have successfully written code that loops a microphone input to an output using a WaveIn, a BufferedWaveProvider, and a WaveOut. However when I try to read one BufferedWaveProvider with two instances of WaveOut, the two outputs create this odd 'interleaved' choppy sound. Here is a code snippet for the output portion;
private void CreateWaveOutDevice()
{
waveProvider = new BufferedWaveProvider(waveIn.WaveFormat);
waveOut = new WaveOut();
waveOut.DeviceNumber = 0; //Sound card 1
waveOut.DesiredLatency = 100;
waveOut.Init(waveProvider);
waveOut.PlaybackStopped += wavePlayer_PlaybackStopped;
waveOut2 = new WaveOut();
waveOut2.DeviceNumber = 1; //Sound card 2
waveOut2.DesiredLatency = 100;
waveOut2.Init(waveProvider);
waveOut2.PlaybackStopped += wavePlayer_PlaybackStopped;
waveOut.Play();
waveOut2.Play();
}
I think the reason this is happening is because when the waveProvider circular buffer is read, the data is deleted so the two read methods are 'fighting' over the data which results in the choppy sound.
So I really have two questions;
1.) I see the Naudio library contains many types of waveStreams (RawSourceWaveStream is particularly interesting) However, I have been unable to find a good example of how to read a single stream with multiple waveOut methods. I have also been unable to create working code using waveStream with multiple outputs. Is anyone familiar with waveStreams and knows if this is something that can be done?
2.) If the Naudio wave streams cannot be used in a single producer multiple consumer situation then I believe I would need to make a circular buffer that is not cleared on a read, but only when the buffer is full and new data is pushed in. The code won't care if the data was read or not it just keeps filling the buffer. Would this be the correct approach?
I've spent days searching so hopefully this hasn't already been asked. Thanks for reading.
If you're just reading from a microphone and want two WaveOut's to play it, then the simple option is to create two BufferedWaveProviders, one for each WaveOut, and then when audio is received, send it to both.
Likewise if you were playing from an audio file to two soundcards, the easiest way is to use two reader objects and start them both separately.
There is unfortunately no easy way to synchronize, short of starting and stopping both players at the same time.
There are a few more advanced ways to try to split off an audio stream to two readers, but there can be complications especially if the two readers are not able to read at roughly the same rate.

How to play sound only on the left channel of the headphone and only on the right channel of the headphone in c#?

I have a requirement to play the sound (.wav file) only on the left channel of teh headphone and another file to play only on the right channel of the headphone.
I am new to c #, please help me out to solve this problem.
I don't think that WPF alone can do that, but you might want to check out NAudio.
var input2 = new Mp3FileReader(#"C:\Users\Public\Music\Sample Music\Kalimba.mp3");
var input1 = new Mp3FileReader(#"C:\Users\Public\Music\Sample Music\Maid with the Flaxen Hair.mp3");
MultiplexingWaveProvider waveProvider = new MultiplexingWaveProvider(new IWaveProvider[] { input1, input2 }, 2);
waveProvider.ConnectInputToOutput(1, 1);
WaveOut wave = new WaveOut();
wave.Init(waveProvider);
wave.Play();
I just added the following lines to the above code and it works with 2 channels
waveProvider.ConnectInputToOutput(1, 0);
waveProvider.ConnectInputToOutput(3, 1);
and removed
waveProvider.ConnectInputToOutput(1, 1);
This article on the code project shows a way to visualize audio.
If you can actively visualize audio you will understand that your common stereo wave file contains two channels (A left and a right channel). With that information on how to visualize it you can easily adapt the stream to destroy the left channel (E.G. put zero's in the left stream/right stream). And thus when you would play it the regular way after that it would only play music in the left or the right ear.
The above is the easy part when you have a stereo file which has two equal channels.
There is also the case where your stereo file has different channels (E.G. a guitar on the left ear a drum on the right ear.) In that case you will have to merge two channels to one channel, find duplicate information and finally destroy the other channel you don't want to use.)
The third possibility is your audio file is mono and thus only has one channel. Normally your computer will automatically duplicate the channel from a mono file to a stereo file. So it plays on both ears/speakers. Thus we will never here that the file is mono. However in programming if you would want to make it play on just one ear/channel. You would have to manually convert it to a stereo file (by adding a blank channel). And then again you would have sound on just one ear/speaker.
Hope this helps.

C# NAudio: What is correct data struct?

I have a circuit that sends me microphone data as bytes.I save these bytes into .txt file and reading them using FileStream Circuit's frequency is 16khz.
My problem is when I try to convert those bytes to wav, it gives me a meaningless result. Also my record time is not matched with wav file duration.
WaveFormat waveFormat = new WaveFormat(16000,8,1);
using (WaveFileWriter writer = new WaveFileWriter(tempFile, waveFormat))
{
writer.Write(audioBuffer, 0, audioBuffer.Length);
}
What is the data structure for a wav file? Should I convert them Hex or float or something else?
It's very common to record 16 bit rather than 8 bit, so I'd recommend trying this first:
new WaveFormat(16000,16,1);
Once you get the bit depth right, you should hear recognizable sound, although the pitch will be wrong if you have the wrong sample rate or channel count.

C# NAudio Out of Memory Exception while working with large files

NAudio.Wave.WaveChannel32 wave = new NAudio.Wave.WaveChannel32(new NAudio.Wave.WaveFileReader(open.FileName));
byte[] mainBuffer = new byte[wave.Length];
wave.Read(mainBuffer, 0, mainBuffer.Length);
I want to do some calculations and plot the waveform of a wav file using NAudio and ZedGraph. However when file is too large (greater than 100 megabyte) wave.Read(mainBuffer, 0, mainBuffer.Length);throws out of memory exception. How can i solve this issue? Pls can anyone help me?
I changed my code i will write here as soon as possible.
I would recommend against reading the whole file in one go. Read a few seconds at a time, calculate the peak values for your waveform plot and then move on to the next few seconds.

Getting mp3 file length

I am currently trying to write an Audio Player in C#. I am using BASS library to deal with playing music but now i have small problem with getting length of the song.
Well i have read BASS doc and found a way:
"All" i need to do is
int stream = Bass.BASS_StreamCreateFile(filepath,....);
int length = Bass.BASS_ChannelBytes2Seconds(stream, Bass.BASS_ChannelGetLength(stream));
And in most of cases i get valid length of song. And here the problem starts. As far as i know the stream creation operation is quite expensive (correct me if i am mistaken) and creating a stream only to get length of the song looks a little silly.
So my question is: Is there any other way to get it without creating steam file (not being so expensive). I will be thinking later about reading id3 tags. Is creating that stream "evil that must be done no matter what" and even if i would try to get it with other library it would do exactly the same thing?
You can use the Microsoft.WindowsAPICodePack.Shell:
using Microsoft.WindowsAPICodePack.Shell;
Then code like so:
string file = "myfile.mp3"
ShellFile so = ShellFile.FromFilePath(file);
double 100nanoseconds;
double.TryParse(so.Properties.System.Media.Duration.Value.ToString(), out 100nanoseconds);
There is a code project that could help you as well

Categories

Resources