NAudio : Read Wav File As Double Array - c#

Assuming my WAV file contains 16 bit PCM, How can I read wav file as double array:
using (WaveFileReader reader = new WaveFileReader("myfile.wav"))
{
Assert.AreEqual(16, reader.WaveFormat.BitsPerSample, "Only works with 16 bit audio");
byte[] bytesBuffer = new byte[reader.Length];
int read = reader.Read(bytesBuffer, 0, buffer.Length);
// HOW TO GET AS double ARRAY
}

Just use the ToSampleProvider extension method on your WaveFileReader, and the Read method will take a float[] with the samples converted to floating point. Alternatively use AudioFileReader instead of WaveFileReader and again you can access a version of the Read method that fills a float[]

16-bit PCM is an signed-integer encoding. Presuming you want doubles between 0 and 1, you simply read each sample as an 16-bit signed integer, and then divide by (double)32768.0;
var floatSamples = new double[read/2];
for(int sampleIndex = 0; sampleIndex < read/2; sampleIndex++) {
var intSampleValue = BitConverter.ToInt16(bytesBuffer, sampleIndex*2);
floatSamples[sampleIndex] = intSampleValue/32768.0;
}
Note that stereo channels are interleaved (left sample, right sample, left sample, right sample).
There's some good info on the format here: http://blog.bjornroche.com/2013/05/the-abcs-of-pcm-uncompressed-digital.html

Related

NAudio - Get Wav Peak Data as Int16 Values

I want to use NAudio to extract the peak values of wav audio files like the audiowaveform library provides. Which is an array of short values like:
{
"version": 2,
"channels": 2,
"sample_rate": 48000,
"samples_per_pixel": 512,
"bits": 8,
"length": 3,
"data": [-65,63,-66,64,-40,41,-39,45,-55,43,-55,44]
}
https://github.com/bbc/audiowaveform/blob/master/doc/DataFormat.md
I saw that NAudio has a built-in WaveformRenderer that outputs png images, but I just need the raw peak data. Built-in NAudio classes like MaxPeakProvider (which is initialized with waveReader.ToSampleProvider()) etc works with floats, I need them as short values. Is it possible?
Edit after #Blindy's response
I have this piece of code, converting peak data to shorts, but it produces slightly wrong values when comparing with the output of audiowaveform. What might be the problem? I guess there is a data type conversion or rounding error?
var waveReader = new WaveFileReader(openFileDialog1.FileName);
var wave = new WaveChannel32(waveReader);
var peakProvider = new MaxPeakProvider();
int bytesPerSample = (waveReader.WaveFormat.BitsPerSample / 8);
var samples = waveReader.Length / bytesPerSample;
int pixelsPerPeak = 1;
int spacerPixels = 0;
var samplesPerPixel = 32;
var stepSize = pixelsPerPeak + spacerPixels;
peakProvider.Init(waveReader.ToSampleProvider(), samplesPerPixel * stepSize);
List<short> resultValuesAsShort = new List<short>();
List<float> resultValuesAsFloat= new List<float>();
PeakInfo currentPeak = null;
for (int i = 0; i < samples / samplesPerPixel; i++)
{
currentPeak = peakProvider.GetNextPeak();
resultValuesAsShort.Add((short)(currentPeak.Min * short.MaxValue));
resultValuesAsShort.Add((short)(currentPeak.Max * short.MaxValue));
//resultValuesAsFloat.Add(currentPeak.Min);
//resultValuesAsFloat.Add(currentPeak.Max);
}
Here are the results comparison:
Edit2: Examining further, I noticed that it is generating quite different results for the latter values than audiowaveform library and I don't have any idea for the reason:
MaxPeakProvider [...] works with floats, I need them as short values
NAudio in general works with floats, it's just how it was designed. To get the 2-byte short representation, multiply each float by short.MaxValue (the floats are in the [-1..1] range).

strange encoded mp3 file in xml need help to get it back

i have an XML file which holds alot of data.
For the moment i can read out every data in c# except a mp3 file which is hold as an base64 string in a child.elemt named Data with commentary line: "4 bytes float array converted to base64".
I am very new to c# and before just a beginner in php/java, so be indulgent.
I have attached the base64 string in a text file and the original mp3, maybe it helps.
Can you tell me how i can convert this back ? I already tryd to get single bytes out of the array to a stream and write it back as mp3 file, but atleast its 4 times bigger and absolute not near the same file and just holds in crap.
https://www.file-upload.net/download-12719496/base64string.rar.html
edit:
After the Help of L.B, i got this, thank you.
var mp3base64string = Convert.FromBase64String(child.Element("Data").Value);
using(FileStream file = File.Create(mp3datafilename)) {
using(BinaryWriter writer = new BinaryWriter(file)) {
for (int i = 0; i < mp3base64string.Length; i += 4) {
writer.Write((byte)(967.644334 f * BitConverter.ToSingle(mp3base64string, i)));
}
}
}
This code works and the output is exactly the same as original mp3, but don't ask how I got that magic number :) (Does author of xml think it is some kind of encryption/obfuscation?)
var buf = Convert.FromBase64String(File.ReadAllText(#"base64string.txt"));
int count = 0;
var buf2 = buf.GroupBy(x => count++ / 4)
.Select(g => (byte)(967.644334f * BitConverter.ToSingle(g.ToArray(), 0)))
.ToArray();
File.WriteAllBytes(#"base64string.mp3", buf2);
PS: A non-linq version will be faster....

Byte to value error

So in c#, I have needed a random below given number generator and I found one on StackOverFlow. But near the end, it converts the byte array into a BigInteger. I tried doing the same, though I am using the Deveel-Math lib as it allows me to us BigDeciamals. But I have tried to the array change into a value, and that into a String but I keep getting a "Could not find any recognizable digits." error and as of now I am stumped.
public static BigInteger RandomIntegerBelow1(BigInteger N)
{
byte[] bytes = N.ToByteArray();
BigInteger R;
Random random = new Random();
do
{
random.NextBytes(bytes);
bytes[bytes.Length - 1] &= (byte)0x7F; //force sign bit to positive
R = BigInteger.Parse(BytesToStringConverted(bytes)) ;
//the Param needs a String value, exp: BigInteger.Parse("100")
} while (R >= N);
return R;
}
static string BytesToStringConverted(byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
{
using (var streamReader = new StreamReader(stream))
{
return streamReader.ReadToEnd();
}
}
}
Deveel-Math
Wrong string conversion
You are converting your byte array to a string of characters based on UTF encoding. I'm pretty sure this is not what you want.
If you want to convert a byte array to a string that contains a number expressed in decimal, try this answer using BitConverter.
if (BitConverter.IsLittleEndian)
Array.Reverse(array); //need the bytes in the reverse order
int value = BitConverter.ToInt32(array, 0);
This is way easier
On the other hand, I notice that Deveel-Math's BigInteger has a constructor that takes a byte array as input (see line 226). So you should be able to greatly simplify your code by doing this:
R = new Deveel.Math.BigInteger(1, bytes) ;
However, since Deveel.Math appears to be BigEndian, you may need to reverse the array first:
System.Array.Reverse(bytes);
R = new Deveel.Math.BigInteger(1, bytes);

C#: Converting binary to axb matrix

I am trying to read a binary file (.bin) and convert the resources of the file into a matrix. The code I use to get the file is here.
using (BinaryReader Reader = new BinaryReader(File.Open(string.Format("{0}{1}.bin", DefaultFilePath, "MyBinaryFile"), FileMode.Open)))
{
//the code to convert binary to AxB matrix here.
byteArray = Reader.ReadBytes(100000);
float myFloat = System.BitConverter.ToSingle(byteArray, 0);
}
I need to write a piece of code which can convert the resources of a binary file into a AxB matrix. From the code above, you can see that I convert binary file into Byte[], then to float, but I am stuck in here.
In Matlab, you can read .bin file easily and get the AxB array such as in this link.
How can I proceed?
If the file is just a long list of 32-bit floats, you already got the first value converter correctly. Now you just need to do the rest by adding a loop and incrementing the second argument for ToSingle by 4 each time.
Or, since you're already using a BinaryReader you could just use its ReadSingle method in a loop. If you want a two-dimensional matrix, using a multidimensional array might be a good idea.
// In reality you might want to figure out the array size based on the file size
float[,] floatArray = new float[5000, 32];
using (BinaryReader reader = new BinaryReader(File.Open(string.Format("{0}{1}.bin", DefaultFilePath, "MyBinaryFile"), FileMode.Open)))
{
for (x = 0; x < floatArray.GetLength(0); x++)
{
for (y = 0; y < floatArray.GetLength(1); y++)
floatArray[x, y] = reader.ReadSingle();
}
}
Note: you might need to flip things around depending on whether your data file and desired memory representation is row-major or column-major.
Also remember that multidimensional arrays are contiguous in memory, so if your file is huge, you might run into problems processing it.

How to cast binary resource to short array?

I need to process a binary resource (contents of a binary file). The file contains shorts. I can't figure how to cast the result of accessing that resource to a short array though:
short[] indexTable = Properties.Resources.IndexTable;
doesn't work; I can only use
byte[] indexTable = Properties.Resources.IndexTable;
Using Array.Copy wouldn't work since it would convert each byte from the array returned by accessing the resource to a short.
How do I solve this problem please (other than manually converting the byte array)? Is there a way to tell C# that the resource is of type short[] rather than byte[]?
I had even tried to edit the project's resx file and change the resources' data types to System.UInt16, but then I got the error message that now constructor could be found for them.
Using VS 2010 Express and .NET 4.0.
You should use BinaryReader:
using (var reader = new BinaryReader(new MemoryStream(Properties.Resources.IndexTable)))
{
var firstValue = reader.ReadInt16();
...
}
Buffer.BlockCopy()
byte[] srcTable = Properties.Resources.IndexTable;
short[] destTable = new short[srcTable.Length / 2];
Buffer.BlockCopy(srcTable, 0, destTable, 0, srcTable.Length);
Here's how if you prefer a 'Do it yourself' style (however, I personally prefer #Joel Rondeau's method)
byte[] bytes = Properties.Resources.IndexTable;
short[] shorts = new short[bytes.Length/2];
for( int i = 0; i < bytes.Length; i += 2 )
{
// the order of these depends on your endianess
// big
shorts[i/2] = (short)(( bytes[i] << 8 ) | (bytes[i+1] ));
// little
// shorts[i/2] = (short)(( bytes[i+1] << 8 ) | (bytes[i] ));
}

Categories

Resources