Как определить 24-битный тип данных в C? - PullRequest
15 голосов
/ 14 сентября 2011

Мне придется много работать с 24-битными аудиоданными.Это просто беззнаковое целое, но вместо 32-битного - 24-битное.Итак, как проще определить и работать с 24-битными данными в C?

Ответы [ 7 ]

13 голосов
/ 14 сентября 2011

В зависимости от требований, я бы использовал для этого битовое поле:

struct int24{
    unsigned int data : 24;
};

Или, если разделение проще, просто используйте 3 байта (unsigned char с).Вы можете принудительно упаковать структуру, если не хотите, чтобы она была дополнена.

[edit: я вижу, что тег C++ был удален, но я оставляю его здесь независимо от того] Если вы 'Для удобства работы с C ++ вы можете использовать что-то вроде следующего:

const int INT24_MAX = 8388607;

class Int24
{
protected:
    unsigned char value[3];
public:
    Int24(){}

    Int24( const Int24& val )
    {
        *this   = val;
    }

    operator int() const
    {
        /* Sign extend negative quantities */
        if( value[2] & 0x80 ) {
            return (0xff << 24) | (value[2] << 16)
                                | (value[1] <<  8)
                                |  value[0];
        } else {
            return (value[2] << 16)
                 | (value[1] <<  8)
                 |  value[0];
        }
    }

    Int24& operator= (const Int24& input)
    {
        value[0]   = input.value[0];
        value[1]   = input.value[1];
        value[2]   = input.value[2];

        return *this;
    }

    Int24& operator= (const int input)
    {
        value[0]   = ((unsigned char*)&input)[0];
        value[1]   = ((unsigned char*)&input)[1];
        value[2]   = ((unsigned char*)&input)[2];

        return *this;
    }

    Int24 operator+ (const Int24& val) const
    {
        return Int24( (int)*this + (int)val );
    }

    Int24 operator- (const Int24& val) const
    {
        return Int24( (int)*this - (int)val );
    }

    Int24 operator* (const Int24& val) const
    {
        return Int24( (int)*this * (int)val );
    }

    Int24 operator/ (const Int24& val) const
    {
        return Int24( (int)*this / (int)val );
    }

    Int24& operator+= (const Int24& val)
    {
        *this   = *this + val;
        return *this;
    }

    Int24& operator-= (const Int24& val)
    {
        *this   = *this - val;
        return *this;
    }

    Int24& operator*= (const Int24& val)
    {
        *this   = *this * val;
        return *this;
    }

    Int24& operator/= (const Int24& val)
    {
        *this   = *this / val;
        return *this;
    }

    Int24 operator>> (const int val) const
    {
        return Int24( (int)*this >> val );
    }

    Int24 operator<< (const int val) const
    {
        return Int24( (int)*this << val );
    }

    operator bool() const
    {
        return (int)*this != 0;
    }

    bool operator! () const
    {
        return !((int)*this);
    }

    Int24 operator- ()
    {
        return Int24( -(int)*this );
    }

    bool operator== (const Int24& val) const
    {
        return (int)*this == (int)val;
    }

    bool operator!= (const Int24& val) const
    {
        return (int)*this != (int)val;
    }

    bool operator>= (const Int24& val) const
    {
        return (int)*this >= (int)val;
    }

    bool operator<= (const Int24& val) const
    {
        return (int)*this <= (int)val;
    }

    /* Define all operations you need below.. */
5 голосов
/ 14 сентября 2011

Чистый и портативный способ, при условии, что ваши образцы имеют порядок байтов и не имеют знака:

static inline uint32_t getsample(uint8_t *buf, size_t pos)
{
    return buf[3*pos] + 256UL*buf[3*pos+1] + 65536UL*buf[3*pos+2];
}

static inline void putsample(uint8_t *buf, size_t pos, uint32_t sample)
{
    buf[3*pos]=sample;
    buf[3*pos+1]=sample/256;
    buf[3*pos+2]=sample/65536;
}

Исправление его для работы со знаковыми значениями немного больше работы, особенно если вы хотите сохранить его переносимым. Обратите внимание, что производительность может быть намного выше, если вы преобразуете все окно сэмплов в более разумный формат перед его обработкой, а затем преобразуете обратно, когда закончите.

0 голосов
/ 29 декабря 2017

Я делаю это для обработки как 24-разрядных целых чисел со знаком, так и без знака, просто добавляя / вычитая перед упаковкой и после распаковки, вот так:

void int24_write(uint8_t* bytes, int32_t val)
{
    // Add to make the value positive.
    val += (1 << 23);

    // Make sure the value is in an acceptable range of
    // [-2^23, +2^23).
    assert(val >= 0 && val < (1 << 24));

    // Pack the data from 32-bit to 24-bit.
    bytes[0] = (uint8_t)(val & 0xff);
    bytes[1] = (uint8_t)((val >> 8) & 0xff);
    bytes[2] = (uint8_t)(val >> 16);
}

int32_t int24_read(const uint8_t* bytes)
{
    // Unpack the data from 24-bit to 32-bit.
    return (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16)) - 
           (1 << 23);
}

Не уверен насчет оптимальной скорости, ноэто просто и без ветвей.Может упаковывать и распаковывать целые числа в диапазоне: [-2^23, +2^23).Если у кого-то есть предложения о том, как сделать это более эффективно, я был бы всем заинтересован.

Я склонен использовать его таким образом, чтобы данные для упаковки были достаточно маленькими, в среднем всего 3 или 4 целых числа(многие крошечные массивы сжимаются с 32-битных до 24-битных или менее), и обычно у нас есть только 3 или 4 целых числа для упаковки / распаковки за раз с, к сожалению, шаблоном произвольного доступа для извлечения этих небольших 24-битных массивов.Тем не менее, я имею тенденцию иметь несколько пакетов для распаковки / распаковки за раз, но опять же просто небольшое число, например 3 или 4, а не десятки, сотни или более.

Если вам нужен только неподписанный, то просто:

void uint24_write(uint8_t* bytes, uint32_t val)
{
    // Make sure the value is in an acceptable range of
    // [0, +2^24).
    assert(val >= 0 && val < (1 << 24));

    // Pack the data from 32-bit to 24-bit.
    bytes[0] = (uint8_t)(val & 0xff);
    bytes[1] = (uint8_t)((val >> 8) & 0xff);
    bytes[2] = (uint8_t)(val >> 16);
}

uint32_t uint24_read(const uint8_t* bytes)
{
    // Unpack the data from 24-bit to 32-bit.
    return (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16));
}

В этом случае целые числа без знака могут находиться в диапазоне [0, +2^24).

0 голосов
/ 24 сентября 2017

Эта работа для меня:

typedef unsigned char UInt24[3];
0 голосов
/ 14 сентября 2011

Что-то в этом роде:

struct Uint24
{
    unsigned char bits[3]; // assuming char is 8 bits

    Uint24()
        : bits()
    {}

    Uint24(unsigned val) {
        *this = val;
    }

    Uint24& operator=(unsigned val) {
        // store as little-endian
        bits[2] = val >> 16 & 0xff;
        bits[1] = val >> 8 & 0xff;
        bits[0] = val & 0xff;
        return *this;
    }

    unsigned as_unsigned() const {
        return bits[0] | bits[1] << 8 | bits[2] << 16;
    }
};
0 голосов
/ 14 сентября 2011

Используйте тип данных, достаточно большой, чтобы вместить 24 бита данных. Это int32_t или uint32_t, оба определены в stdint.h

Вы работаете с аудиоданными, поэтому вы хотите, чтобы дополнение работало (оно вам нужно для микширования). Кроме того, наличие дополнительных 8 битов - неплохая вещь, так как дает вам около 24 дБ дополнительного динамического диапазона, который вам понадобится для смешивания нескольких источников, которые полностью используют 24-битный динамический диапазон.

Обратите внимание, что вы просто спрашивали о типе данных, который будет использоваться для 24-битных выборок. Если вы собираетесь читать плотно упакованный поток 24-битных сэмплов, вам придется разбить его на части. Сначала вы должны знать, является ли поток старшим порядковым числом или младшим порядковым номером . Затем вы можете использовать что-то вроде этого, чтобы перегруппировать поток в сэмплы:

uint32_t bitstream_BE_to_sample(uint8_t bits[3])
{
    return (bits[0] << 16) | (bits[1] << 8) | (bits[2]);
}

uint32_t bitstream_LE_to_sample(uint8_t bits[3])
{
    return (bits[2] << 16) | (bits[1] << 8) | (bits[0]);
}

uint8_t *bitstream;
uint32_t *samples;
for(;;) {
    *samples = bitstream_XX_to_sample(bitstream);
    samples++;
    bistream += 3;

    if(end_of_bitstream())
         break;
}
0 голосов
/ 14 сентября 2011

Вы можете сделать что-то вроде этого;

union u32to24 {
    unsigned int i;
    char c[3];
};

в вашем коде манипулировать значениями, используя u32to24.i, например,

u32to24 val = //...

val.i += foo
val.i -= bar
// etc

, затем вывести символы для получения 24 битов

fprintf("%c%c%c",val.c[0],val.c[1],val.c[2]);

Proviso: это первое, что приходит мне в голову, поэтому могут быть ошибки, и вполне могут быть лучшие способы сделать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...