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?
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.
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.
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 char
s). 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.. */
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.
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With