Подтвердить что ты не робот

Как определить 24-битный тип данных в C?

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

4b9b3361

Ответ 1

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

struct int24{
    unsigned int data : 24;
};

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

[edit: Я вижу, что тег 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.. */

Ответ 2

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

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

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

Ответ 3

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

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: это первое, что появляется в моей голове, поэтому могут быть ошибки, и, возможно, есть лучшие способы сделать это.

Ответ 4

Используйте тип данных, достаточно большой для хранения 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;
}

Ответ 5

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

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

Ответ 6

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

typedef unsigned char UInt24[3];

Ответ 7

То, как я это делаю для обработки как подписанных, так и неподписанных 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).