C#: storing 16 bit data - c#

I have written the following code in C# to get 16-bit twos compliment values over uart from 8 bit microcontroller. I am receiving data in bytes form. So I am combining the two bytes to form a 16-bit value. My issue is that my all values are becoming negative, but in reality some are negative some are not. Please tell me what is wrong of the following code that I am doing:
int t = 0;
int bytes = serialPort1.BytesToRead;
byte[] buffer = new byte[bytes];
serialPort1.Read(buffer, 0, bytes);
float [] buffer2 = new float[bytes];
for(int i=0;t< buffer.Length;i++)
{
buffer2[i]= ~(((buffer[t]<< 8) | buffer[t+1]) - 1);
t = t + 2;
}

Related

How to convert a number to unsigned short (always 16 bit, big endian byte order) in C#

I'm looking for C# method that's equivalent to PHP pack()
I've found lot of articles about this on Google, but the result is always different than using my PHP code when I've tried some code.
I have no idea.
Here's my PHP code which I'd like to transfer in C# code.
$binaryMagic = pack("n", 0xbabe);
This code should give you the same result:
string hex = "babe";
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < hex.Length; i += 2) {
bytes[i/2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
string converted = System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
Console.WriteLine(converted);

Save audio stream float frames as WAV with C#

I am testing an application in C# that receives a live audio stream and then saves it to a WAV file. The audio stream has these characteristics: frequency or sampling rate: 16000, channels: 1, frame Samples Per Channel: 320, play Delay in Ms: 200. The audio frames come as floats, and I am collecting the float frames and storing them into a Memorystream with Binarywriter. After that, I convert the content of the Memorystream into an array, and that array then is converted to a Float array again. With the float array, I start the process to assemble the WAV file.
I have compared the float frames values received with the ones inside the float array that I am using to build the WAV file and are the same. I am having trouble processing the float array to assemble the WAV file. I am not sure if I am doing the data conversion wrong with the ConvertAndWrite() method, or if the WAV header is not well formatted according to the characteristics of the audio stream.
I can see the WAV file being created, but there is no content inside apart from the header I think. Any guidance will be much appreciated. I put together this sample code for you to test what I am doing:
using System;
using System.IO;
using System.Text;
class SaveAudioStreamToWav
{
//Sample as received from stream. Here as a double to avoid altering the sample adding F to each value.
public double[] receivedStreamSample = { 0, -0.003509521, -0.003356934, 0.0002746582, -0.004516602, -0.0027771, -0.0003967285, -0.001739502, 0.004150391, 0.0008544922, 0.002593994, 0.00970459, 0.003631592, 0.001800537, 0.004760742, 0.004272461, -0.002655029, -0.001495361, -0.006835938, -0.004211426, -0.0008239746, 0.001525879, 0.006347656, 0.002532959, -0.002471924, -0.001342773, 0.001159668, 0.0006713867, -0.000793457, 0.001403809, -0.0006713867, -0.0006713867, -0.0007629395, 0.0009460449, -0.003662109, 0.00390625, -0.001312256, -0.001678467, 0.002288818, -0.001831055, -0.00579834, 0.001220703, -0.005096436, -0.003631592, -0.007019043, -0.0001220703, -0.0008850098, -0.0001220703, -0.005371094, 0.004608154, 0.004425049, 0.0027771, 0.005279541, 0.0001525879, 0.0009765625, 0.004150391, -0.002807617, 0.001678467, -0.004577637, -0.002685547, -0.004364014, -0.0008544922, 0.001281738, -0.0009155273, -0.008148193, -0.001983643, 9.155273E-05, 0.0008239746, 0.0004272461, 0.002807617, -0.00289917, 0.002075195, 0.008392334, 0.003479004, 0.005615234, 0.0009460449, 0.002471924, 0.0004272461, -0.006164551, 0.0003967285, -0.0007629395, -0.007476807, -0.002532959, 0.01495361, 0.01382446, 0.002288818, -0.009063721, -0.1129761, -0.05401611, 0.03497314, -0.03027344, 0.08999634, 0.01831055, 0.01037598, 0.03302002, 0.02667236, 0.04309082, -0.01806641, -0.0440979, 0.07125854, 0.00680542, -0.01242065, 0.001983643, -0.03710938, 0.009552002, 0.01013184, 0.002258301, 0.007446289, 0.004486084, -0.009063721, -0.007293701, 0.008239746, -0.0003967285, 0.001556396, 0.001586914, 0.002258301, 0.001281738, 0.001617432, -0.001831055, 0.001556396, -0.001525879, -0.002410889, 0.004516602, 0.000793457, -0.001403809, -0.004882813, -0.0005187988, -0.003540039, -0.004302979, 0.0004272461, 0.004974365, -0.002868652, -0.003875732, -0.0001220703, 0.001617432, 0.002258301, -0.005889893, -0.001068115, 0.003295898, 0.002410889, -0.00201416, 0.001068115, 0.003143311, -0.001464844, 0.000579834, 0.005310059, 0.001434326, 0.001403809, 0.001312256, -0.001617432, 0.0009460449, -0.0009765625, -0.0007324219, -0.001617432, -0.004730225, 0.001373291, -0.001586914, 0.0005187988, 0.001556396, -0.001647949, 0.0008544922, 0.001739502, 0.0027771, 0.001831055, 3.051758E-05, -0.04672241, 0.02276611, 0.02529907, -0.005249023, -0.02285767, -0.0378418, -0.1454468, 0.04385376, -0.04058838, -0.005249023, -3.051758E-05, -0.02166748, -0.006378174, -0.002380371, -0.0368042, 0.04330444, -0.008453369, 0.0300293, -0.01651001, -0.005554199, -0.01828003, 0.008972168, -0.01571655, -0.01202393, 0.01141357, -0.003997803, 0.004119873, -0.002532959, 0.004333496, -0.001495361, -0.001281738, -0.003692627, -0.001647949, -0.001861572, 0.000793457, -0.0003662109, -0.002532959, -0.001342773, 0.0003051758, 0.002075195, 0.002349854, 0.001464844, 0.001678467, -0.0008850098, -0.0001525879, 0.003723145, -0.0009155273, 0.002807617, -0.005157471, -0.001617432, 0.002471924, 0.002166748, -0.0003356934, 0.000213623, -0.000793457, -0.0008544922, -0.00100708, 0.000213623, 0.001037598, -0.003448486, 0.0009460449, -0.0006103516, -0.002655029, -0.009735107, -0.01101685, 0.01937866, 0.00994873, -0.02600098, 0.04592896, 0.1063843, 0.002441406, -0.0100708, 0.002990723, -0.01235962, -0.003448486, 0.01089478, -0.01480103, -0.02902222, 0.02990723, -0.01376343, 0.01275635, -0.008666992, 0.006469727, -0.009857178, 0.002655029, -0.0004882813, 0.003814697, 0.004943848, -0.002990723, -0.0003051758, -0.001678467, 0.003265381, 0.0009460449, -9.155273E-05, -0.001403809, 0.001739502, -0.002685547, -0.0009460449, -0.001281738, 0.0009765625, 0.001312256, 0.002288818, -0.0002746582, -0.001098633, -0.002319336, -0.000793457, 0.001464844, 0.001281738, -0.002319336, 6.103516E-05, 0.0003967285, -0.002532959, 0.0002441406, 0.001861572, 0.0009765625 };
public float[] floatsArray;
public FileStream fileStream;
static void Main(string[] args)
{
var saveAudioStreamToWav = new SaveAudioStreamToWav();
saveAudioStreamToWav.ConvertDoubleToFloat();
saveAudioStreamToWav.CreateEmpty(saveAudioStreamToWav.SetNameAndPath());
saveAudioStreamToWav.ConvertAndWrite();
saveAudioStreamToWav.WriteHeader();
}
public void ConvertDoubleToFloat()
{
floatsArray = new float[receivedStreamSample.Length];
floatsArray = Array.ConvertAll(receivedStreamSample, x => (float)x);
}
public string SetNameAndPath()
{
//Setting the name of the file
string timeStamp = DateTime.Now.ToString("yyyyMMddHHmmssfff");
string filename = "/TestSavingStreamToWav_" + timeStamp + ".wav";
string path = Directory.GetCurrentDirectory();
string filepath = path + filename;
Console.WriteLine(filepath);
return filepath;
}
public void CreateEmpty(string filepath)
{
const int HEADER_SIZE = 44;
fileStream = new FileStream(filepath, FileMode.CreateNew, FileAccess.ReadWrite);
byte emptyByte = new byte();
for (int i = 0; i < HEADER_SIZE; i++) //preparing an empty space for the header
{
fileStream.WriteByte(emptyByte);
}
}
public void ConvertAndWrite()
{
Int16[] intData = new Int16[floatsArray.Length];
Byte[] bytesData = new Byte[floatsArray.Length * 2]; // bytesData array is twice the size of floatsArray array because a float converted in Int16 is 2 bytes.
const float rescaleFactor = 32767; //to convert float to Int16
for (var i = 0; i < floatsArray.Length; i++)
{
intData[i] = (short)(floatsArray[i] * rescaleFactor);
var byteArr = new Byte[2];
byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData, i * 2);
}
fileStream.Write(bytesData, 0, bytesData.Length);
}
public void WriteHeader()
{
int hz = 16000; //frequency or sampling rate
int headerSize = 44; //default for uncompressed wav
fileStream.Seek(0, SeekOrigin.Begin);
Byte[] riff = System.Text.Encoding.UTF8.GetBytes("RIFF"); //RIFF marker. Marks the file as a riff file. Characters are each 1 byte long.
fileStream.Write(riff, 0, 4);
Byte[] chunkSize = BitConverter.GetBytes(fileStream.Length - 8); //file-size (equals file-size - 8). Size of the overall file - 8 bytes, in bytes (32-bit integer). Typically, you'd fill this in after creation.
fileStream.Write(chunkSize, 0, 4);
Byte[] wave = System.Text.Encoding.UTF8.GetBytes("WAVE"); //File Type Header. For our purposes, it always equals "WAVE".
fileStream.Write(wave, 0, 4);
Byte[] fmt = System.Text.Encoding.UTF8.GetBytes("fmt "); //Mark the format section. Format chunk marker. Includes trailing null.
fileStream.Write(fmt, 0, 4);
Byte[] subChunk1 = BitConverter.GetBytes(16); //Length of format data. Always 16.
fileStream.Write(subChunk1, 0, 4);
UInt16 two = 2;
UInt16 one = 1;
Byte[] audioFormat = BitConverter.GetBytes(one); //Type of format (1 is PCM, other number means compression) . 2 byte integer. Wave type PCM
fileStream.Write(audioFormat, 0, 2);
Byte[] numChannels = BitConverter.GetBytes(one); //Number of Channels - 2 byte integer
fileStream.Write(numChannels, 0, 2);
Byte[] sampleRate = BitConverter.GetBytes(hz); //Sample Rate - 32 byte integer. Sample Rate = Number of Samples per second, or Hertz.
fileStream.Write(sampleRate, 0, 4);
Byte[] byteRate = BitConverter.GetBytes(hz * 2 * 1);// sampleRate * bytesPerSample * number of channels, here 16000*2*1.
fileStream.Write(byteRate, 0, 4);
UInt16 blockAlign = (ushort)(1 * 2); //channels * bytesPerSample, here 1 * 2 // Bytes Per Sample: 1=8 bit Mono, 2 = 8 bit Stereo or 16 bit Mono, 4 = 16 bit Stereo
fileStream.Write(BitConverter.GetBytes(blockAlign), 0, 2);
UInt16 sixteen = 16;
Byte[] bitsPerSample = BitConverter.GetBytes(sixteen); //Bits per sample (BitsPerSample * Channels) ?? should be 8???
fileStream.Write(bitsPerSample, 0, 2);
Byte[] dataString = System.Text.Encoding.UTF8.GetBytes("data"); //"data" chunk header. Marks the beginning of the data section.
fileStream.Write(dataString, 0, 4);
Byte[] subChunk2 = BitConverter.GetBytes(fileStream.Length - headerSize); //Size of the data section. data-size (equals file-size - 44). or NumSamples * NumChannels * bytesPerSample ??
fileStream.Write(subChunk2, 0, 4);
fileStream.Close();
}
}//end of class
I have updated your code into an extension method.
The idea was so you could append you data to a stream, like a file stream or memory stream, obviously this won't work for non seekable streams. So you could probably add error checking and validation.
I think I got the header right after looking at the specs, it seems to play at least. Note this is not really cross platform because of the endianness.
I'm not really sure what the rescaleFactor however I'll have to trust you there.
However, you should be able to modify this to accept data in different formats.
Lastly, I am updating the header at the end of the append, you could probably do this separately, i.e keep adding to the stream and then update it once when finished, add pepper and salt to taste.
Usage
using (var stream = new FileStream(GetFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
stream.AppendWaveData(receivedStreamSample);
}
Extension
public static class BinaryWriterExtensions
{
private const int HeaderSize = 44;
private const int Hz = 16000; //frequency or sampling rate
private const float RescaleFactor = 32767; //to convert float to Int16
public static void AppendWaveData<T>(this T stream, float[] buffer)
where T : Stream
{
if (stream.Length > HeaderSize)
{
stream.Seek(0, SeekOrigin.End);
}
else
{
stream.SetLength(HeaderSize);
stream.Position = HeaderSize;
}
// rescale
var floats = Array.ConvertAll(buffer, x => (short)(x * RescaleFactor));
// Copy to bytes
var result = new byte[floats.Length * sizeof(short)];
Buffer.BlockCopy(floats, 0, result, 0, result.Length);
// write to stream
stream.Write(result, 0, result.Length);
// Update Header
UpdateHeader(stream);
}
public static void UpdateHeader(Stream stream)
{
var writer = new BinaryWriter(stream);
writer.Seek(0, SeekOrigin.Begin);
writer.Write(Encoding.ASCII.GetBytes("RIFF")); //RIFF marker. Marks the file as a riff file. Characters are each 1 byte long.
writer.Write((int)(writer.BaseStream.Length - 8)); //file-size (equals file-size - 8). Size of the overall file - 8 bytes, in bytes (32-bit integer). Typically, you'd fill this in after creation.
writer.Write(Encoding.ASCII.GetBytes("WAVE")); //File Type Header. For our purposes, it always equals "WAVE".
writer.Write(Encoding.ASCII.GetBytes("fmt ")); //Mark the format section. Format chunk marker. Includes trailing null.
writer.Write(16); //Length of format data. Always 16.
writer.Write((short)1); //Type of format (1 is PCM, other number means compression) . 2 byte integer. Wave type PCM
writer.Write((short)2); //Number of Channels - 2 byte integer
writer.Write(Hz); //Sample Rate - 32 byte integer. Sample Rate = Number of Samples per second, or Hertz.
writer.Write(Hz * 2 * 1); // sampleRate * bytesPerSample * number of channels, here 16000*2*1.
writer.Write((short)(1 * 2)); //channels * bytesPerSample, here 1 * 2 // Bytes Per Sample: 1=8 bit Mono, 2 = 8 bit Stereo or 16 bit Mono, 4 = 16 bit Stereo
writer.Write((short)16); //Bits per sample (BitsPerSample * Channels) ?? should be 8???
writer.Write(Encoding.ASCII.GetBytes("data")); //"data" chunk header. Marks the beginning of the data section.
writer.Write((int)(writer.BaseStream.Length - HeaderSize)); //Size of the data section. data-size (equals file-size - 44). or NumSamples * NumChannels * bytesPerSample ??
}
} //end of class
change
writer.Write((short)2); into writer.Write((short)1);
and the generated file(.wav) will be plays well both on Windows(test against on windows 7) and android device.
otherwise,the Windows Media Player will says:having trouble playing files;android will plays with a quickly speed than expected.

How do I read an audio file into an array in C#

I am trying to read a WAV file into a buffer array in c# but am having some problems. I am using a file stream to manage the audio file. Here is what I have...
FileStream WAVFile = new FileStream(#"test.wav", FileMode.Open);
//Buffer for the wave file...
BinaryReader WAVreader = new BinaryReader(WAVFile);
//Read information from the header.
chunkID = WAVreader.ReadInt32();
chunkSize = WAVreader.ReadInt32();
RiffFormat = WAVreader.ReadInt32();
...
channels = WAVreader.ReadInt16();
samplerate = WAVreader.ReadInt32();
byteRate = WAVreader.ReadInt32();
blockAllign = WAVreader.ReadInt16();
bitsPerSample = WAVreader.ReadInt16();
dataID = WAVreader.ReadInt32();
dataSize = WAVreader.ReadInt32();
The above is reading data from the WAV file header. Then I have this:
musicalData = WAVreader.ReadBytes(dataSize);
...to read the actual sample data but this is only 26 bytes for 60 seconds of audio. Is this correct?
How would I go about converting the byte[] array to double[]?
This code should do the trick. It converts a wave file to a normalized double array (-1 to 1), but it should be trivial to make it an int/short array instead (remove the /32768.0 bit and add 32768 instead). The right[] array will be set to null if the loaded wav file is found to be mono.
I can't claim it's completely bullet proof (potential off-by-one errors), but after creating a 65536 sample array, and creating a wave from -1 to 1, none of the samples appear to go 'through' the ceiling or floor.
// convert two bytes to one double in the range -1 to 1
static double bytesToDouble(byte firstByte, byte secondByte)
{
// convert two bytes to one short (little endian)
short s = (secondByte << 8) | firstByte;
// convert to range from -1 to (just below) 1
return s / 32768.0;
}
// Returns left and right double arrays. 'right' will be null if sound is mono.
public void openWav(string filename, out double[] left, out double[] right)
{
byte[] wav = File.ReadAllBytes(filename);
// Determine if mono or stereo
int channels = wav[22]; // Forget byte 23 as 99.999% of WAVs are 1 or 2 channels
// Get past all the other sub chunks to get to the data subchunk:
int pos = 12; // First Subchunk ID from 12 to 16
// Keep iterating until we find the data chunk (i.e. 64 61 74 61 ...... (i.e. 100 97 116 97 in decimal))
while(!(wav[pos]==100 && wav[pos+1]==97 && wav[pos+2]==116 && wav[pos+3]==97))
{
pos += 4;
int chunkSize = wav[pos] + wav[pos + 1] * 256 + wav[pos + 2] * 65536 + wav[pos + 3] * 16777216;
pos += 4 + chunkSize;
}
pos += 8;
// Pos is now positioned to start of actual sound data.
int samples = (wav.Length - pos)/2; // 2 bytes per sample (16 bit sound mono)
if (channels == 2)
{
samples /= 2; // 4 bytes per sample (16 bit stereo)
}
// Allocate memory (right will be null if only mono sound)
left = new double[samples];
if (channels == 2)
{
right = new double[samples];
}
else
{
right = null;
}
// Write to double array/s:
int i=0;
while (pos < length)
{
left[i] = bytesToDouble(wav[pos], wav[pos + 1]);
pos += 2;
if (channels == 2)
{
right[i] = bytesToDouble(wav[pos], wav[pos + 1]);
pos += 2;
}
i++;
}
}
If you wanted to use plugins, then assuming your WAV file contains 16 bit PCM (which is the most common), you can use NAudio to read it out into a byte array, and then copy that into an array of 16 bit integers for convenience. If it is stereo, the samples will be interleaved left, right.
using (WaveFileReader reader = new WaveFileReader("myfile.wav"))
{
Assert.AreEqual(16, reader.WaveFormat.BitsPerSample, "Only works with 16 bit audio");
byte[] buffer = new byte[reader.Length];
int read = reader.Read(buffer, 0, buffer.Length);
short[] sampleBuffer = new short[read / 2];
Buffer.BlockCopy(buffer, 0, sampleBuffer, 0, read);
}
I personally try to avoid using third-party libraries as much as I can. But the option is still there if you'd like the code to look better and easier to handle.
It's been a good 10-15 years since I touched WAVE file processing, but unlike the first impression that most people get about wave files as simple fixed-size header followed by PCM encoded audio data, WAVE files are a bit more complex RIFF format files.
Instead of re-engineering RIFF file processing and various cases, I would suggest to use interop and call on APIs that deal with RIFF file format.
You can see example of how to open and get data buffer (and meta information about what buffer is) in this example. It's in C++, but it shows use of mmioOpen, mmioRead, mmioDescend, mmioAscend APIs that you would need to use to get your hands on a proper audio buffer.

Why I can't convert a byte[2] array to int with BitConverter?

The main problem is that I recive a binary number with only 10 bits in use from a SerialPort so I use this to receive the complete data:
byte[] buf = new byte[2];
serialPort.Read(buf, 0, buf.Length);
BitArray bits = new BitArray(buf);
The original idea for convert binary to int was this:
foreach (bool b in bits)
{
if(b){
binary += "1";
}
else{
binary+= "0";
}
}
decimal = Convert.ToInt32(binary, 2);
decimal = decimal >> 6;
binary is obviously a string, that works but I need to know if exists another solution, instead of the previuos code I try with this:
decimal = BitConverter.ToInt16(buf, 0);
But this only read the first 8 bits, I need the other 2 bits missing! If I change ToInt16 for a ToInt32
decimal = BitConverter.ToInt32(buf, 0);
The program stops for a System.ArgumentException: Destination array was not long enough...
What can I do?
You can just shift the values in the bytes so that they match, and put them together. If I got the use of bits right, that would be:
int value = (buf[0] << 2) | (buf[1] >> 6);

Convert byte array to int

I am trying to do some conversion in C#, and I am not sure how to do this:
private int byteArray2Int(byte[] bytes)
{
// bytes = new byte[] {0x01, 0x03, 0x04};
// how to convert this byte array to an int?
return BitConverter.ToInt32(bytes, 0); // is this correct?
// because if I have a bytes = new byte [] {0x32} => I got an exception
}
private string byteArray2String(byte[] bytes)
{
return System.Text.ASCIIEncoding.ASCII.GetString(bytes);
// but then I got a problem that if a byte is 0x00, it show 0x20
}
Could anyone give me some ideas?
BitConverter is the correct approach.
Your problem is because you only provided 8 bits when you promised 32. Try instead a valid 32-bit number in the array, such as new byte[] { 0x32, 0, 0, 0 }.
If you want an arbitrary length array converted, you can implement this yourself:
ulong ConvertLittleEndian(byte[] array)
{
int pos = 0;
ulong result = 0;
foreach (byte by in array) {
result |= ((ulong)by) << pos;
pos += 8;
}
return result;
}
It's not clear what the second part of your question (involving strings) is supposed to produce, but I guess you want hex digits? BitConverter can help with that too, as described in an earlier question.
byte[] bytes = { 0, 0, 0, 25 };
// If the system architecture is little-endian (that is, little end first),
// reverse the byte array.
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
int i = BitConverter.ToInt32(bytes, 0);
Console.WriteLine("int: {0}", i);
this is correct, but you're
missing, that Convert.ToInt32
'wants' 32 bits (32/8 = 4 bytes)
of information to make a conversion,
so you cannot convert just One byte:
`new byte [] {0x32}
absolutely the the same trouble
you have. and do not forget about
the encoding you use: from encoding to encoding you have 'different byte count per symbol'
A fast and simple way of doing this is just to copy the bytes to an integer using Buffer.BlockCopy:
UInt32[] pos = new UInt32[1];
byte[] stack = ...
Buffer.BlockCopy(stack, 0, pos, 0, 4);
This has the added benefit of being able to parse numerous integers into an array just by manipulating offsets..

Categories