Writing a double array to a wave file in c# - c#

I'm trying to write a wave file from scratch using c#. I managed to write 16 bit samples with no issue. But when it comes to 24 bit, apparently all bets are off.
I tried various ways of converting an int to a 3-byte array, which I would proceed to write to the data chunk L-L-L-R-R-R (as its a 24 bit stereo PCM wav).
For the 16bit part, I used this to generate the samples:
//numberOfBytes = 2 - for 16bit. slice = something like 2*Math.Pi*frequency/samplerate
private static byte[,] BuildByteWave(double slice, int numberOfBytes=2)
{
double dataPt = 0;
byte[,] output = new byte[Convert.ToInt32(Samples),numberOfBytes];
for (int i = 0; i < Samples; i++)
{
dataPt = Math.Sin(i * slice) * Settings.Amplitude;
int data = Convert.ToInt32(dataPt * Settings.Volume * 32767);
for (int j = 0; j < numberOfBytes; j++)
{
output[i, j] = ExtractByte(data, j);
}
}
return output;
}
This returns an array I later use to write to the data chunk like so
writer.WriteByte(samples[1][0]); //write to the left channel
writer.WriteByte(samples[1][1]); //write to the left channel
writer.WriteByte(samples[2][0]); //now to the second channel
writer.WriteByte(samples[2][1]); //and yet again.
Where 1 and 2 represent a certain sine wave.
However, if I tried the above with numberOfBytes = 3, it fails hard. The wave is a bunch of non-sense. (the header is formatted correctly).
I understood that I need to convert int32 to int24 and that I need to "pad" the samples, but I found no concrete 24bit tutorial anywhere.
Could you please point me in the right direction?
Edited for clarity.

There is no int24 - you will need to do it yourself. for/switch is a bit of an anti-pattern, too.
int[] samples = /* samples scaled to +/- 8388607 (0x7f`ffff) */;
byte[] data = new byte[samples.Length * 3];
for (int i = 0, j = 0; i < samples.Length; i++, j += 3)
{
// WAV is little endian
data[j + 0] = (byte)((i >> 0) & 0xff);
data[j + 1] = (byte)((i >> 8) & 0xff);
data[j + 2] = (byte)((i >> 16) & 0xff);
}
// data now has the 24-bit samples.
As an example, here's a program (Github) which generates a 15 second 44.1kHz 24-bit stereo wav file with 440 Hz in the left channel and 1 kHz in the right channel:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace WavGeneratorDemo
{
class Program
{
const int INT24_MAX = 0x7f_ffff;
static void Main(string[] args)
{
const int sampleRate = 44100;
const int lengthInSeconds = 15 /* sec */;
const int channels = 2;
const double channelSamplesPerSecond = sampleRate * channels;
var samples = new double[lengthInSeconds * sampleRate * channels];
// Left is 440 Hz sine wave
FillWithSineWave(samples, channels, channelSamplesPerSecond, 0 /* Left */, 440 /* Hz */);
// Right is 1 kHz sine wave
FillWithSineWave(samples, channels, channelSamplesPerSecond, 1 /* Right */, 1000 /* Hz */);
WriteWavFile(samples, sampleRate, channels, "out.wav");
}
private static void WriteWavFile(double[] samples, uint sampleRate, ushort channels, string fileName)
{
using (var wavFile = File.OpenWrite(fileName))
{
const int chunkHeaderSize = 8,
waveHeaderSize = 4,
fmtChunkSize = 16;
uint samplesByteLength = (uint)samples.Length * 3u;
// RIFF header
wavFile.WriteAscii("RIFF");
wavFile.WriteLittleEndianUInt32(
waveHeaderSize
+ chunkHeaderSize + fmtChunkSize
+ chunkHeaderSize + samplesByteLength);
wavFile.WriteAscii("WAVE");
// fmt header
wavFile.WriteAscii("fmt ");
wavFile.WriteLittleEndianUInt32(fmtChunkSize);
wavFile.WriteLittleEndianUInt16(1); // AudioFormat = PCM
wavFile.WriteLittleEndianUInt16(channels);
wavFile.WriteLittleEndianUInt32(sampleRate);
wavFile.WriteLittleEndianUInt32(sampleRate * channels);
wavFile.WriteLittleEndianUInt16((ushort)(3 * channels)); // Block Align (stride)
wavFile.WriteLittleEndianUInt16(24); // Bits per sample
// samples data
wavFile.WriteAscii("data");
wavFile.WriteLittleEndianUInt32(samplesByteLength);
for (int i = 0; i < samples.Length; i++)
{
var scaledValue = DoubleToInt24(samples[i]);
wavFile.WriteLittleEndianInt24(scaledValue);
}
}
}
private static void FillWithSineWave(double[] samples, int channels, double channelSamplesPerSecond, int channelNo, double freq)
{
for (int i = channelNo; i < samples.Length; i += channels)
{
var t = (i - channelNo) / channelSamplesPerSecond;
samples[i] = Math.Sin(t * (freq * Math.PI * 2));
}
}
private static int DoubleToInt24(double value)
{
if (value < -1 || value > 1)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
return (int)(value * INT24_MAX);
}
}
static class StreamExtensions
{
public static void WriteAscii(this Stream s, string str) => s.Write(Encoding.ASCII.GetBytes(str));
public static void WriteLittleEndianUInt32(this Stream s, UInt32 i)
{
var b = new byte[4];
b[0] = (byte)((i >> 0) & 0xff);
b[1] = (byte)((i >> 8) & 0xff);
b[2] = (byte)((i >> 16) & 0xff);
b[3] = (byte)((i >> 24) & 0xff);
s.Write(b);
}
public static void WriteLittleEndianInt24(this Stream s, Int32 i)
{
var b = new byte[3];
b[0] = (byte)((i >> 0) & 0xff);
b[1] = (byte)((i >> 8) & 0xff);
b[2] = (byte)((i >> 16) & 0xff);
s.Write(b);
}
public static void WriteLittleEndianUInt16(this Stream s, UInt16 i)
{
var b = new byte[2];
b[0] = (byte)((i >> 0) & 0xff);
b[1] = (byte)((i >> 8) & 0xff);
s.Write(b);
}
}
}
Which generates:

Related

Crc64 LSB based reverse implementation

I have tried to implement LSB based reversal function. The code is in C#. There's a bug in reversal of the checksum. The function can only reverse the Crc64 checksum when it was computed with only 8 bytes original data. When I try to reverse the checksum through FixChecksum method, the middle 6 bytes are reversed, but the first & the last byte are reversed corrupted. Please inform what is wrong, what's needed to be implemented or fixed. I will appreciate any solution.
[UPDATED]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MYC;
using primaryMethodsCS_2;
using System.Globalization;
using UnitsFramework.Numerics;
using UnitsFramework;
namespace MYC
{
public class Crc64_LSB_Reverse
{
public const UInt64 POLY64REV = 0xD800000000000000; //0xffffffffffffff00;
public static ulong TOPBIT_MASK = 0x8000000000000000;
public static ulong LOWBIT_MASK = 0x0000000000000001;
public const ulong startxor = 0; //0xffffffffffffffff;
public const ulong FinalXor = 0; // 0xffffffffffffffff;
public UInt64[] CRCTable;
public UInt64[] revCRCTable;
public UInt64 crc = 0;
public Crc64_LSB_Reverse(UInt64 POLY = POLY64REV)
{
List<ulong> listforward = new List<ulong>();
List<ulong> listreverse = new List<ulong>();
for (int i = 0; i <= 255; i++)
{
List<ulong> forward = generateCrcTableConstants(new List<ulong>() { (UInt64)i }, POLY);
List<ulong> reverse = generateRevCrcTableConstants(new List<ulong>() { (UInt64)i }, POLY);
listforward.AddRange(forward);
listreverse.AddRange(reverse);
}
this.CRCTable = listforward.ToArray();
this.revCRCTable = listreverse.ToArray();
return;
}
public static List<UInt64> generateCrcTableConstants(List<UInt64> initialValues, UInt64 POLY)
{
List<UInt64> list = new List<ulong>();
for (int thisValue = 0; thisValue < initialValues.Count; thisValue++)
{
UInt64 currentValue = initialValues[thisValue];
UInt64 initialValue = currentValue;
currentValue <<= 56; // is valid for MSB forward table creation
// MSB based forward table implementation.
for (byte bit = 0; bit < 8; bit++)
{
if ((currentValue & TOPBIT_MASK) != 0)
{
//currentValue <<= 1;
//currentValue ^= CrcFramework.Reflect64(POLY);
currentValue = (currentValue << 1) ^ ((0 - (currentValue >> 63)) & POLY); // fwd
}
else
{
currentValue <<= 1;
}
}
list.Add(currentValue);
}
return list;
}
public static List<UInt64> generateRevCrcTableConstants(List<UInt64> initialValues, UInt64 POLY)
{
List<UInt64> list = new List<ulong>();
for (int thisValue = 0; thisValue < initialValues.Count; thisValue++)
{
UInt64 initialValue = initialValues[thisValue];
UInt64 currentValue = initialValues[thisValue];
// LSB based reverse table implementation for MSB based forward table function.
for (byte bit = 0; bit < 8; bit++)
{
if ((currentValue & LOWBIT_MASK) != 0)
{
//currentValue ^= POLY; // CrcFramework.Reflect64(POLY); //POLY;
currentValue = (currentValue >> 1) ^ ((0 - (currentValue & 1)) & POLY); // rvs
//currentValue >>= 1;
//currentValue |= 1; // TOPBIT_MASK;
}
else
{
currentValue >>= 1;
}
}
list.Add(currentValue);
}
return list;
}
public ulong Compute_LSB(byte[] bytes, bool reset = true)
{
if (reset) this.crc = startxor;
foreach (byte b in bytes)
{
byte curByte = b;
/* update the LSB of crc value with next input byte */
crc = (ulong)(crc ^ (ulong)(curByte));
/* this byte value is the index into the lookup table */
byte pos = (byte)(crc & 0xFF); // tushar: original 12-September-2019-1: & 0xFF);
/* shift out this index */
crc = (ulong)(crc >> 8);
/* XOR-in remainder from lookup table using the calculated index */
crc = (ulong)(crc ^ (ulong)CRCTable[pos]);
/* shorter:
byte pos = (byte)((crc ^ curByte) & 0xFF);
crc = (ulong)((crc >> 8) ^ (ulong)(crcTable[pos]));
*/
}
return (ulong)(crc ^ FinalXor);
}
public ulong Compute_MSB(byte[] bytes, bool reset = true)
{
if (reset) this.crc = startxor;
foreach (byte b in bytes)
{
byte curByte = b;
/* update the MSB of crc value with next input byte */
crc = (ulong)(crc ^ (ulong)((ulong)curByte << 56));
/* this MSB byte value is the index into the lookup table */
byte pos = (byte)(crc >> 56);
/* shift out this index */
crc = (ulong)(crc << 8);
/* XOR-in remainder from lookup table using the calculated index */
crc = (ulong)(crc ^ (ulong)CRCTable[pos]);
/* shorter:
byte pos = (byte)((crc ^ (curByte << 56)) >> 56);
crc = (uint)((crc << 8) ^ (ulong)(crcTable[pos]));
*/
}
return (ulong)(crc ^ FinalXor);
}
public UInt64 FixChecksum(byte[] bytes, Int64 length, Int64 fixpos, UInt64 wantcrc)
{
if (fixpos + 8 > length) return 0;
UInt64 crc = startxor;
for (Int64 i = 0; i < fixpos; i++)
{
crc = (crc >> 8) ^ CRCTable[(crc ^ bytes[i]) & 0xff];
}
Array.Copy(BitConverter.GetBytes(crc), 0, bytes, fixpos, 8);
List<UInt64> list = new List<UInt64>();
crc = wantcrc ^ startxor;
for (Int64 i = length - 1; i >= fixpos; i--)
{
UInt64 param0 = (UInt64)(crc >> 56);
list.Add(param0);
crc = (crc << 8) ^ revCRCTable[param0] ^ bytes[i]; //
}
Array.Copy(BitConverter.GetBytes(crc), 0, bytes, fixpos, 8);
return crc;
}
}
}
The solution to this problem involves reverse cycling a CRC.
If using tables, for CRC generation (forward cycling), a left shifting CRC uses the left byte of the CRC to index a table, and a right shifting CRC uses the right byte of the CRC to index a table. For CRC reverse cycling, a left shifting CRC uses the right byte of the CRC to index a table, and a right shifting CRC uses the left byte of the CRC to index a table.
Example code below for both left shifting and right shifting CRCs. Two additional tables are used for reverse cycling CRCs. The CRC functions take in a data length parameter, in case the buffer has additional space to store the CRC after generating it.
To simplify the reverse cycling functions, a CRC is generated and xor'ed with the wanted CRC, that can be used with an imaginary buffer of zeroes, to generate the 8 bytes of data needed to produce the xor'ed CRC. The 8 bytes of data are then xor'ed to the original buffer. The xor's cancel out the CRC initial and xor out values, so the reverse cycling functions don't have to take those values into account.
Note that the CRC bytes can go anywhere in a message (although they are usually at the end of a message). For example a 24 byte message consisting of 8 bytes of data, 8 bytes of CRC, 8 bytes of data, which can be created using crc64fw(0ul, bfr, 24, 8); or crc64rw(0ul, bfr, 24, 8);.
namespace crc64
{
public class crc64
{
public const ulong poly64f = 0x000000000000001bul;
public const ulong poly64r = 0xd800000000000000ul;
public const ulong crcin = 0x0000000000000000ul; // initial value
public const ulong xorot = 0x0000000000000000ul; // xorout
public static ulong[] crctblf; // fwd tbl
public static ulong[] crctblg; // fwd tbl reverse cycle
public static ulong[] crctblr; // ref tbl
public static ulong[] crctbls; // ref tbl reverse cycle
// generate tables
public static void gentbls()
{
ulong crc;
byte b;
b = 0;
do
{
crc = ((ulong)b)<<56;
for(int i = 0; i < 8; i++)
crc = (crc<<1)^((0-(crc>>63))&poly64f);
crctblf[b] = crc;
b++;
}while (b != 0);
do
{
crc = ((ulong)b) << 0;
for (int i = 0; i < 8; i++)
crc = (crc<<63)^((crc^((0-(crc&1))&poly64f))>>1);
crctblg[b] = crc;
b++;
} while (b != 0);
do
{
crc = ((ulong)b)<<0;
for(int i = 0; i < 8; i++)
crc = (crc>>1)^((0-(crc&1))&poly64r);
crctblr[b] = crc;
b++;
}while (b != 0);
do
{
crc = ((ulong)b) << 56;
for (int i = 0; i < 8; i++)
crc = (crc>>63)^((crc^((0-(crc>>63))&poly64r))<<1);
crctbls[b] = crc;
b++;
} while (b != 0);
}
// generate forward crc
public static ulong crc64f(byte[] bfr, int len)
{
ulong crc = crcin;
for(int i = 0; i < len; i++)
crc = (crc<<8)^(crctblf[(crc>>56)^bfr[i]]);
return (crc^xorot);
}
// append forward crc
public static void crc64fa(ulong crc, byte[] bfr, int pos)
{
bfr[pos+0] = (byte)(crc>>56);
bfr[pos+1] = (byte)(crc>>48);
bfr[pos+2] = (byte)(crc>>40);
bfr[pos+3] = (byte)(crc>>32);
bfr[pos+4] = (byte)(crc>>24);
bfr[pos+5] = (byte)(crc>>16);
bfr[pos+6] = (byte)(crc>> 8);
bfr[pos+7] = (byte)(crc>> 0);
}
// "fix" bfr to generate wanted forward crc
public static void crc64fw(ulong crc, byte[] bfr, int len, int pos)
{
crc ^= crc64f(bfr, len);
for(int i = pos; i < len; i++)
crc = (crc>>8)^(crctblg[crc&0xff]);
bfr[pos+0] ^= (byte)(crc>>56);
bfr[pos+1] ^= (byte)(crc>>48);
bfr[pos+2] ^= (byte)(crc>>40);
bfr[pos+3] ^= (byte)(crc>>32);
bfr[pos+4] ^= (byte)(crc>>24);
bfr[pos+5] ^= (byte)(crc>>16);
bfr[pos+6] ^= (byte)(crc>> 8);
bfr[pos+7] ^= (byte)(crc>> 0);
}
// generate reflected crc
public static ulong crc64r(byte[] bfr, int len)
{
ulong crc = crcin;
for(int i = 0; i < len; i++)
crc = (crc>>8)^(crctblr[(crc&0xff)^bfr[i]]);
return (crc^xorot);
}
// append reflected crc
public static void crc64ra(ulong crc, byte[] bfr, int pos)
{
bfr[pos+0] = (byte)(crc>> 0);
bfr[pos+1] = (byte)(crc>> 8);
bfr[pos+2] = (byte)(crc>>16);
bfr[pos+3] = (byte)(crc>>24);
bfr[pos+4] = (byte)(crc>>32);
bfr[pos+5] = (byte)(crc>>40);
bfr[pos+6] = (byte)(crc>>48);
bfr[pos+7] = (byte)(crc>>56);
}
// "fix" bfr to generate wanted reflected crc
public static void crc64rw(ulong crc, byte[] bfr, int len, int pos)
{
crc ^= crc64r(bfr, len);
for (int i = pos; i < len; i++)
crc = (crc<<8)^(crctbls[crc>>56]);
bfr[pos+0] ^= (byte)(crc>> 0);
bfr[pos+1] ^= (byte)(crc>> 8);
bfr[pos+2] ^= (byte)(crc>>16);
bfr[pos+3] ^= (byte)(crc>>24);
bfr[pos+4] ^= (byte)(crc>>32);
bfr[pos+5] ^= (byte)(crc>>40);
bfr[pos+6] ^= (byte)(crc>>48);
bfr[pos+7] ^= (byte)(crc>>56);
}
static void Main(string[] args)
{
crctblf = new ulong[256];
crctblg = new ulong[256];
crctblr = new ulong[256];
crctbls = new ulong[256];
byte[] bfr = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, // data (16 bytes)
0x38,0x39,0x61,0x62,0x63,0x64,0x65,0x66,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // space for crc
ulong crcf, crcr, crcw, crcg, crcs;
int dl = bfr.Length-8; // length of data
int bl = bfr.Length; // length of bfr
gentbls();
crcf = crc64f(bfr, dl); // forward crc
crc64fa(crcf, bfr, dl); // append crc
crcw = crc64f(bfr, bl); // crcw == 0
crcr = crc64r(bfr, dl); // reflected crc
crc64ra(crcr, bfr, dl); // append crc
crcw = crc64r(bfr, bl); // crcw == 0
Console.WriteLine(crcf.ToString("x16") + " " + crcr.ToString("x16"));
crcw = 0x0001020304050607ul; // wanted crc
crc64fw(crcw, bfr, dl, 8); // "fix" for forward
crcg = crc64f(bfr, dl); // crcg == crcw
crc64fw(crcf, bfr, dl, 8); // undo "fix" for forward (restore bfr)
crc64rw(crcw, bfr, dl, 8); // "fix" for reflected
crcs = crc64r(bfr, dl); // crcs == crcw
Console.WriteLine(crcg.ToString("x16") + " " + crcs.ToString("x16"));
}
}
}

Converting 32bit wav array to x-bit

I have a 32 bit wav array and I wanted to convert it to 8 bit
So I tried to take this function which converts 32 to 16
void _waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
byte[] newArray16Bit = new byte[e.BytesRecorded / 2];
short two;
float value;
for (int i = 0, j = 0; i < e.BytesRecorded; i += 4, j += 2)
{
value = (BitConverter.ToSingle(e.Buffer, i));
two = (short)(value * short.MaxValue);
newArray16Bit[j] = (byte)(two & 0xFF);
newArray16Bit[j + 1] = (byte)((two >> 8) & 0xFF);
}
}
And modify it to take x bit as destination
private byte[] Convert32BitRateToNewBitRate(byte[] bytes, int newBitRate)
{
var sourceBitRate = 32;
byte[] newArray = new byte[bytes.Length / (sourceBitRate / newBitRate)];
for (int i = 0, j = 0; i < bytes.Length; i += (sourceBitRate / 8), j += (newBitRate / 8))
{
var value = (BitConverter.ToSingle(bytes, i));
var two = (short)(value * short.MaxValue);
newArray[j] = (byte)(two & 0xFF);
newArray[j + 1] = (byte)((two >> 8) & 0xFF);
}
return newArray;
}
My problem is that I wasn't sure how to convert the code within the "for" loop, I tried to debug it but I couldn't quite figure out how it works.
I saw here : simple wav 16-bit / 8-bit converter source code?, that they divided the value by 256 to get from 16 to 8, I tried to divide by 256 to get from 32 to 16 but it didn't work
for (int i = 0, j = 0; i < bytes.Length; i += sourceBitRateBytes, j += newBitRateBytes)
{
var value = BitConverter.ToInt32(bytes, i);
value /= (int)Math.Pow(256, sourceBitRate / newBitRate / 2.0);
var valueBytes = BitConverter.GetBytes(value);
for (int k = 0; k < newBitRateBytes; k++)
{
newArray[k + j] = valueBytes[k];
}
The for loop is still using 16 bit in the following places:
short.MaxValue. Use byte.MaxValue instead.
by assigning two bytes at [j] and [j+1]. Assign one byte only.
I don't have the rest of the program and no sample data, so it's hard for me to try. But I'd say the following sounds about right
for (int i = 0, j = 0; i < bytes.Length; i += (sourceBitRate / 8), j += (newBitRate / 8))
{
var value = (BitConverter.ToSingle(bytes, i));
var two = (byte)(value * byte.MaxValue);
newArray[j] = two;
}
Be aware that this works for 8 bit only, so newBitRate must be 8, otherwise it does not work. It should probably not be a parameter to the method.

include two CRC16 bytes in a program ...?

i want to send some bytes via RS232 to a DSPIC33F that controls a robot motors, the DSPIC must receive 9 bytes orderly the last 2 bytes are for CRC16, am working in C#, so how can i calculate the CRC bytes meant to be sent.
the program that calculates the CRC16, i have found it in the internet :
using System;
using System.Collections.Generic;
using System.Text;
namespace SerialPortTerminal
{
public enum InitialCrcValue { Zeros, NonZero1 = 0xffff, NonZero2 = 0x1D0F }
public class Crc16Ccitt
{
const ushort poly = 4129;
ushort[] table = new ushort[256];
ushort initialValue = 0;
public ushort ComputeChecksum(byte[] bytes)
{
ushort crc = this.initialValue;
for (int i = 0; i < bytes.Length; i++)
{
crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
}
return crc;
}
public byte[] ComputeChecksumBytes(byte[] bytes)
{
ushort crc = ComputeChecksum(bytes);
return new byte[] { (byte)(crc >> 8), (byte)(crc & 0x00ff) };
}
public Crc16Ccitt(InitialCrcValue initialValue)
{
this.initialValue = (ushort)initialValue;
ushort temp, a;
for (int i = 0; i < table.Length; i++)
{
temp = 0;
a = (ushort)(i << 8);
for (int j = 0; j < 8; j++)
{
if (((temp ^ a) & 0x8000) != 0)
{
temp = (ushort)((temp << 1) ^ poly);
}
else
{
temp <<= 1;
}
a <<= 1;
}
table[i] = temp;
}
}
}
}
With the class you provided you would create the buffer of data that you want:
byte[] data = new byte[7];
data[0] = 1; // This example is just random numbers
data[1] = 12;
data[2] = 17;
data[3] = 9;
data[4] = 106;
data[5] = 12;
data[6] = 0;
Then calculate the checksum bytes:
Crc16Ccitt calculator = new Crc16Ccitt();
byte[] checksum = calculator.ComputeChecksumBytes(data);
Then either write the two parts of the data
port.Write(data);
port.Write(checksum);
Or build a packet to be written from the two parts:
byte[] finalData = new byte[9];
Buffer.BlockCopy(data, 0, finalData, 0, 7);
Buffer.BlockCopy(data, 7, checksum, 0, 2);
port.Write(finalData);
The class you've posted could be rewritten slightly to make it a bit more efficient and easier/cleaner to use but it should suffice as long as it calculates the CRC 16 in the same way as the device you're communicating with. If this doesn't work then you need to consult the documentation for the device, or ask the manufacturer for the details you need.

How to read IMediaSample 24 bit PCM data

I have the following method which collects PCM data from the IMediaSample into floats for the FFT:
public int PCMDataCB(IntPtr Buffer, int Length, ref TDSStream Stream, out float[] singleChannel)
{
int numSamples = Length / (Stream.Bits / 8);
int samplesPerChannel = numSamples / Stream.Channels;
float[] samples = new float[numSamples];
if (Stream.Bits == 32 && Stream.Float) {
// this seems to work for 32 bit floating point
byte[] buffer32f = new byte[numSamples * 4];
Marshal.Copy(Buffer, buffer32f, 0, numSamples);
for (int j = 0; j < buffer32f.Length; j+=4)
{
samples[j / 4] = System.BitConverter.ToSingle(new byte[] { buffer32f[j + 0], buffer32f[j + 1], buffer32f[j + 2], buffer32f[j + 3]}, 0);
}
}
else if (Stream.Bits == 24)
{
// I need this code
}
// compress result into one mono channel
float[] result = new float[samplesPerChannel];
for (int i = 0; i < numSamples; i += Stream.Channels)
{
float tmp = 0;
for (int j = 0; j < Stream.Channels; j++)
tmp += samples[i + j] / Stream.Channels;
result[i / Stream.Channels] = tmp;
}
// mono output to be used for visualizations
singleChannel = result;
return 0;
}
Seems to work for 32b float, because I get sensible data in the spectrum analyzer (although it seems too shifted(or compressed?) to the lower frequencies).
I also seem to manage to make it work for 8, 16 and 32 non float, but I can only read garbage when the bits are 24.
How can I adapt this to work with 24 bit PCM coming into Buffer?
Buffer comes from an IMediaSample.
Another thing I am wondering is if the method I use to add all channels to one by summing and dividing by the number of channels is ok...
I figured it out:
byte[] buffer24 = new byte[numSamples * 3];
Marshal.Copy(Buffer, buffer24, 0, numSamples * 3);
var window = (float)(255 << 16 | 255 << 8 | 255);
for (int j = 0; j < buffer24.Length; j+=3)
{
samples[j / 3] = (buffer24[j] << 16 | buffer24[j + 1] << 8 | buffer24[j + 2]) / window;
}
Creates a integer from the three bytes and then scales it into the range 1/-1 by dividing with the max value of three bytes.
Have you tried
byte[] buffer24f = new byte[numSamples * 3];
Marshal.Copy(Buffer, buffer24f, 0, numSamples);
for (int j = 0; j < buffer24f.Length; j+=3)
{
samples[j / 3] = System.BitConverter.ToSingle(
new byte[] {
0,
buffer24f[j + 0],
buffer24f[j + 1],
buffer24f[j + 2]
}, 0);
}

how to decompose integer array to a byte array (pixel codings)

Hi sorry for being annoying by rephrasing my question but I am just on the point of discovering my answer.
I have an array of int composed of RGB values, I need to decompose that int array into a byte array, but it should be in BGR order.
The array of int composed of RGB values is being created like so:
pix[index++] = (255 << 24) | (red << 16) | blue;
C# code
// convert integer array representing [argb] values to byte array representing [bgr] values
private byte[] convertArray(int[] array)
{
byte[] newarray = new byte[array.Length * 3];
for (int i = 0; i < array.Length; i++)
{
newarray[i * 3] = (byte)array[i];
newarray[i * 3 + 1] = (byte)(array[i] >> 8);
newarray[i * 3 + 2] = (byte)(array[i] >> 16);
}
return newarray;
}
#define N something
unsigned char bytes[N*3];
unsigned int ints[N];
for(int i=0; i<N; i++) {
bytes[i*3] = ints[i]; // Blue
bytes[i*3+1] = ints[i] >> 8; // Green
bytes[i*3+2] = ints[i] >> 16; // Red
}
Using Linq:
pix.SelectMany(i => new byte[] {
(byte)(i >> 0),
(byte)(i >> 8),
(byte)(i >> 16),
}).ToArray();
Or
return (from i in pix
from x in new[] { 0, 8, 16 }
select (byte)(i >> x)
).ToArray();
Try to use Buffer Class
byte[] bytes = new byte[ints.Length*4];
Buffer.BlockCopy(ints, 0, bytes, 0, ints.Length * 4);
r = (pix[index] >> 16) & 0xFF
the rest is similar, just change 16 to 8 or 24.

Categories