How to increase volume/amplitude on raw audio bytes - c#

I'm working with phone raw phone sounds and recordings and I want to normalize them to a certain volume level in a .Net C# project.
The sound is a collection of raw audio bytes (mono unheadered 16-bit signed PCM audio 16000Hz).
The audio is split into blocks of 3200 bytes == 100ms.
Any suggestions how to increase the volume/amplitude so the sounds is louder?
I haven't got a clue if I need to add a constant or multiply values, or if I need to do it to every 1,2,3.... bytes? And maybe there is already a open source solution for this?

To answer my own question (for others).
The solution is to multiply every sample (when 16bit PCM that are 2 bytes) with a constant value.
Do avoid overflow\to much increase you can calculate the highest constant value you can use by looking for the highest sample value and calculate the multiply factor to get it to highest sample value possible, in 16bit PCM case thats 32676 or something.
Here is litle example:
public byte[] IncreaseDecibel(byte[] audioBuffer, float multiplier)
{
// Max range -32768 and 32767
var highestValue = GetHighestAbsoluteSample(audioBuffer);
var highestPosibleMultiplier = (float)Int16.MaxValue/highestValue; // Int16.MaxValue = 32767
if (multiplier > highestPosibleMultiplier)
{
multiplier = highestPosibleMultiplier;
}
for (var i = 0; i < audioBuffer.Length; i = i + 2)
{
Int16 sample = BitConverter.ToInt16(audioBuffer, i);
sample *= (Int16)(sample * multiplier);
byte[] sampleBytes = GetLittleEndianBytesFromShort(sample);
audioBuffer[i] = sampleBytes[sampleBytes.Length-2];
audioBuffer[i+1] = sampleBytes[sampleBytes.Length-1];
}
return audioBuffer;
}
// ADDED GetHighestAbsoluteSample, hopefully its still correct because code has changed over time
/// <summary>
/// Peak sample value
/// </summary>
/// <param name="audioBuffer">audio</param>
/// <returns>0 - 32768</returns>
public static short GetHighestAbsoluteSample(byte[] audioBuffer)
{
Int16 highestAbsoluteValue = 0;
for (var i = 0; i < (audioBuffer.Length-1); i = i + 2)
{
Int16 sample = ByteConverter.GetShortFromLittleEndianBytes(audioBuffer, i);
// prevent Math.Abs overflow exception
if (sample == Int16.MinValue)
{
sample += 1;
}
var absoluteValue = Math.Abs(sample);
if (absoluteValue > highestAbsoluteValue)
{
highestAbsoluteValue = absoluteValue;
}
}
return (highestAbsoluteValue > LowestPossibleAmplitude) ?
highestAbsoluteValue : LowestPossibleAmplitude;
}

Related

Fastest way to copy data from ReadOnlySpan to output with pixel conversion

I'm having performance issue when I copy the data from input (ReadOnlySpan) to output (Span) using (Loops like 'for')
there is Span.CopyTo, it's perfect and very fast
but for now it's useless without converting the pixels.
below is the code, I have feeling that there is some short way to do that instead of the current process:
public unsafe void UpdateFromOutput(CanvasDevice device, ReadOnlySpan<byte> data, uint width, uint height, uint pitch)
{
using (var renderTargetMap = new BitmapMap(device, RenderTarget))
{
var inputPitch = (int)pitch;
var mapPitch = (int)renderTargetMap.PitchBytes;
var mapData = new Span<byte>(new IntPtr(renderTargetMap.Data).ToPointer(), (int)RenderTarget.Size.Height * mapPitch);
switch (CurrentPixelFormat)
{
case PixelFormats.RGB0555:
FramebufferConverter.ConvertFrameBufferRGB0555ToXRGB8888(width, height, data, inputPitch, mapData, mapPitch);
break;
case PixelFormats.RGB565:
FramebufferConverter.ConvertFrameBufferRGB565ToXRGB8888(width, height, data, inputPitch, mapData, mapPitch);
break;
}
}
}
then inside function like ConvertFrameBufferRGB0555ToXRGB8888
I will go through width and height like below:
var castInput = MemoryMarshal.Cast<byte, ushort>(input);
var castInputPitch = inputPitch / sizeof(ushort);
var castOutput = MemoryMarshal.Cast<byte, uint>(output);
var castOutputPitch = outputPitch / sizeof(uint);
castOutput.Fill(0);
for (var i = 0; i < height;i++)
{
var inputLine = castInput.Slice(i * castInputPitch, castInputPitch);
var outputLine = castOutput.Slice(i * castOutputPitch, castOutputPitch);
for (var j = 0; j < width;j++)
{
outputLine[j] = ConverToRGB888(inputLine[j]);
}
}
The code above working but slow in some cases.
Please note: I'm modifying a project so the code above was written by the original developer and I need help because I don't understand how the process is working, still very confused.. specially in the Slice part.
Tried as test only to copy the input to output directly data.CopyTo(mapData); and I got this (as expected):
Hope there is some solution with Marshal and Span functions
Many thanks.
Update regarding (ConverToRGB888)
As for ConverToRGB888, the original code contains RGB565LookupTable:
private const uint LookupTableSize = ushort.MaxValue + 1;
private static uint[] RGB565LookupTable = new uint[LookupTableSize];
public static void SetRGB0565LookupTable()
{
uint r565, g565, b565;
double red = 255.0;
double green = 255.0;
double blue = 255.0;
for (uint i = 0; i < LookupTableSize; i++)
{
//RGB565
r565 = (i >> 11) & 0x1F;
g565 = (i >> 5) & 0x3F;
b565 = (i & 0x1F);
r565 = (uint)Math.Round(r565 * red / 31.0);
g565 = (uint)Math.Round(g565 * green / 63.0);
b565 = (uint)Math.Round(b565 * blue / 31.0);
RGB565LookupTable[i] = (0xFF000000 | r565 << 16 | g565 << 8 | b565);
}
}
private static uint ConverToRGB888(ushort x)
{
return RGB565LookupTable[x];
}
SetRGB0565LookupTable() will be called only once to fill the values.
Conclusion:
The Fill(0) was not important and it was causing delay
The unsafe version (accepted answer) was clear for me and a bit faster
Avoiding For partially even faster like Here [Tested]
Pre-Lookup table is very helpful and made the conversion faster
Memory helpers like Span.CopyTo, Buffer.MemoryCopy source available Here
Using Parallel.For is faster in some cases with help of UnsafeMemory
If you have input with (Pixels Type) supported by Win2D there is possibility to avoid loops like:
byte[] dataBytes = new byte[data.Length];
fixed (byte* inputPointer = &data[0])
Marshal.Copy((IntPtr)inputPointer, dataBytes, 0, data.Length);
RenderTarget = CanvasBitmap.CreateFromBytes(renderPanel, dataBytes, (int)width, (int)height, DirectXPixelFormat.R8G8UIntNormalized, 92, CanvasAlphaMode.Ignore);
but not sure from the last point as I wasn't able to test on 565,555.
Thanks for DekuDesu the explanation and simplified version he provide helped me to do more tests.
I'm having performance issue when I copy the data from input (ReadOnlySpan) to output (Span) using (Loops like 'for')
The the code you provided is already pretty safe and has the best complexity you're going get for pixel-by-pixel operations. The presence of nested for loops does not necessarily correspond to performance issues or increased complexity.
I need help because I don't understand how the process is working, still very confused.. specially in the Slice part.
This code looks like it's meant to convert one bitmap format to another. Bitmaps come in varying sizes and formats. Because of this they include an additional piece of information along with width and height, pitch.
Pitch is the distance in bytes between two lines of pixel information, this is used to account for formats that don't include full 32/64bit color information.
Knowing this I commented the method in question as to help explain what it's doing.
public static void ConvertFrameBufferRGB565ToXRGB8888(uint width, uint height, ReadOnlySpan<byte> input, int inputPitch, Span<byte> output, int outputPitch)
{
// convert the span of bytes into a span of ushorts
// so we can use span[i] to get a ushort
var castInput = MemoryMarshal.Cast<byte, ushort>(input);
// pitch is the number of bytes between the first byte of a line and the first byte of the next line
// convert the pitch from bytes into ushort pitch
var castInputPitch = inputPitch / sizeof(ushort);
// convert the span of bytes into a span of ushorts
// so we can use span[i] to get a ushort
var castOutput = MemoryMarshal.Cast<byte, uint>(output);
var castOutputPitch = outputPitch / sizeof(uint);
for (var i = 0; i < height; i++)
{
// get a line from the input
// remember that pitch is the number of ushorts between lines
// so i * pitch here gives us the index of the i'th line, and we don't need the padding
// ushorts at the end so we only take castInputPitch number of ushorts
var inputLine = castInput.Slice(i * castInputPitch, castInputPitch);
// same thing as above but for the output
var outputLine = castOutput.Slice(i * castOutputPitch, castOutputPitch);
for (var j = 0; j < width; j++)
{
// iterate through the line, converting each pixel and storing it in the output span
outputLine[j] = ConverToRGB888(inputLine[j]);
}
}
}
Fastest way to copy data from ReadOnlySpan to output with pixel conversion
Honestly the method you provided is just fine, it's safe and fast ish. Keep in mind that copying data like bitmaps linearly on CPU's is an inherently slow process. The most performance savings you could hope for is avoiding copying data redundantly. Unless this needs absolutely blazing speed I would not recommend changes other than removing .fill(0) since it's probably unnecessary, but you would have to test that.
If you ABSOLUTELY must get more performance out of this you may want to consider something like what I've provided below. I caution you however, unsafe code like this is well.. unsafe and is prone to errors. It has almost no error checking and makes a LOT of assumptions, so that's up for you to implement.
If it's still not fast enough consider writing a .dll in C and use interop maybe.
public static unsafe void ConvertExtremelyUnsafe(ulong height, ref byte inputArray, ulong inputLength, ulong inputPitch, ref byte outputArray, ulong outputLength, ulong outputPitch)
{
// pin down pointers so they dont move on the heap
fixed (byte* inputPointer = &inputArray, outputPointer = &outputArray)
{
// since we have to account for padding we should go line by line
for (ulong y = 0; y < height; y++)
{
// get a pointer for the first byte of the line of the input
byte* inputLinePointer = inputPointer + (y * inputPitch);
// get a pointer for the first byte of the line of the output
byte* outputLinePointer = outputPointer + (y * outputPitch);
// traverse the input line by ushorts
for (ulong i = 0; i < (inputPitch / sizeof(ushort)); i++)
{
// calculate the offset for the i'th ushort,
// becuase we loop based on the input and ushort we dont need an index check here
ulong inputOffset = i * sizeof(ushort);
// get a pointer to the i'th ushort
ushort* rgb565Pointer = (ushort*)(inputLinePointer + inputOffset);
ushort rgb565Value = *rgb565Pointer;
// convert the rgb to the other format
uint rgb888Value = ConverToRGB888(rgb565Value);
// calculate the offset for i'th uint
ulong outputOffset = i * sizeof(uint);
// at least attempt to avoid overflowing a buffer, not that the runtime would let you do that, i would hope..
if (outputOffset >= outputLength)
{
throw new IndexOutOfRangeException($"{nameof(outputArray)}[{outputOffset}]");
}
// get a pointer to the i'th uint
uint* rgb888Pointer = (uint*)(outputLinePointer + outputOffset);
// write the bytes of the rgb888 to the output array
*rgb888Pointer = rgb888Value;
}
}
}
}
disclaimer: I wrote this on mobile

Create random ints with minimum and maximum from Random.NextBytes()

Title pretty much says it all. I know I could use Random.NextInt(), of course, but I want to know if there's a way to turn unbounded random data into bounded without statistical bias. (This means no RandomInt() % (maximum-minimum)) + minimum). Surely there is a method like it, that doesn't introduce bias into the data it outputs?
If you assume that the bits are randomly distributed, I would suggest:
Generate enough bytes to get a number within the range (e.g. 1 byte to get a number in the range 0-100, 2 bytes to get a number in the range 0-30000 etc).
Use only enough bits from those bytes to cover the range you need. So for example, if you're generating numbers in the range 0-100, take the bottom 7 bits of the byte you've generated
Interpret the bits you've got as a number in the range [0, 2n) where n is the number of bit
Check whether the number is in your desired range. It should be at least half the time (on average)
If so, use it. If not, repeat the above steps until a number is in the right range.
The use of just the required number of bits is key to making this efficient - you'll throw away up to half the number of bytes you generate, but no more than that, assuming a good distribution. (And if you are generating numbers in a nicely binary range, you won't need to throw anything away.)
Implementation left as an exercise to the reader :)
You could try with something like:
public static int MyNextInt(Random rnd, int minValue, int maxValue)
{
var buffer = new byte[4];
rnd.NextBytes(buffer);
uint num = BitConverter.ToUInt32(buffer, 0);
// The +1 is to exclude the maxValue in the case that
// minValue == int.MinValue, maxValue == int.MaxValue
double dbl = num * 1.0 / ((long)uint.MaxValue + 1);
long range = (long)maxValue - minValue;
int result = (int)(dbl * range) + minValue;
return result;
}
Totally untested... I can't guarantee that the results are truly pseudo-random... But the idea of creating a double (dbl) number is the same used by the Random class. Only I use the uint.MaxValue as the base instead of int.MaxValue. In this way I don't have to check for negative values of the buffer.
I propose a generator of random integers, based on NextBytes.
This method discards only 9.62% of bits in average over the word size range for positive Int32's due to the usage of Int64 as a representation for bit manupulation.
Maximum bit loss occurs at word size of 22 bits, and it's 20 lost bits of 64 used in byte range conversion. In this case bit efficiency is 68.75%
Also, 25% of values are lost because of clipping the unbound range to maximum value.
Be careful to use Take(N) on the IEnumerable returned, because it's an infinite generator otherwise.
I'm using a buffer of 512 long values, so it generates 4096 random bytes at once. If you just need a sequence of few integers, change the buffer size from 512 to a more optimal value, down to 1.
public static class RandomExtensions
{
public static IEnumerable<int> GetRandomIntegers(this Random r, int max)
{
if (max < 1)
throw new ArgumentOutOfRangeException("max", max, "Must be a positive value.");
const int longWordsTotal = 512;
const int bufferSize = longWordsTotal * 8;
var buffer = new byte[bufferSize];
var wordSize = (int)Math.Log(max, 2) + 1;
while(true)
{
r.NextBytes(buffer);
for (var longWordIndex = 0; longWordIndex < longWordsTotal; longWordIndex++)
{
ulong longWord = BitConverter.ToUInt64(buffer, longWordIndex);
var lastStartBit = 64 - wordSize;
var count = 0;
for (var startBit = 0; startBit <= lastStartBit; startBit += wordSize)
{
count ++;
var mask = ((1UL << wordSize) - 1) << startBit;
var unboundValue = (int)((mask & longWord) >> startBit);
if (unboundValue <= max)
yield return unboundValue;
}
}
}
}
}

how to split wave signal into frames

I'm working a project about chord recognition. I'm using someone's journal as a reference but I still have little grasp in field of DSP. In her reference, first thing is I need to split the signal from wav file into number of frames. In my case, I need to split into 65 ms each frame, with 2866 sample per frame.
I have searched how to split signal into frames but I don't find them clear enough for me to understand.
So far these are some of my codes in WavProcessing class:
public void SetFileName(String fileNameWithPath) //called first in the form, to get the FileStream
{
_fileNameWithPath = fileNameWithPath;
strm = File.OpenRead(_fileNameWithPath);
}
public double getLengthTime(uint wavSize, uint sampleRate, int bitRate, int channels)
{
wavTimeLength = ((strm.Length - 44) / (sampleRate * (bitRate / 8))) / channels;
return wavTimeLength;
}
public int getNumberOfFrames() //return number of frames, I just divided total length time with interval time between frames. (in my case, 3000ms / 65 ms = 46 frames)
{
numOfFrames = (int) (wavTimeLength * 1000 / _sampleFrameTime);
return numOfFrames;
}
public int getSamplePerFrame(UInt32 sampleRate, int sampleFrameTime) // return the sample per frame value (in my case, it's 2866)
{
_sampleRate = sampleRate;
_sampleFrameTime = sampleFrameTime;
sFr = (int)(sampleRate * (sampleFrameTime / 1000.0 ));
return sFr;
}
I just still don't get the idea how to split the signal into 65 ms per frame in C#.
Do I need to split the FileStream and break them into frames and save them into array? Or anything else?
with NAudio you would do it like this:
using (var reader = new AudioFileReader("myfile.wav"))
{
float[] sampleBuffer = new float[2866];
int samplesRead = reader.Read(sampleBuffer, 0, sampleBuffer.Length);
}
As others have commented, the number of samples you read ought to be a power of 2 if you plan to pass it into an FFT. Also, if the file is stereo, you will have left and right samples interleaved, so your FFT will need to be able to cope with this.

Variable length encoding of an integer

Whats the best way of doing variable length encoding of an unsigned integer value in C# ?
"The actual intent is to append a variable length encoded integer (bytes) to a file header."
For ex: "Content-Length" - Http Header
Can this be achieved with some changes in the logic below.
I have written some code which does that ....
A method I have used, which makes smaller values use fewer bytes, is to encode 7 bits of data + 1 bit of overhead pr. byte.
The encoding works only for positive values starting with zero, but can be modified if necessary to handle negative values as well.
The way the encoding works is like this:
Grab the lowest 7 bits of your value and store them in a byte, this is what you're going to output
Shift the value 7 bits to the right, getting rid of those 7 bits you just grabbed
If the value is non-zero (ie. after you shifted away 7 bits from it), set the high bit of the byte you're going to output before you output it
Output the byte
If the value is non-zero (ie. same check that resulted in setting the high bit), go back and repeat the steps from the start
To decode:
Start at bit-position 0
Read one byte from the file
Store whether the high bit is set, and mask it away
OR in the rest of the byte into your final value, at the bit-position you're at
If the high bit was set, increase the bit-position by 7, and repeat the steps, skipping the first one (don't reset the bit-position)
39 32 31 24 23 16 15 8 7 0
value: |DDDDDDDD|CCCCCCCC|BBBBBBBB|AAAAAAAA|
encoded: |0000DDDD|xDDDDCCC|xCCCCCBB|xBBBBBBA|xAAAAAAA| (note, stored in reverse order)
As you can see, the encoded value might occupy one additional byte that is just half-way used, due to the overhead of the control bits. If you expand this to a 64-bit value, the additional byte will be completely used, so there will still only be one byte of extra overhead.
Note: Since the encoding stores values one byte at a time, always in the same order, big- or little-endian systems will not change the layout of this. The least significant byte is always stored first, etc.
Ranges and their encoded size:
0 - 127 : 1 byte
128 - 16.383 : 2 bytes
16.384 - 2.097.151 : 3 bytes
2.097.152 - 268.435.455 : 4 bytes
268.435.456 - max-int32 : 5 bytes
Here's C# implementations for both:
void Main()
{
using (FileStream stream = new FileStream(#"c:\temp\test.dat", FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(stream))
writer.EncodeInt32(123456789);
using (FileStream stream = new FileStream(#"c:\temp\test.dat", FileMode.Open))
using (BinaryReader reader = new BinaryReader(stream))
reader.DecodeInt32().Dump();
}
// Define other methods and classes here
public static class Extensions
{
/// <summary>
/// Encodes the specified <see cref="Int32"/> value with a variable number of
/// bytes, and writes the encoded bytes to the specified writer.
/// </summary>
/// <param name="writer">
/// The <see cref="BinaryWriter"/> to write the encoded value to.
/// </param>
/// <param name="value">
/// The <see cref="Int32"/> value to encode and write to the <paramref name="writer"/>.
/// </param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="writer"/> is <c>null</c>.</para>
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <para><paramref name="value"/> is less than 0.</para>
/// </exception>
/// <remarks>
/// See <see cref="DecodeInt32"/> for how to decode the value back from
/// a <see cref="BinaryReader"/>.
/// </remarks>
public static void EncodeInt32(this BinaryWriter writer, int value)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (value < 0)
throw new ArgumentOutOfRangeException("value", value, "value must be 0 or greater");
do
{
byte lower7bits = (byte)(value & 0x7f);
value >>= 7;
if (value > 0)
lower7bits |= 128;
writer.Write(lower7bits);
} while (value > 0);
}
/// <summary>
/// Decodes a <see cref="Int32"/> value from a variable number of
/// bytes, originally encoded with <see cref="EncodeInt32"/> from the specified reader.
/// </summary>
/// <param name="reader">
/// The <see cref="BinaryReader"/> to read the encoded value from.
/// </param>
/// <returns>
/// The decoded <see cref="Int32"/> value.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="reader"/> is <c>null</c>.</para>
/// </exception>
public static int DecodeInt32(this BinaryReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
bool more = true;
int value = 0;
int shift = 0;
while (more)
{
byte lower7bits = reader.ReadByte();
more = (lower7bits & 128) != 0;
value |= (lower7bits & 0x7f) << shift;
shift += 7;
}
return value;
}
}
You should first make an histogram of your value. If the distribution is random (that is, every bin of your histogram's count is close to the other), then you'll not be able encode more efficiently than the binary representation for this number.
If your histogram is unbalanced (that is, if some values are more present than others), then it might make sense to choose an encoding that's using less bits for these values, while using more bits for the other -unlikely- values.
For example, if the number you need to encode are 2x more likely to be smaller than 15 bits than larger, you can use the 16-th bit to tell so and only store/send 16 bits (if it's zero, then the upcoming byte will form a 16-bits numbers that can fit in a 32 bits number).
If it's 1, then the upcoming 25 bits will form a 32 bits numbers.
You loose one bit here but because it's unlikely, in the end, for a lot of number, you win more bits.
Obviously, this is a trivial case, and the extension of this to more than 2 cases is the Huffman algorithm that affect a "code word" that close-to optimum based on the probability of the numbers to appear.
There's also the arithmetic coding algorithm that does this too (and probably other).
In all cases, there is no solution that can store random value more efficiently than what's being done currently in computer memory.
You have to think about how long and how hard will be the implementation of such solution compared to the saving you'll get in the end to know if it's worth it. The language itself is not relevant here.
If small values are more common than large ones you can use Golomb coding.
I know this question was asked quite a few years ago, however for MIDI developers I thought to share some code from a personal midi project I'm working on. The code block is based on a segment from the book Maximum MIDI by Paul Messick (This example is a tweaked version for my own needs however, the concept is all there...).
public struct VariableLength
{
// Variable Length byte array to int
public VariableLength(byte[] bytes)
{
int index = 0;
int value = 0;
byte b;
do
{
value = (value << 7) | ((b = bytes[index]) & 0x7F);
index++;
} while ((b & 0x80) != 0);
Length = index;
Value = value;
Bytes = new byte[Length];
Array.Copy(bytes, 0, Bytes, 0, Length);
}
// Variable Length int to byte array
public VariableLength(int value)
{
Value = value;
byte[] bytes = new byte[4];
int index = 0;
int buffer = value & 0x7F;
while ((value >>= 7) > 0)
{
buffer <<= 8;
buffer |= 0x80;
buffer += (value & 0x7F);
}
while (true)
{
bytes[index] = (byte)buffer;
index++;
if ((buffer & 0x80) > 0)
buffer >>= 8;
else
break;
}
Length = index;
Bytes = new byte[index];
Array.Copy(bytes, 0, Bytes, 0, Length);
}
// Number of bytes used to store the variable length value
public int Length { get; private set; }
// Variable Length Value
public int Value { get; private set; }
// Bytes representing the integer value
public byte[] Bytes { get; private set; }
}
How to use:
public void Example()
{
//Convert an integer into a variable length byte
int varLenVal = 480;
VariableLength v = new VariableLength(varLenVal);
byte[] bytes = v.Bytes;
//Convert a variable length byte array into an integer
byte[] varLenByte = new byte[2]{131, 96};
VariableLength v = new VariableLength(varLenByte);
int result = v.Length;
}
As Grimbly pointed out, there exists BinaryReader.Read7BitEncodedInt and BinaryWriter.Write7BitEncodedInt. However, these are internal methods that one cannot call from a BinaryReader or -Writer object.
However, what you can do is take the internal implementation and copy it from the reader and the writer:
public static int Read7BitEncodedInt(this BinaryReader br) {
// Read out an Int32 7 bits at a time. The high bit
// of the byte when on means to continue reading more bytes.
int count = 0;
int shift = 0;
byte b;
do {
// Check for a corrupted stream. Read a max of 5 bytes.
// In a future version, add a DataFormatException.
if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7
throw new FormatException("Format_Bad7BitInt32");
// ReadByte handles end of stream cases for us.
b = br.ReadByte();
count |= (b & 0x7F) << shift;
shift += 7;
} while ((b & 0x80) != 0);
return count;
}
public static void Write7BitEncodedInt(this BinaryWriter br, int value) {
// Write out an int 7 bits at a time. The high bit of the byte,
// when on, tells reader to continue reading more bytes.
uint v = (uint)value; // support negative numbers
while (v >= 0x80) {
br.Write((byte)(v | 0x80));
v >>= 7;
}
br.Write((byte)v);
}
When you include this code in any class of your project, you'll be able to use the methods on any BinaryReader/BinaryWriter object. They've only been slightly modified to make them work outside of their original classes (for example by changing ReadByte() to br.ReadByte()). The comments are from the original source.
BinaryReader.Read7BitEncodedInt Method ?
BinaryWriter.Write7BitEncodedInt Method ?

How to correctly convert filesize in bytes into mega or gigabytes?

I'm using the DriveInfo class in my C# project to retrieve the available bytes on given drives. How to I correctly convert this number into Mega- or Gigabytes? Dividing by 1024 will not do the job I guess. The results always differ from those shown in the Windows-Explorer.
1024 is correct for usage in programs.
The reason you may be having differences is likely due to differences in what driveinfo reports as "available space" and what windows considers available space.
Note that only drive manufacturers use 1,000. Within windows and most programs the correct scaling is 1024.
Also, while your compiler should optimize this anyway, this calculation can be done by merely shifting the bits by 10 for each magnitude:
KB = B >> 10
MB = KB >> 10 = B >> 20
GB = MB >> 10 = KB >> 20 = B >> 30
Although for readability I expect successive division by 1024 is clearer.
XKCD has the definite answer:
/// <summary>
/// Function to convert the given bytes to either Kilobyte, Megabyte, or Gigabyte
/// </summary>
/// <param name="bytes">Double -> Total bytes to be converted</param>
/// <param name="type">String -> Type of conversion to perform</param>
/// <returns>Int32 -> Converted bytes</returns>
/// <remarks></remarks>
public static double ConvertSize(double bytes, string type)
{
try
{
const int CONVERSION_VALUE = 1024;
//determine what conversion they want
switch (type)
{
case "BY":
//convert to bytes (default)
return bytes;
break;
case "KB":
//convert to kilobytes
return (bytes / CONVERSION_VALUE);
break;
case "MB":
//convert to megabytes
return (bytes / CalculateSquare(CONVERSION_VALUE));
break;
case "GB":
//convert to gigabytes
return (bytes / CalculateCube(CONVERSION_VALUE));
break;
default:
//default
return bytes;
break;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return 0;
}
}
/// <summary>
/// Function to calculate the square of the provided number
/// </summary>
/// <param name="number">Int32 -> Number to be squared</param>
/// <returns>Double -> THe provided number squared</returns>
/// <remarks></remarks>
public static double CalculateSquare(Int32 number)
{
return Math.Pow(number, 2);
}
/// <summary>
/// Function to calculate the cube of the provided number
/// </summary>
/// <param name="number">Int32 -> Number to be cubed</param>
/// <returns>Double -> THe provided number cubed</returns>
/// <remarks></remarks>
public static double CalculateCube(Int32 number)
{
return Math.Pow(number, 3);
}
//Sample Useage
String Size = "File is " + ConvertSize(250222,"MB") + " Megabytes in size"
1024 is actually wrong. The International Engineering Community (IEC) has developed a standard in 2000, which is sadly being ignored by the computer industry. This standard basically says that
1000 bytes is a kilobyte, 1000KB are one MB and so on. The abbreviations are KB, MB, GB and so on.
The widely used 1024 bytes = 1 kilobyte should instead by called 1024 bytes = 1 Kibibyte (KiB), 1024 KiB = 1 Mebibyte (MiB), 1024 MiB = 1 Gibibyte (GiB) and so on.
You can all read it up on the IEC SI zone.
So in order for your conversions to be correct and right according to international standardization you should use this scientific notation.
It depends on if you want the actual file size or the size on disk. The actual file size is the actual number of bytes that the file uses in memory. The size on disk is a function of the file size and the block size for your disk/file system.
I have a faint recollection that the answer on whether to use 1000 or 1024 lies in the casing of the prefix.
Example:
If the "scientific" 1000 scaling is used, then the "scientific" unit will be kB (just as in kg, kN etc). If the computer centric 1024 scaling is used, then the unit will be KB. So, uppercasing the scientific prefix makes it computer centric.
Divide by 1024.
Here is simple c++ code sample I have prepared which might be helpful. You need to provide input size in bytes and the function will return in human readable size:
std::string get_human_readable_size(long bytes)
{
long gb = 1024 * 1024 * 1024;
long mb = 1024 * 1024;
long kb = 1024;
if( bytes >= gb) return std::to_string( (float)bytes/gb ) + " GB ";
if( bytes >= mb) return std::to_string( (float)bytes/mb ) + " MB ";
if( bytes >= kb) return std::to_string( (float)bytes/gb ) + " KB ";
return std::to_string(bytes) + " B ";
}

Categories