Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define 24bit data type in C?

Tags:

c

I will have to work a lot with 24-bit audio data. Which is just unsigned int but instead of 32bit, it is 24bit. So what is the easier way to define and work with 24bit data in C?

like image 857
mary Avatar asked Sep 14 '11 12:09

mary


People also ask

What is 24 bit integer?

The range of unsigned integers that can be represented in 24 bits is 0 to 16,777,215 (FFFFFF16 in hexadecimal). The range of signed integers that can be represented in 24 bits is −8,388,608 to 8,388,607.

What is UInt24?

UInt24(UInt24) Creates 24-bit unsigned integer from an existing 24-bit unsigned integer. UInt24(Byte[], Int32) Creates 24-bit unsigned integer from three bytes at a specified position in a byte array.


3 Answers

Depending on the requirements I'd either use a bitfield for it:

struct int24{
    unsigned int data : 24;
};

Or, if a separation is easier, just use 3 bytes (unsigned chars). You can force the struct to be packed if you don't want it to be padded.

[edit: I see the C++ tag was removed, but I'm leaving it here regardless] If you're more comfortable with C++, you can use something like the following:

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.. */
like image 95
Michael Foukarakis Avatar answered Sep 21 '22 08:09

Michael Foukarakis


The clean and portable way, assuming your samples are little endian and 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;
}

Fixing it up to work for signed values it a little more work, especially if you want to keep it portable. Note that performance might be much better if you convert a whole window of samples to a saner format before processing it, then convert back when you're done.

like image 26
R.. GitHub STOP HELPING ICE Avatar answered Sep 17 '22 08:09

R.. GitHub STOP HELPING ICE


Use a data type that's large enough to hold 24 bits of data. That are int32_t or uint32_t both defined in stdint.h

You're working with audio data, so you want addition working (you need it for mixing). Also having some additional 8 bits available is not a bad thing, as it gives you about 24dB of extra dynamic range, which you'll need mixing several sources that make full use of the 24bit dynamic range.

Note that you were just asking about a datatype to use for 24 bit samples. If you're going to read a tighly packed stream of 24bit sample frames, you'll have to split that one up. First you must know if the stream is big endian or low endian. Then you can use something like this to rearrange the stream into samples:

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;
}
like image 41
datenwolf Avatar answered Sep 19 '22 08:09

datenwolf