Convert 32 bit float audio to 16 bit byte array? - c#

I am getting audio using the NAudio library which returns a 32 bit float[] of audio data. I'm trying to find a way to convert this to a 16 bit byte[] for playback.
private void sendData(float[] samples)
{
Buffer.BlockCopy(samples, 0, byteArray, 0, samples.Length);
byte[] encoded = codec.Encode(byteArray, 0, byteArray.Length);
waveProvider.AddSamples(byteArray, 0, byteArray.Length);
s.Send(encoded, SocketFlags.None);
}
The audio being sent to waveProvider is coming out static-y — I don't think I'm converting correctly. How can I convert to a byte array of 16 bit samples?

Buffer.BlockCopy copies a number of bytes, but you're passing it a number of elements. Since a float is 4 bytes and a byte is obviously 1, you're using a fourth of samples to fill up half of byteArray, leaving the rest untouched. That probably won't give you very good audio, to say the least.
What you'll need to do is convert from a floating-point value between −1 and 1 to a 16-bit integer value between −215 and 215−1. If we convert to shorts rather than bytes, it's rather simple:
shortSample = (short)Math.Floor(floatSample * 32767);
(If you know that floatSample will always be less than 1, you should multiply by 32,768 rather than 32,767.)
Of course, you want a byte array rather than a short array. Unfortunately, you've not given enough information for that last step. There's two things that you'll need to know: endianness and signedness. If it's unsigned, you'll need to convert that short to a ushort by adding 32,768. Then you need to split each short or ushort up into two bytes. Endianness determines the order, but you'll probably need to do some bit-shifting.

Related

How to read/write unsigned byte array between C# and Java on a file?

This question is a bit similar to my previous one, where I asked a "cross-language" way to write and read integers between a Java and a C# program. Problem was the endianess.
Anyway, I'm facing a secondary problem. My goal is now to store and retrieve an array of unsigned bytes (values from 0 to 255) in a way that it can be processed by both Java and C#.
In C# it's easy, since unsigned byte[] exists:
BinaryWriterBigEndian writer = new BinaryWriterBigEndian(fs);
// ...
writer.Write(byteData, 0, byteData.Length);
BinaryWriterBigEndian is... well... a big-endian binary writer ;)
This way, the file will contain a sequence composed by, for example, the following values (in a big-endian representation):
[0][120][50][250][221]...
Now it's time to do the same thing under Java. Since unsigned byte[] does not exist here, the array is stored in memory as a (signed) int[] in order to have the possibility to represent values higher than 127.
How to write it as a sequence of unsigned byte values like C# does?
I tried with this:
ByteBuffer byteBuffer = ByteBuffer.allocate(4 * dataLength);
IntBuffer intBuffer = byteBuffer.asIntBuffer();
intBuffer.put(intData);
outputStream.write(byteBuffer.array());
Writing goes well, but C# is not able to read it in the proper way.
Since unsigned byte[] does not exist [...]
You don't care. Signed or unsigned, a byte is ultimately 8 bits. Just use a regular ByteBuffer and write your individual bytes in it.
In C# as well in Java, 1000 0000 (for instance) is exactly the same binary representation of a byte; the fact that in C# it can be treated as an unsigned value, and not in Java, is irrelevant as long as you don't do any arithmetic on the value.
When you need a readable representation of it and you'd like it to be unsigned, you can use (int) (theByte & 0xff) (you need the mask, otherwise casting will "carry" the sign bit).
Or, if you use Guava, you can use UnsignedBytes.toString().

BlockCopy alternative that allows me to choose endianness

Say I have an array of bytes:
byte[] input = { 0xFF, 0xFc, 0x00, 0x00 }
You can use Buffer.BlockCopy to copy bytes from one array to another, regardless of type. So, I can do this:
uint[] output = new uint[1];
Buffer.BlockCopy(input , 0, output, 0, input.Length);
This will copy the bytes from input to output, converting them from an array of bytes to an array of uints along the way.
The problem is that BlockCopy interprets the bytes with little endianness. I need a copy that uses big endianness. So rather than getting a uint value of 4294705152 (0xFFFC0000), like I need, I get the value 64767 (0x0000FCFF). Note that this is a bare-bones example, I cannot easily reverse the order of the bytes in my actual application.
Is there anything with the convenience and speed of BlockCopy that includes the ability to set the endianness I need?
The topic seems to be covered here:
How to get little endian data from big endian in c# using bitConverter.ToInt32 method?
However the conversion has to be iterated on the entire input array.

Showing a +255 byte in a byte[]

I really hope someone can help me.
I have a single byte[] that has to show the amount of bytes in die byte[] to follow. Now my value is above 255. Is there a way to display/enter a large number?
A byte holds a value from 0 to 255. To represent 299, you either have to use 2 bytes, or use a scheme (which the receiver will have to use as well) where the value in the byte is interpreted as more than its nominal value in order to expand the possible range of values. For instance, the value could be the length / 2. This would allow lengths of 0 - 510, but would allow only even lengths (odd length arrays would need a pad byte).
You can use two (or more) bytes to represent a number larger than 255. Is that what you want ?
short value = 2451;
byte[] data = BitConverter.GetBytes(value);
If this is needed in order to exchange data with some external system, remember to read about Endianness.
That depends on what you consider a good approach. You can perform some form of encoding to allow you store larger than 2 bytes worth of data. I.e. perhaps setting the first byte as 0xFF means you will consider the next byte as part of its data.
[0x01,0x0A,0xFF,0x0A]
Would be interpreted as 3 values of [1,10,265]

How to force BinaryReader to read this array correctly?

I have a byte array, received from java application via network.
I need to parse this array correctly. The format is as follow:
int - first for bytes
int - next for bytes
string - rest of data.
Take a look at this screenshot.
As you can see, the first four bytes is 0, 0, 0, 1. The actual integer value should be 1, but when I read it through BinaryReader.ReadInt32, I receive 16777216. I doubt this has something to do with byte endianness (but could be wrong), but I don't know how to fix it.
Any help would be appreciated.
Thanks
Yes, that's an endian issue. This question has examples of how to reverse the byte-order of integer types

MemoryStream: why convert to byte after readByte

In this example from MS, you'll notice that after we read a byte from memory stream, it goes into an int which must then be converted to byte. It stikes me as strange that a function like .ReadByte() doesn't return a byte in the first place. Is there a reason why MS did it this way?
// Read the remaining bytes, byte by byte.
while(count < memStream.Length)
{
byteArray[count++] =
Convert.ToByte(memStream.ReadByte());
}
a thought occured to me. Perhaps this comes down to usage. Perhaps ReadByte() is often used to retrieve short lengths, which subsequents get consumed in the retrieve via length variety
int length=ms.ReadByte();
ms.Read(buf,0,lenth);
i.e. you can use the length without a cast. Is this a good enough reason?
This is not specific to Memory stream, rather it is because of the design of base class "Stream" and the reason for that is
Return value:
The unsigned byte cast to an Int32, or -1 if at the end of the stream.
-1 cannot be represented using unsigned byte
When you use ReadByte If the read is successful then the current position within the stream is advanced by one byte. but its designed to return -1 if the end of the stream has been reached.
Now this would not be a valid value for Byte (its unsigned)
ms.Read(buf,0,lenth); here lenth is the number of bytes to read from the stream and what you get from ReadByte is first byte its not be used in the this fashion, something like
byte[] buff = new byte[ms.Length];
ms.Read(buff , 0, buff .Length);
I do believe they are converting with that from int to byte in a reallllllly nice way, since ReadByte() returns an int and their byteArray is of type int[].

Categories

Resources