Could read properly the data of WAV file - c#

I am trying to read a wav file as under
class Program
{
struct WavHeader
{
public int riffID;
public int size;
public int wavID;
public int fmtID;
public int fmtSize;
public int format;
public int channels;
public int sampleRate;
public int bytePerSec;
public int blockSize;
public int bit;
public int dataID;
public int dataSize;
}
static void Main(string[] args)
{
WavHeader Header = new WavHeader();
List<short> lDataList = new List<short>();
List<short> rDataList = new List<short>();
using (FileStream fs = new FileStream(#"D:\Test.wav", FileMode.Open, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fs))
{
try
{
Header.riffID = br.ReadInt32();
Header.size = br.ReadInt32();
Header.wavID = br.ReadInt32();
Header.fmtID = br.ReadInt32();
Header.fmtSize = br.ReadInt32();
Header.format = br.ReadUInt16();
Header.channels = br.ReadUInt16();
Header.sampleRate = br.ReadInt32();
Header.bytePerSec = br.ReadInt32();
Header.blockSize = br.ReadInt16();
Header.bit = br.ReadInt16();
if (Header.fmtSize == 18)
{
// Read any extra values
int fmtExtraSize = br.ReadInt16();
br.ReadBytes(fmtExtraSize);
}
Header.dataID = br.ReadInt32();
Header.dataSize = br.ReadInt32();
int bytesForSamp = Header.bit / 8;
int samps = Header.dataSize / bytesForSamp;
for (int i = 0; i < samps; i++)
{
lDataList.Add((short)br.ReadUInt16());
rDataList.Add((short)br.ReadUInt16());
}
}
finally
{
if (br != null)
{
br.Close();
}
if (fs != null)
{
fs.Close();
}
}
}
}
}
But getting runtime error at
lDataList.Add((short)br.ReadUInt16());
rDataList.Add((short)br.ReadUInt16());
{"Unable to read beyond the end of the stream."}
I have seen this SO Q/A and tried to fit as per the requirement but that's returns float.

Here you correctly calculate the bytes per sample and the number of samples:
int bytesForSamp = Header.bit / 8;
int samps = Header.dataSize / bytesForSamp;
But here you assume, that the file has 2 channels and 16-bit samples:
for (int i = 0; i < samps; i++)
{
lDataList.Add((short)br.ReadUInt16());
rDataList.Add((short)br.ReadUInt16());
}
If the file actually has 8-bit samples and/or only 1 channel, then this loop tries to read beyond the input file at some point.
You would either have to assert, that the file is really 16-bit 2ch after reading the header, or handle the file correctly, by reading correctly according to the number of bits per sample and channels specified in the header of the wave-file.

Related

RC4 encrypting data when knowing how to decrypt it

Im currently working with an online API that sends me some users with a encrypted PIN that is encrypted with RC4. its sent in Hex(8) and ive managed to decrypt the hex into the int that it originally was, but i cant for the life of me reverse it to send an encrypted pin(is 4 digits long, pre enc) back. im not the sharpest tool in the shed when it comes to crypt especially not when its between data types so im hoping someone is abit more experianced.
My Classes for RC4:
Decrypt Function:
public string DecryptUserPin(Users user)
{
try
{
byte[] out_ = new byte[8];
crypto crypto = new crypto((long)user.UID);
byte[] bytes = BitConverter.GetBytes(int.Parse(user.PIN, NumberStyles.HexNumber));
crypto.crypto_rc4(ref bytes, ref out_, (long)bytes.Length);
return BitConverter.ToInt64(out_, 0).ToString();
}
catch (Exception ex)
{
Console.WriteLine("ERROR" + ex.ToString());
return (string)null;
}
}
Crypto class:
public class crypto
{
private string key = "Secret words ";
public rc4_key_t ctx;
public crypto(long userId)
{
this.key += userId.ToString();
byte length = (byte)this.key.Length;
byte[] numArray = new byte[256];
for (short index = 0; index < (short)256; ++index)
numArray[(int)index] = (byte)index;
this.ctx.x = (byte)0;
this.ctx.y = (byte)0;
byte index1 = 0;
byte index2 = 0;
for (short index3 = 0; index3 < (short)256; ++index3)
{
index2 = (byte)((int)this.key[(int)index1] + (int)numArray[(int)index3] + (int)index2 & (int)byte.MaxValue);
byte num = numArray[(int)index3];
numArray[(int)index3] = numArray[(int)index2];
numArray[(int)index2] = num;
index1 = (byte)(((uint)index1 + 1U) % (uint)length);
}
this.ctx.state = numArray;
}
public void crypto_rc4(ref byte[] in_, ref byte[] out_, long len)
{
byte index1 = this.ctx.x;
byte index2 = this.ctx.y;
byte[] numArray = new byte[8];
byte[] state = this.ctx.state;
for (short index3 = 0; (long)index3 < len; ++index3)
{
index1 = (byte)((int)index1 + 1 & (int)byte.MaxValue);
index2 = (byte)((int)state[(int)index1] + (int)index2 & (int)byte.MaxValue);
byte num1 = state[(int)index1];
state[(int)index1] = state[(int)index2];
state[(int)index2] = num1;
byte index4 = (byte)((int)state[(int)index1] + (int)state[(int)index2] & (int)byte.MaxValue);
byte num2 = (byte)((uint)in_[(int)index3] ^ (uint)state[(int)index4]);
numArray[(int)index3] = num2;
}
this.ctx.x = index1;
this.ctx.y = index2;
out_ = numArray;
}
}
Support Class:
public struct rc4_key_t
{
public byte[] state;
public byte x;
public byte y;
}

byte[] to string destroying the integers

I created small not finishd Packet Builder class.
AddString() working without problems, but if i use AddInt() the console output looks very weird. Any can tell me why the integer not display correctly?
Main
Packet packet = new Packet();
packet.builder.AddString(Constants.Requests.GET_RESOURCES);
packet.builder.AddString("Another_String");
packet.builder.AddInt(500);
byte[] byteArray = packet.builder.GetByteBuffer();
Console.WriteLine(ByteArrayToString(byteArray));
ByteArray Output: Get_Resources:Another_String:?☺:
47-65-74-5F-52-65-73-6F-75-72-63-65-73-00-3A-41-6E-6F-74-68-65-72-5F-53-74-72-69-6E-67-00-3A-F4-01-00-00-00-3A
As you can see: ?☺ is definitly wrong. The functions are almost the same.
Class
class Packet
{
public Builder builder;
public Packet()
{
builder = new Builder();
}
private static string ByteArrayToString(byte[] arr)
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetString(arr);
}
public static string[] Read(byte[] _recievedData)
{
string data = ByteArrayToString(_recievedData).Trim();
string[] result = data.Split(':');
return result;
}
public class Builder
{
private byte[] buffer;
private int offset;
//Makes very easy on client to filter packets...
private byte[] seperator;
public Builder()
{
offset = 0;
buffer = new byte[4096];
seperator = BitConverter.GetBytes(':');
}
public void AddInt(int intValue)
{
byte[] byteArray = BitConverter.GetBytes(intValue);
for (int x = 0; x < byteArray.Length; x++)
{
buffer[x + offset] = byteArray[x];
}
for (int y = 0; y < seperator.Length; y++)
{
buffer[byteArray.Length + (y + 1) + offset] = seperator[y];
}
offset += (byteArray.Length + seperator.Length);
}
public void AddString(string str)
{
byte[] byteArray = Encoding.ASCII.GetBytes(str);
for (int x = 0; x < byteArray.Length; x++)
{
buffer[x + offset] = byteArray[x];
}
for (int y = 0; y < seperator.Length; y++)
{
buffer[byteArray.Length + (y + 1) + offset] = seperator[y];
}
offset += (byteArray.Length + seperator.Length);
}
public byte[] GetByteBuffer()
{
return buffer;
}
public void Reset()
{
buffer = null;
offset = 0;
}
}
}
Your code is working perfectly fine. Possibly it is not what you want but following code converts an int in 4 bytes because it is a 32-bit integer.
byte[] byteArray = BitConverter.GetBytes(intValue);
at the end of your output, you see those 4 bytes as expected in little endian format F4-01-00-00 because 500 in hexadecimal is 0x01F4. This explains why you are getting, what you are getting.
Now I am assuming that you are expecting 500 instead of ?☺. Following code should fetch you desired result:
byte[] byteArray = BitConverter.GetBytes(intValue.ToString());
This will add a string representation of the number instead of binary representation. Based on the return type of Read function, the need seems to be a string representation.

Zlib Compression differences between zlib.c Adler implemention and Deflate, Ionic Deflate

I have been experiencing some issues with imcompatiabilities between the difference implementations of zlib compression.
As a test case I thought to create test data with 10000 doubles ranging from 0 - 10000.
I created some test code to compress and decompress this data that uses the compress and uncompress in zlib.c
unsigned int Test(char* comparisonFile)
{
unsigned long partsSize = 0x80000;
const int arraySize = 10000;
Bytef doubleArray[sizeof(double) * arraySize];
Bytef outBuffer[sizeof(double) * arraySize];
for (int i = 0; i < arraySize; i++)
{
Bytef doubleBytes[sizeof(double)];
*(double*)(doubleBytes) = (double)i;
for (int x = 0; x < 8; x++)
doubleArray[(8 * i) + x] = doubleBytes[x];
}
compress(outBuffer, &partsSize, doubleArray, sizeof(double) * arraySize);
//create file of compressed data
char * filename = "zlibCompressed";
FILE * file = fopen(filename, "w+b");
int compressResult = et_int64(fwrite((char *)outBuffer, 1, size_t(partsSize), file));
fclose(file);
//load file of compressed data either from zlib or other
if (comparisonFile != NULL)
filename = comparisonFile;
FILE * compressedFile = fopen(filename, "r+b");
if (compressedFile == NULL)
return -1;
unsigned long outBufferSize = sizeof(double) * arraySize;
fseek(compressedFile, 0, SEEK_END);
long fsize = ftell(compressedFile);
fseek(compressedFile, 0, SEEK_SET); /* same as rewind(f); */
partsSize = int(fsize);
double * doubleResult = new double [arraySize];
Bytef* inBuffer = (Bytef*)malloc(sizeof(Bytef)*partsSize);
int readresult = et_int64(fread((char *)inBuffer, 1, partsSize , compressedFile));
if (readresult != partsSize)
return -1;
Bytef * uncompressedOutBuffer = static_cast<Bytef*>((void *)doubleResult);
int result = uncompress(uncompressedOutBuffer, &outBufferSize, inBuffer, partsSize);
for (int i = 0; i < arraySize; i++)
{
// uncompressed data does not match expectation
if ((int)doubleResult[i] != i)
return -2;
}
fclose(compressedFile);
return 0;
}
This allows me to test the internal compression and also substitute compression results from C#.
However, when I use ionic or standard deflate in the following manner I can only recover about 8150 of the expected 10000.
The uncompression returns a:
Z_DATA_ERROR
Given that these seem in theory interoperable, i'm not sure why C# compression results can only be partially unpacked with Adler's zlib? Any help out there?
public void ZlibTest()
{
byte[] buffer;
using (var ms = new MemoryStream())
{
for (int i = 0; i < 10000; i++)
ms.Write(BitConverter.GetBytes((double) i), 0, sizeof(double));
buffer = ms.ToArray();
}
var file = "dummy1";
if (File.Exists(file))
File.Delete(file);
using (Stream fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using (var resultStream = new MemoryStream())
{
using (var compressionStream2 = new Ionic.Zlib.ZlibStream(resultStream, Ionic.Zlib.CompressionMode.Compress, CompressionLevel.Default))
{
compressionStream2.Write(buffer, 0, buffer.Length);
var packetLength = (int)resultStream.Length;
fs.Write(resultStream.ToArray(), 0, packetLength);
}
}
}
file = "dummy2";
using (Stream fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using (var resultStream = new MemoryStream())
{
using (var compressionStream2 = new System.IO.Compression.DeflateStream(resultStream, System.IO.Compression.CompressionMode.Compress))
{
compressionStream2.Write(buffer, 0, buffer.Length);
var packetLength = (int)resultStream.Length;
fs.Write(BitConverter.GetBytes((ushort)40056), 0, sizeof(ushort));
fs.Write(resultStream.ToArray(), 0, packetLength);
}
}
}
}
They're both using the same zlib. There must be an error in your C# code. It is possible that you are not reading the file in binary mode in C#, which would cause occasional corruption.

Reading and writing WAVE file produces unplayable output

I want to add echo to wave file, in order to do so I need to open it and then save it first. I've written some methods but output file is not correct.
Testing input file size: 731 014
Output file sieze: 730 898
But the "space on disc" is the same for both files, which is 733 184
Error code or whatever it is displayed when opened: 0xc00d36c4
What can be the issue here? This looks pretty simple and yet it's not working. Here's my header, read and write methods:
class WaveFile
{
struct WaveHeader
{
public byte[] RiffID;
public uint fileSize;
public byte[] format;
//Wave format chunk 1
public byte[] fmtID;
public uint fmtSize;
public ushort audioFormat;
public ushort channels;
public uint sampleRate;
public uint byteRate;
public ushort blockAlign;
public int bitsPerSample;
//Wave format chunk 2
public byte[] dataID;
public uint dataSize;
}
uint samples;
public List<short> L;
public List<short> R;
WaveHeader header = new WaveHeader();
//loading file, preparation for modyfying
public bool loadWaveFile(string filePath)
{
using (FileStream fs = File.Open(filePath, FileMode.Open))
using (BinaryReader reader = new BinaryReader(fs))
{
// chunk 0
header.RiffID = reader.ReadBytes(4);
header.fileSize = reader.ReadUInt32();
header.format = reader.ReadBytes(4);
// chunk 1
header.fmtID = reader.ReadBytes(4);
header.fmtSize = reader.ReadUInt32();
header.audioFormat = reader.ReadUInt16();
header.channels = reader.ReadUInt16();
header.sampleRate = reader.ReadUInt32();
header.byteRate = reader.ReadUInt32();
header.blockAlign = reader.ReadUInt16();
header.bitsPerSample = reader.ReadInt16();
// chunk 2
header.dataID = reader.ReadBytes(4);
header.dataSize = reader.ReadUInt32();
// DATA is stereo
L = new List<short>();
R = new List<short>();
samples = header.dataSize / header.blockAlign;
for (int i = 0; i < samples; i++)
{
L.Add((short)reader.ReadUInt16());
R.Add((short)reader.ReadUInt16());
}
reader.Close();
fs.Close();
}
return true;
}
public bool addEcho(int threadsNumber, int echoesNumber, int delay, int attenuation)
{
return true;
}
public bool saveWaveFile(string savingPath)
{
using (FileStream fs = new FileStream(#savingPath + "\\echo.wav", FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fs))
{
//chunk 0
writer.Write(header.RiffID);
writer.Write(header.fileSize);
writer.Write(header.format);
//chunk 1
writer.Write(header.fmtID);
writer.Write(header.fmtSize);
writer.Write(header.audioFormat);
writer.Write(header.channels);
writer.Write(header.sampleRate);
writer.Write(header.byteRate);
writer.Write(header.blockAlign);
writer.Write(header.bitsPerSample);
//chunk 2
writer.Write(header.dataID);
writer.Write(header.dataSize);
for (int i = 0; i < samples; i++)
{
writer.Write(L[i]);
writer.Write(R[i]);
}
writer.Close();
fs.Close();
return true;
}
}
}
I didn't find out what the issue was, but for echo purposes this class will work:
Class WaveFile
{
byte[] byteArray;
public void loadWaveFile(string filePath)
{
byteArray = File.ReadAllBytes(filePath);
}
public bool addEcho(int threadsNumber, int echoesNumber, int delay, int attenuation)
{
return true;
}
public bool saveWaveFile(string savingPath)
{
using (FileStream fs = new FileStream(#savingPath + "\\echo.wav", FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fs))
{
writer.Write(byteArray);
writer.Close();
fs.Close();
return true;
}
}
}

How can I get the data of 8bit wav file

I'm making a demo about sound in WindowsForm, I created 3 classes for taking data of wave file. Code is below:
RiffBlock
public class RiffBlock
{
private byte[] riffID;
private uint riffSize;
private byte[] riffFormat;
public byte[] RiffID
{
get { return riffID; }
}
public uint RiffSize
{
get { return (riffSize); }
}
public byte[] RiffFormat
{
get { return riffFormat; }
}
public RiffBlock()
{
riffID = new byte[4];
riffFormat = new byte[4];
}
public void ReadRiff(FileStream inFS)
{
inFS.Read(riffID, 0, 4);
BinaryReader binRead = new BinaryReader(inFS);
riffSize = binRead.ReadUInt32();
inFS.Read(riffFormat, 0, 4);
}
}
FormatBlock
public class FormatBlock
{
private byte[] fmtID;
private uint fmtSize;
private ushort fmtTag;
private ushort fmtChannels;
private uint fmtSamplesPerSec;
private uint fmtAverageBytesPerSec;
private ushort fmtBlockAlign;
private ushort fmtBitsPerSample;
public byte[] FmtID
{
get { return fmtID; }
}
public uint FmtSize
{
get { return fmtSize; }
}
public ushort FmtTag
{
get { return fmtTag; }
}
public ushort Channels
{
get { return fmtChannels; }
}
public uint SamplesPerSec
{
get { return fmtSamplesPerSec; }
}
public uint AverageBytesPerSec
{
get { return fmtAverageBytesPerSec; }
}
public ushort BlockAlign
{
get { return fmtBlockAlign; }
}
public ushort BitsPerSample
{
get { return fmtBitsPerSample; }
}
public FormatBlock()
{
fmtID = new byte[4];
}
public void ReadFmt(FileStream inFS)
{
inFS.Read(fmtID, 0, 4);
BinaryReader binRead = new BinaryReader(inFS);
fmtSize = binRead.ReadUInt32();
fmtTag = binRead.ReadUInt16();
fmtChannels = binRead.ReadUInt16();
fmtSamplesPerSec = binRead.ReadUInt32();
fmtAverageBytesPerSec = binRead.ReadUInt32();
fmtBlockAlign = binRead.ReadUInt16();
fmtBitsPerSample = binRead.ReadUInt16();
// This accounts for the variable format header size
// 12 bytes of Riff Header, 4 bytes for FormatId, 4 bytes for FormatSize & the Actual size of the Format Header
inFS.Seek(fmtSize + 20, System.IO.SeekOrigin.Begin);
}
}
DataBlock
public class DataBlock
{
private byte[] dataID;
private uint dataSize;
private Int16[] data;
private int dataNumSamples;
public byte[] DataID
{
get { return dataID; }
}
public uint DataSize
{
get { return dataSize; }
}
public Int16 this[int pos]
{
get { return data[pos]; }
}
public int NumSamples
{
get { return dataNumSamples; }
}
public DataBlock()
{
dataID = new byte[4];
}
public void ReadData(FileStream inFS)
{
inFS.Read(dataID, 0, 4);
BinaryReader binRead = new BinaryReader(inFS);
dataSize = binRead.ReadUInt32();
data = new Int16[dataSize];
inFS.Seek(40, SeekOrigin.Begin);
dataNumSamples = (int)(dataSize / 2);
for (int i = 0; i < dataNumSamples; i++)
{
data[i] = binRead.ReadInt16();
}
}
}
It works ok with only 16bit wave file, but when I choose a 8 bit wav file or another, the result of this command dataSize = binRead.ReadUInt32();is only 4 although the file size is big.
How I can get the data of 8bit, 24bit... wav file?
Some solutions is appreciated, thank you very much.
Your reading methodology is flawed. The length is correct but for an 8 bits per sample file you should be reading bytes not words; as it stands the data will be incorrect and the value returned by the NumSamples property will be wrong.
In my case, the sub chunk size is 1160, the number of channels is 1 (mono) and the bits per sample is 8 (byte). You will need to decode the bits per sample and adjust your reading accordingly. For the WAV file I used, your program allocated a data array the correct length but 16 bit, divided the data length by 2 and called this the number of samples (wrong, it should be 1160) and then proceeded to read 580 word values from the stream.
Edit: My ancient code will not cut it in the modern age (I seem to recall having to modify it some years ago to cope with at least one additional chunk type but the details escape me).
This is what you get; anything more and your question should read "Could someone write me a program to load WAV files", as it is, we are way beyond the original question and it is time for you to knuckle down and make it work how you need it to :-)
References:
http://www.neurophys.wisc.edu/auditory/riff-format.txt
http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
http://www.shikadi.net/moddingwikiResource_Interchange_File_Format_(RIFF)
http://soundfile.sapp.org/doc/WaveFormat/
All are very useful.
///<summary>
///Reads up to 16 bit WAV files
///</summary>
///<remarks> Things have really changed in the last 15 years</remarks>
public class RiffLoader
{
public enum RiffStatus { Unknown = 0, OK, FileError, FormatError, UnsupportedFormat };
LinkedList<Chunk> chunks = new LinkedList<Chunk>();
RiffStatus status = RiffStatus.Unknown;
List<String> errorMessages = new List<string>();
String source;
public String SourceName { get { return source; } }
public RiffStatus Status { get { return status; } }
public String[] Messages { get { return errorMessages.ToArray(); } }
enum chunkType { Unknown = 0, NoMore, Riff, Fmt, Fact, Data, Error = -1 };
static Int32 scan32bits(byte[] source, int offset = 0)
{
return source[offset] | source[offset + 1] << 8 | source[offset + 2] << 16 | source[offset + 3] << 24;
}
static Int32 scan16bits(byte[] source, int offset = 0)
{
return source[offset] | source[offset + 1] << 8;
}
static Int32 scan8bits(byte[] source, int offset = 0)
{
return source[offset];
}
abstract class Chunk
{
public chunkType Ident = chunkType.Unknown;
public int ByteCount = 0;
}
class RiffChunk : Chunk
{
public RiffChunk(int count)
{
this.Ident = chunkType.Riff;
this.ByteCount = count;
}
}
class FmtChunk : Chunk
{
int formatCode = 0;
int channels = 0;
int samplesPerSec = 0;
int avgBytesPerSec = 0;
int blockAlign = 0;
int bitsPerSample = 0;
int significantBits = 0;
public int Format { get { return formatCode; } }
public int Channels { get { return channels; } }
public int BlockAlign { get { return blockAlign; } }
public int BytesPerSample { get { return bitsPerSample / 8 + ((bitsPerSample % 8) > 0 ? 1 : 0); } }
public int BitsPerSample
{
get
{
if (significantBits > 0)
return significantBits;
return bitsPerSample;
}
}
public FmtChunk(byte[] buffer) : base()
{
int size = buffer.Length;
// if the length is 18 then buffer 16,17 should be 00 00 (I don't bother checking)
if (size != 16 && size != 18 && size != 40)
return;
formatCode = scan16bits(buffer, 0);
channels = scan16bits(buffer, 2);
samplesPerSec = scan32bits(buffer, 4);
avgBytesPerSec = scan32bits(buffer, 8);
blockAlign = scan16bits(buffer, 12);
bitsPerSample = scan16bits(buffer, 14);
if (formatCode == 0xfffe) // EXTENSIBLE
{
if (size != 40)
return;
significantBits = scan16bits(buffer, 18);
// skiping speaker map
formatCode = scan16bits(buffer, 24); // first two bytes of the GUID
// the rest of the GUID is fixed, decode it and check it if you wish
}
this.Ident = chunkType.Fmt;
this.ByteCount = size;
}
}
class DataChunk : Chunk
{
byte[] samples = null;
///<summary>
///Create a data chunk
///<para>
///The supplied buffer must be correctly sized with zero offset and must be purely for this class
///</para>
///<summary>
///<param name="buffer">source array</param>
public DataChunk(byte[] buffer)
{
this.Ident = chunkType.Data;
this.ByteCount = buffer.Length;
samples = buffer;
}
public enum SampleStatus { OK, Duff }
public class Samples
{
public SampleStatus Status = SampleStatus.Duff;
public List<int[]> Channels = new List<int[]>();
#if false // debugger helper method
/*
** Change #if false to #if true to include this
** Break at end of GetSamples on "return retval"
** open immediate window and type retval.DumpLast(16)
** look in output window for dump of last 16 entries
*/
public int DumpLast(int count)
{
for (int i = Channels[0].Length - count; i < Channels[0].Length; i++)
Console.WriteLine(String.Format("{0:X4} {1:X4},{2:X4}", i, Channels[0][i], Channels[1][i]));
return 0;
}
#endif
}
/*
** Return the decoded samples
*/
public Samples GetSamples(FmtChunk format)
{
Samples retval = new Samples();
int samplesPerChannel = this.ByteCount / (format.BytesPerSample * format.Channels);
int mask = 0, sign=0;
int [][] samples = new int [format.Channels][];
for (int c = 0; c < format.Channels; c++)
samples[c] = new int[samplesPerChannel];
if (format.BitsPerSample >= 8 && format.BitsPerSample <= 16) // 24+ is left as an excercise
{
sign = (int)Math.Floor(Math.Pow(2, format.BitsPerSample - 1));
mask = (int)Math.Floor(Math.Pow(2, format.BitsPerSample)) - 1;
int offset = 0, index = 0;
int s = 0;
while (index < samplesPerChannel)
{
for (int c = 0; c < format.Channels; c++)
{
switch (format.BytesPerSample)
{
case 1:
s = scan8bits(this.samples, offset) & mask;
break;
case 2:
s = scan16bits(this.samples, offset) & mask;
break;
}
// sign extend the data to Int32
samples[c][index] = s | ((s & sign) != 0 ? ~mask : 0);
offset += format.BytesPerSample;
}
++index;
}
retval.Channels = new List<int[]>(samples);
retval.Status = SampleStatus.OK;
}
return retval;
}
}
class FactChunk : Chunk
{
int samplesPerChannel;
public int SamplesPerChannel { get { return samplesPerChannel; } }
public FactChunk(byte[] buffer)
{
this.Ident = chunkType.Fact;
this.ByteCount = buffer.Length;
if (buffer.Length >= 4)
samplesPerChannel = scan32bits(buffer);
}
}
class DummyChunk : Chunk
{
public DummyChunk(int size, chunkType type = chunkType.Unknown)
{
this.Ident = type;
this.ByteCount = size;
}
}
public RiffLoader(String fileName)
{
source = fileName;
try
{
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
using (BinaryReader reader = new BinaryReader(fs))
{
Chunk c = getChunk(fs, reader);
if (c.Ident != chunkType.Riff)
{
status = RiffStatus.FileError;
errorMessages.Add(String.Format("Error loading \"{0}\": No valid header"));
}
chunks.AddLast(c);
c = getChunk(fs, reader);
if (c.Ident != chunkType.Fmt)
{
status = RiffStatus.FileError;
errorMessages.Add(String.Format("Error loading \"{0}\": No format chunk"));
}
chunks.AddLast(c);
/*
** From now on we just keep scanning to the end of the file
*/
while (fs.Position < fs.Length)
{
c = getChunk(fs, reader);
switch (c.Ident)
{
case chunkType.Fact:
case chunkType.Data:
chunks.AddLast(c);
break;
case chunkType.Unknown:
break; // skip it - don't care what it is
}
}
FmtChunk format = null;
foreach (var chunk in chunks)
{
switch(chunk.Ident)
{
case chunkType.Fmt:
format = chunk as FmtChunk;
break;
case chunkType.Data:
if (format != null)
{
DataChunk dc = chunk as DataChunk;
var x = dc.GetSamples(format);
}
break;
}
}
}
}
catch (Exception e)
{
status = RiffStatus.FileError;
errorMessages.Add(String.Format("Error loading \"{0}\": {1}", e.Message));
}
}
/*
** Get a chunk of data from the file - knows nothing of the internal format of the chunk.
*/
Chunk getChunk(FileStream stream, BinaryReader reader)
{
byte[] buffer;
int size;
buffer = reader.ReadBytes(8);
if (buffer.Length == 8)
{
String prefix = new String(Encoding.ASCII.GetChars(buffer, 0, 4));
size = scan32bits(buffer, 4);
if (size + stream.Position <= stream.Length) // skip if there isn't enough data
{
if (String.Compare(prefix, "RIFF") == 0)
{
/*
** only "WAVE" type is acceptable
**
** Don't read size bytes or the entire file will end up in the RIFF chunk
*/
if (size >= 4)
{
buffer = reader.ReadBytes(4);
String ident = new String(Encoding.ASCII.GetChars(buffer, 0, 4));
if (String.CompareOrdinal(ident, "WAVE") == 0)
return new RiffChunk(size - 4);
}
}
else if (String.Compare(prefix, "fmt ") == 0)
{
if (size >= 16)
{
buffer = reader.ReadBytes(size);
if (buffer.Length == size)
return new FmtChunk(buffer);
}
}
else if (String.Compare(prefix, "fact") == 0)
{
if (size >= 4)
{
buffer = reader.ReadBytes(4);
if (buffer.Length == size)
return new FactChunk(buffer);
}
}
else if (String.Compare(prefix, "data") == 0)
{
// assume that there has to be data
if (size > 0)
{
buffer = reader.ReadBytes(size);
if ((size & 1) != 0) // odd length?
{
if (stream.Position < stream.Length)
reader.ReadByte();
else
size = -1; // force an error - there should be a pad byte
}
if (buffer.Length == size)
return new DataChunk(buffer);
}
}
else
{
/*
** there are a number of weird and wonderful block types - assume there has to be data
*/
if (size > 0)
{
buffer = reader.ReadBytes(size);
if ((size & 1) != 0) // odd length?
{
if (stream.Position < stream.Length)
reader.ReadByte();
else
size = -1; // force an error - there should be a pad byte
}
if (buffer.Length == size)
{
DummyChunk skip = new DummyChunk(size);
return skip;
}
}
}
}
}
return new DummyChunk(0, chunkType.Error);
}
}
You will need to add properties as required and code to navigate the returned linked list. Particular properties you may need are the sample rates in the format block, assuming you are going to process the data in some way.
Adding 24 to 32 bits is simple, if you need to go beyond 32 bits you will have to switch to int64's.
I have tested it with some good samples from http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Samples.html, as well as your 8 bit file, and it decodes OK and seems to have the correct data.
You should be more than capable of making it work now, good luck.
Edit (again, like the proverbial dog...):
Of course, I should have sign extended the value, so DataChunk.GetSamples() now does that. Looking at the Codeproject files (it isn't the greatest code by the way, but the guy does say that he is only just learning C#, so fair play for tackling a graphical user control) it is obvious that the data is signed. It's a shame that he didn't standardise his source to be an array of Int32's, then it wouldn't matter how many bits the WAV was encoded in.
You could use the Naudio library:
https://naudio.codeplex.com/ (take a look at their website, there are quite a lot of tutorials).
Hope this helps :).

Categories