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.
This is C# related. We have a case where we need to copy the entire source stream into a destination stream except for the last 16 bytes.
EDIT: The streams can range upto 40GB, so can't do some static byte[] allocation (eg: .ToArray())
Looking at the MSDN documentation, it seems that we can reliably determine the end of stream only when the return value is 0. Return values between 0 and the requested size can imply bytes are "not currently available" (what does that really mean?)
Currently it copies every single byte as follows. inStream and outStream are generic - can be memory, disk or network streams (actually some more too).
public static void StreamCopy(Stream inStream, Stream outStream)
{
var buffer = new byte[8*1024];
var last16Bytes = new byte[16];
int bytesRead;
while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0)
{
outStream.Write(buffer, 0, bytesRead);
}
// Issues:
// 1. We already wrote the last 16 bytes into
// outStream (possibly over the n/w)
// 2. last16Bytes = ? (inStream may not necessarily support rewinding)
}
What is a reliable way to ensure all but the last 16 are copied? I can think of using Position and Length on the inStream but there is a gotcha on MSDN that says
If a class derived from Stream does not support seeking, calls to Length, SetLength, Position, and Seek throw a NotSupportedException. .
Read between 1 and n bytes from the input stream.1
Append the bytes to a circular buffer.2
Write the first max(0, b - 16) bytes from the circular buffer to the output stream, where b is the number of bytes in the circular buffer.
Remove the bytes that you just have written from the circular buffer.
Go to step 1.
1This is what the Read method does – if you call int n = Read(buffer, 0, 500); it will read between 1 and 500 bytes into buffer and return the number of bytes read. If Read returns 0, you have reached the end of the stream.
2For maximum performance, you can read the bytes directly from the input stream into the circular buffer. This is a bit tricky, because you have to deal with the wraparound within the array underlying the buffer.
The following solution is fast and tested. Hope it's useful. It uses the double buffering idea you already had in mind. EDIT: simplified loop removing the conditional that separated the first iteration from the rest.
public static void StreamCopy(Stream inStream, Stream outStream) {
// Define the size of the chunk to copy during each iteration (1 KiB)
const int blockSize = 1024;
const int bytesToOmit = 16;
const int buffSize = blockSize + bytesToOmit;
// Generate working buffers
byte[] buffer1 = new byte[buffSize];
byte[] buffer2 = new byte[buffSize];
// Initialize first iteration
byte[] curBuffer = buffer1;
byte[] prevBuffer = null;
int bytesRead;
// Attempt to fully fill the buffer
bytesRead = inStream.Read(curBuffer, 0, buffSize);
if( bytesRead == buffSize ) {
// We succesfully retrieved a whole buffer, we will output
// only [blockSize] bytes, to avoid writing to the last
// bytes in the buffer in case the remaining 16 bytes happen to
// be the last ones
outStream.Write(curBuffer, 0, blockSize);
} else {
// We couldn't retrieve the whole buffer
int bytesToWrite = bytesRead - bytesToOmit;
if( bytesToWrite > 0 ) {
outStream.Write(curBuffer, 0, bytesToWrite);
}
// There's no more data to process
return;
}
curBuffer = buffer2;
prevBuffer = buffer1;
while( true ) {
// Attempt again to fully fill the buffer
bytesRead = inStream.Read(curBuffer, 0, buffSize);
if( bytesRead == buffSize ) {
// We retrieved the whole buffer, output first the last 16
// bytes of the previous buffer, and output just [blockSize]
// bytes from the current buffer
outStream.Write(prevBuffer, blockSize, bytesToOmit);
outStream.Write(curBuffer, 0, blockSize);
} else {
// We could not retrieve a complete buffer
if( bytesRead <= bytesToOmit ) {
// The bytes to output come solely from the previous buffer
outStream.Write(prevBuffer, blockSize, bytesRead);
} else {
// The bytes to output come from the previous buffer and
// the current buffer
outStream.Write(prevBuffer, blockSize, bytesToOmit);
outStream.Write(curBuffer, 0, bytesRead - bytesToOmit);
}
break;
}
// swap buffers for next iteration
byte[] swap = prevBuffer;
prevBuffer = curBuffer;
curBuffer = swap;
}
}
static void Assert(Stream inStream, Stream outStream) {
// Routine that tests the copy worked as expected
inStream.Seek(0, SeekOrigin.Begin);
outStream.Seek(0, SeekOrigin.Begin);
Debug.Assert(outStream.Length == Math.Max(inStream.Length - bytesToOmit, 0));
for( int i = 0; i < outStream.Length; i++ ) {
int byte1 = inStream.ReadByte();
int byte2 = outStream.ReadByte();
Debug.Assert(byte1 == byte2);
}
}
A much easier solution to code, yet slower since it would work at a byte level, would be to use an intermediate queue between the input stream and the output stream. The process would first read and enqueue 16 bytes from the input stream. Then it would iterate over the remaining input bytes, reading a single byte from the input stream, enqueuing it and then dequeuing a byte. The dequeued byte would be written to the output stream, until all bytes from the input stream are processed. The unwanted 16 bytes should linger in the intermediate queue.
Hope this helps!
=)
Use a circular buffer sounds great but there is no circular buffer class in .NET which means additional code anyways. I ended up with the following algorithm, a sort of map and copy - I think it's simple. The variable names are longer than usual for the sake of being self descriptive here.
This flows thru the buffers as
[outStream] <== [tailBuf] <== [mainBuf] <== [inStream]
public byte[] CopyStreamExtractLastBytes(Stream inStream, Stream outStream,
int extractByteCount)
{
//var mainBuf = new byte[1024*4]; // 4K buffer ok for network too
var mainBuf = new byte[4651]; // nearby prime for testing
int mainBufValidCount;
var tailBuf = new byte[extractByteCount];
int tailBufValidCount = 0;
while ((mainBufValidCount = inStream.Read(mainBuf, 0, mainBuf.Length)) > 0)
{
// Map: how much of what (passthru/tail) lives where (MainBuf/tailBuf)
// more than tail is passthru
int totalPassthruCount = Math.Max(0, tailBufValidCount +
mainBufValidCount - extractByteCount);
int tailBufPassthruCount = Math.Min(tailBufValidCount, totalPassthruCount);
int tailBufTailCount = tailBufValidCount - tailBufPassthruCount;
int mainBufPassthruCount = totalPassthruCount - tailBufPassthruCount;
int mainBufResidualCount = mainBufValidCount - mainBufPassthruCount;
// Copy: Passthru must be flushed per FIFO order (tailBuf then mainBuf)
outStream.Write(tailBuf, 0, tailBufPassthruCount);
outStream.Write(mainBuf, 0, mainBufPassthruCount);
// Copy: Now reassemble/compact tail into tailBuf
var tempResidualBuf = new byte[extractByteCount];
Array.Copy(tailBuf, tailBufPassthruCount, tempResidualBuf, 0,
tailBufTailCount);
Array.Copy(mainBuf, mainBufPassthruCount, tempResidualBuf,
tailBufTailCount, mainBufResidualCount);
tailBufValidCount = tailBufTailCount + mainBufResidualCount;
tailBuf = tempResidualBuf;
}
return tailBuf;
}
I m developing a C# CE application to read data from binary files which created by C++ progam to do item validation.
Below is the coding of the C++ program..
// File Name: Ean2an.bin which is created by struct
struct EAN2AN_TYPE
{
__int64 ean:40; // 5 bytes, up to 12 digits
__int64 rec_no:24; // 3 bytes rec no in the c_ItemMaster File, up to 16 million records
};
// After bind data to struct, wil create the binary file
bool CreateBin_EAN2AN_TYPE()
{
if(mn_RecordCount_EAN2AN_TYPE == 0) return false;
FILE *binfile;
qsort(mc_EAN2AN_TYPE, mn_RecordCount_EAN2AN_TYPE, sizeof(struct EAN2AN_TYPE), qsort_EAN2AN_TYPE);
try
{
binfile = fopen(ms_Path_EAN2AN_TYPE, "wb");
fwrite(&mc_EAN2AN_TYPE, sizeof(struct EAN2AN_TYPE), mn_RecordCount_EAN2AN_TYPE, binfile);
}
catch(Exception ^ex)
{
TaskProgramLibrary::Message::ERR("Create EAN2AN_TYPE.bin fail!\r\n " + ex->Message);
}
finally
{
fclose(binfile);
mdw_FileSize_EAN2AN_TYPE = FileSize(ms_Path_EAN2AN_TYPE);
}
return true;
}
I tried to read the data by using binary read(based on position) and use bitconverter to convert to int64 or using Marshal.PtrToStructure, but the value return is incorrect. Then i tried to read 5 bytes instead of 8 bytes from the file, but the value return stil incorrect.
Below is the written C# coding
//Struct created in C#
[StructLayout(LayoutKind.Sequential)]
public struct EAN2AN_TYPE
{
[MarshalAs(UnmanagedType.I8)]
public Int64 ean;
[MarshalAs(UnmanagedType.I8)]
public Int64 rec_no;
}
//The ways i tried to read in C#
//1.Read Int64 by Binary
private void ReadByBinary()
{
using (BinaryReader b = new BinaryReader(_fs))
{
while (b.PeekChar() != 0)
{
Int64 x = b.ReadInt64();
Console.WriteLine(x.ToString());
}
}
}
//2.Using Marshal to convert the Intptr to struct's field type
private object ReadByMarshal(Type iType)
{
_oType = iType;// typeof(System.Int64);
byte[] buffer = new byte[Marshal.SizeOf(_oType)];
//byte[] buffer = new byte[5];
object oReturn = null;
try
{
_fs.Read(buffer, 0, buffer.Length);
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
oReturn = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), _oType);
handle.Free();
return oReturn;
}
catch (Exception ex)
{
throw ex;
}
}
//3. Use Binary and use bit converter to convert to Int64
private void ReadByBinaryAndUseBitConverter()
{
using (BinaryReader b = new BinaryReader(_fs))
{
byte[] x = b.ReadBytes(8);
Int64 y = BitConverter.ToInt64(x, 0);
Console.WriteLine(y);
byte[] x2 = b.ReadBytes(8);
Int64 y2 = BitConverter.ToInt64(x2,0);
Console.WriteLine(y2);
}
}
//4. Use Marshal and convert to struct
public EAN2AN_TYPE GetStructValue()
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(EAN2AN_TYPE)];
EAN2AN_TYPE oReturn = new EAN2AN_TYPE();
try
{
//if (EOF) return null;
_fs.Read(buffer, 0, buffer.Length);
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
oReturn = (EAN2AN_TYPE)Marshal.PtrToStructure(rawDataPtr, typeof(EAN2AN_TYPE));
handle.Free();
if (_fs.Position >= _fs.Length)
Close();
return oReturn;
}
catch (Exception ex)
{
throw ex;
}
}
Edit:Upload image for the binary file
Edit:The first 8 bytes value read by C# program
The binary data shown by editor
Anybody have any idea?
Thanks in advance
ean is defined as a 40-bit entity and rec_no is 24-bit, making the entire struct only 64 bits. Your definition of EAN2AN_TYPE is 128 bits, so there's obviously going to be a problem. I question the sanity of whoever wrote the initial code, but your job is to get it back and use it, so you play with what you're dealt.
EDIT: Updated to use your specified data and take into account Ben's complaint
Here are two ways to get the same result. One is a little easier to understand since it does the movement in steps, the other is faster and "more correct". I put your example EAN data into my input to verify the results.
public struct EAN2AN_TYPE
{
public long ean; // can hold 5 bytes
public int rec_no; // can hold 3 bytes
}
byte[] incoming = new byte[] { 0x6F, 0x5D, 0x7C, 0xBA, 0xE3, 0x06, 0x07, 0x08 };
Memory Copying:
using(var stream = new MemoryStream(incoming))
using (var reader = new BinaryReader(stream))
{
// I leave it to you to get to the data
stream.Seek(0, SeekOrigin.Begin);
// get the data, padded to where we need for endianness
var ean_bytes = new byte[8];
// read the first 5 bytes
Buffer.BlockCopy(reader.ReadBytes(5), 0, ean_bytes, 0, 5);
var rec_no_bytes = new byte[4];
// read the last 3
Buffer.BlockCopy(reader.ReadBytes(3), 0, rec_no_bytes, 0, 3);
var ean2 = new EAN2AN_TYPE();
// convert
ean2.ean = BitConverter.ToInt64(ean_bytes, 0);
ean2.rec_no = BitConverter.ToInt32(rec_no_bytes, 0);
}
Bit shifting:
using (var stream = new MemoryStream(incoming))
using (var reader = new BinaryReader(stream))
{
// I leave it to you to get to the data
stream.Seek(0, SeekOrigin.Begin);
// get the data
var data = BitConverter.ToUInt64(reader.ReadBytes(8), 0);
var ean2 = new EAN2AN_TYPE();
// shift into our data
ean2.ean = (long)(data & ~0xFFFFFF0000000000);
ean2.rec_no = (int)(data >> 40);
}
Of course you could make the EAN2AN_TYPE a class, feed it in 8 bytes, then have property accessors that do the shifting shenanigans for you as well. I'd do that if this has to be a 2-way thing (i.e. you need to put data into one of those structs to send back to the C app).
It could be a problem with the endianness (if that's a word) of the data. You'll get problems like this if the data was written on a big-endian system, and read as little-endian, or vice-versa.
The other problem is that the two fields are actually packed into one 64-bit value. You may need to read an Int64 and then use bit operations to extract the two fields. All of your code appears to be reading two Int64 values by various means.
Thanks for your reply..
The initial C++ code is written by vendor. I only can try understand by read the C++ code. As my understanding.. it just create binary file and write in the data..
I cant find any encoding/convert part from code ..
I tried to convert the 1st ean(978086288751) to byte[] manually by code.
The byte[] is 111 93 124 186 227 0 0 0 which is different for the result i get..
I have tested ctacke suggested code. but i still not able to get the correct ean..
Below is the coding.. (i added in the filestream to read the binary file)
using (FileStream fileStream = File.OpenRead(_File))
{
MemoryStream memStream = new MemoryStream();
memStream.SetLength(fileStream.Length);
fileStream.Read(memStream.GetBuffer(), 0, (int)fileStream.Length);
using (BinaryReader reader = new BinaryReader(memStream))
{
//stream.SetLength(_fs);
// I leave it to you to get to the data
memStream.Seek(0, SeekOrigin.Begin);
// get the data, padded to where we need for endianness
byte[] ean_bytes = new byte[8];
// if this is wrong - then change param 4 to '3' to align at the other end
Buffer.BlockCopy(reader.ReadBytes(8), 0, ean_bytes, 0, 8);
//byte[] rec_no_bytes = new byte[4];
byte[] rec_no_bytes = new byte[4];
// if this is wrong - then change param 4 to '1' to align at the other end
Buffer.BlockCopy(reader.ReadBytes(3), 0, rec_no_bytes, 0, 3);
EAN2AN_TYPE ean2 = new EAN2AN_TYPE();
// convert
ean2.ean = BitConverter.ToInt64(ean_bytes, 0);
ean2.rec_no = BitConverter.ToInt32(rec_no_bytes, 0);
}
}
//Result
read 5 bytes : 17 0 0 0 0
ean : 17
//I changed to
var ean_bytes = new byte[8];
Buffer.BlockCopy(reader.ReadBytes(8), 0, ean_bytes, 0, 8);
Result
read 8 bytes :17 0 0 0 0 108 94 5
ean :386865365256241169
So sorry i m still a new user.. not able to post any attachment..
Hope you can understand from my interpretation.
I am trying to make an encryption algorithm.
I can read a file and convert it to bytes without any problems, and am saving the bytes in a byteArray.
The problem is I am currently creating the array size like this:
byte[] FileArray =new byte[10000000];
FileStream TheFileStream = new FileStream(FilePath.Text, FileMode.Open);
BinaryReader TheFileBinary = new BinaryReader(TheFileStream);
for (int i = 0; i < TheFileStream.Length; i++) {
FileArray = TheFileBinary.ReadBytes(10000000);
// I call a function here
if (TheFileStream.Position == TheFileStream.Length)
break;
}
However, I don't want the array size to be fixed, because if I make it 1000000 (as an example), other machines with small memory size might face a problem.
I need to find the Idle size of a memory size for each machine, how can I set the array size dynamically based on the free unallocated memory space, to be used where I can put it in the byteArray?
I have noticed the larger the Arraysize the faster it reads, so I don't want to make it too small either.
I would really appreciate the help.
The FileStream keeps track of how many bytes are in the file. Just use the Length property.
FileStream TheFileStream = new FileStream(FilePath.Text, FileMode.Open);
BinaryReader TheFileBinary = new BinaryReader(TheFileStream);
byte[] FileArray = TheFileBinary.ReadBytes(TheFileStream.Length);
Okay reread the question and finnaly found the part of it that was a question, "how can I know the free unallocated memory space so I can put it in the byteArray". Anyways I suggest you take a look at this question along with its highest rated comment.
If you're really worried about space, then use a simple List, read in chunks of the stream at a time (say 1024), and call the AddRange method on the list. After you're done, call ToArray on the List, and now you have a properly size byte array.
List<byte> byteArr = new List<byte>();
byte[] buffer = new byte[1024];
int bytesRead = 0;
using(FileStream TheFileStream = new FileStream(FilePath.Text, FileMode.Open))
{
while((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
byteArr.AddRange(buffer);
}
buffer = byteArr.ToArray();
// call your method here.
Edit: It's still preferable to read it in chunks for larger files. You can of course play with the buffer size however you want, but 1024 is usually a good starting point. Doing a read of the entire file will ultimately DOUBLE the memory, as you also have to deal with the internal read buffer being the size of the stream (on top of your own buffer). Breaking up the reads into chunks only takes FileStream.Length + <buffer size> memory as opposed to FileStream.Length * 2. Just something to keep in mind...
byte[] buffer = null;
using(FileStream TheFileStream = new FileStream(FilePath.Text, FileMode.Open))
{
buffer = new byte[TheFileStream.Length];
int offset = 0;
while((bytesRead = stream.Read(buffer, offset, 1024)) > 0)
offset += bytesRead;
// Or just TheFileStream.Read(buffer, 0, buffer.Length) if it's small enough.
}
You can use WMI to retrieve the instance of the Win32_OperatingSystem class and base your memory calculations off of the FreePhysicalMemory or TotalVisibleMemorySize properties:
static ulong GetAvailableMemoryKilobytes()
{
const string memoryPropertyName = "FreePhysicalMemory";
using (ManagementObject operatingSystem = new ManagementObject("Win32_OperatingSystem=#"))
return (ulong) operatingSystem[memoryPropertyName];
}
static ulong GetTotalMemoryKilobytes()
{
const string memoryPropertyName = "TotalVisibleMemorySize";
using (ManagementObject operatingSystem = new ManagementObject("Win32_OperatingSystem=#"))
return (ulong) operatingSystem[memoryPropertyName];
}
Then pass the result of either method to a method like this to scale the size of your read buffer to the memory of the local machine:
static int GetBufferSize(ulong memoryKilobytes)
{
const int bufferStepSize = 256; // 256 kilobytes of buffer...
const int memoryStepSize = 128 * 1024;// ...for every 128 megabytes of memory...
const int minBufferSize = 512; // ...no less than 512 kilobytes...
const int maxBufferSize = 10 * 1024; // ...no more than 10 megabytes
int bufferSize = bufferStepSize * ((int) memoryKilobytes / memoryStepSize);
bufferSize = Math.Max(bufferSize, minBufferSize);
bufferSize = Math.Min(bufferSize, maxBufferSize);
return bufferSize;
}
Obviously, increasing your buffer size by 256 KB for every 128 MB of RAM seems a little silly, but these number are just examples of how you might scale your buffer size if you really wanted to do that. Unless you're reading many, many files at once, worrying about a buffer that's a few hundred kilobytes or a few megabytes might be more trouble than it's worth. You might be better off just benchmarking to see which sized buffer gives the best performance (it might not need to be as large as you think) and using that.
Now you can simply update your code like this:
ulong memoryKilobytes =
GetAvailableMemoryKilobytes();
// ...or GetTotalMemoryKilobytes();
int bufferSize = GetBufferSize(memoryKilobytes);
using (FileStream TheFileStream = new FileStream(FilePath.Text, FileMode.Open))
{
byte[] FileArray = new byte[bufferSize];
int readCount;
while ((readCount = TheFileBinary.Read(FileArray, 0, bufferSize)) > 0)
{
// Call a method here, passing FileArray as a parameter
}
}