Как читать 24-битные данные IMediaSample PCM

У меня есть следующий метод, который собирает данные PCM из IMediaSample в поплавки для БПФ:

    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;
    }

Кажется, работает для 32b с плавающей запятой, потому что я получаю разумные данные в анализаторе спектра (хотя они кажутся слишком сдвинутыми (или сжатыми?) к более низким частотам).

Кажется, мне также удалось заставить его работать для 8, 16 и 32 non float, но я могу читать мусор только тогда, когда биты равны 24.

Как я могу адаптировать это для работы с 24-битным PCM, поступающим в буфер?

Буфер поступает из IMediaSample.

Еще мне интересно, подходит ли метод, который я использую для добавления всех каналов в один путем суммирования и деления на количество каналов...


person Marino Šimić    schedule 10.02.2014    source источник


Ответы (2)


Я понял:

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;
}

Создает целое число из трех байтов, а затем масштабирует его до диапазона 1/-1 путем деления на максимальное значение в три байта.

person Marino Šimić    schedule 10.02.2014

Ты пытался

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);
}
person Mehmet Ataş    schedule 10.02.2014
comment
24 бита - это не плавающая точка. Это целочисленное аудио. Если что-то нужно сделать, так это выяснить, как составить целое число из буфера байтов, а затем преобразовать его в число с плавающей запятой, чтобы поместить его в массив образцов. - person Marino Šimić; 10.02.2014