tags:

views:

257

answers:

4

Hi,
is there a 24Bit primitive integral datatype in C++?

If there is none, would it be possible to create a class int24 (, uint24 ) ?

it's purpose could be:
* manipulating soundfiles in 24 bit format
* manipulating bitmapdata without alphachannel

many thanks in advance

Oops

+4  A: 

I wrote this to help me with audio manipulation. Its not the fastest but it works for me :)

const int INT24_MAX = 8388607;

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

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

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

    operator int() const
    {
        if ( m_Internal[2] & 0x80 ) // Is this a negative?  Then we need to siingn extend.
        {
            return (0xff << 24) | (m_Internal[2] << 16) | (m_Internal[1] << 8) | (m_Internal[0] << 0);
        }
        else
        {
            return (m_Internal[2] << 16) | (m_Internal[1] << 8) | (m_Internal[0] << 0);
        }
    }

    operator float() const
    {
        return (float)this->operator int();
    }

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

        return *this;
    }

    Int24& operator =( const int input )
    {
        m_Internal[0]   = ((unsigned char*)&input)[0];
        m_Internal[1]   = ((unsigned char*)&input)[1];
        m_Internal[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 int val ) const
    {
        return Int24( (int)*this + val );
    }

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

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

    Int24 operator /( const int val ) const
    {
        return Int24( (int)*this / 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 )
    {
        *this   = *this + val;
        return *this;
    }

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

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

    Int24& operator /=( const int 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 );
    }

    /***********************************************/

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

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

    /***********************************************/
    /***********************************************/

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

    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 int val ) const
    {
        return (int)*this == val;
    }

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

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

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

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

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

    /***********************************************/
    /***********************************************/
};
Goz
this looks very nice, thanks.you have also bit shifting operators, great stuff!1 question: how to use the class to store the data in a, or to read from a stream? would it be possible to use the << and >> operator too?
Oops
@Oops: I think not, since there are two possible implicit casts of Int24 - to int and to float, and the compiler has no way of choosing which cast to perform, and then use the result with `ostream::operator<<`. But you can easily add operators << and >> for ostreams and istreams yourself...
conio
As conio says if you want stream support you'll need to add the implementations yourself. Its not particularly difficult though :)
Goz
@Goz, could you please give a short example how this must look like? thanks
Oops
+3  A: 

No - all you can really do is:

typedef int32_t int24_t;

which helps to make code/intent more readable/obvious, but doesn't impose any limits on range or storage space.

Paul R
thanks for your post. But wouldn't it be very memory comsumable when reading a huge file? because 24bit raw audio is very huge at all.
Oops
@Oops: you probably want to unpack your 24 bit data to 32 bit when yo read it, otherwise you will need to pack/unpack samples on misaligned boundaries on-the-fly. The only reason to keep the data in its packed form would be if you were *really* pushed for memory.
Paul R
@Paul: The best reason for leaving the sounddata packed as it is, is just playing the sound, isn't it?
Oops
@Oops: it depends on what API you are using for sound playback and what formats it supports. If all you want to do is play the sound then keep it packed. If you want to do any processing of the sound data then it's probably going to be easier and more efficient if it's unpacked.
Paul R
+6  A: 

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

struct int24{
    unsigned int data : 24;
};

Or, if a separation is easier, just use 3 bytes (chars).

Btw, both use cases you mention in the question generally use 32bit integers. In the case of audio processing you'll generally convert to 32 bit ints (or floats, preferably, to prevent overflow situations you'd get with fixed point or integer math) when loading in chunks of audio because you're not going to have the entire file in memory at once.

For image data, people just tend to use 32 bit integers and ignore the alpha 8 alpha bits all together, or if you're dealing with a tightly packed format you're probably better of just manipulating them as char-pointers anyway because you'll have all channels separate. It's going to be a performance/memory trade-off anyway because writing one int is generally faster than three chars separately; however it will take 25% more memory.

Packing structs like this is compiler specific. However, in Visual Studio you'd do the following to make the struct exactly 24 bits.

#pragma pack(push, 1)
struct int24{
    unsigned int data : 24;
}
#pragma pack(pop)
Jasper Bekkers
it would be still padded to 32bits in most cases.
Yossarian
Most compilers allow you to turn off the padding to enable these tightly packed formats.
Jasper Bekkers
could you explain how to "turn off padding" / "create a packed struct" ? maybe you could modify your code
Oops
@Oops I updated the answer with the correct code sample for Visual Studio, however your compiler might do this differently.
Jasper Bekkers
+1  A: 

Working with anything smaller than an integer (32 or 64 bit depending on your architecture) is not ideal. All CPU operations of the smaller data types (short, etc) are done using integer arithmetic. Conversion to and from the CPU has to be done, slowing your application down (even if it is just a tad).

My advice: Store them as 32 (or 64 bit) integers to improve your overall speed. When it comes time to do I/O, then you'll have to do the conversion yourself.

As far as manipulating audio data, there are many libraries available that take care of the I/O for you - unless you want to start learning how PCM, etc are stored - as well as other DSP functions. I would suggest using one of the many libraries out there.

Nicolas