如何在C中定义24位数据类型?

我将不得不使用24位音频数据。 这只是unsigned int而不是32bit,它是24bit。 那么在C中定义和使用24位数据的简单方法是什么?

根据要求,我要么使用位域:

struct int24{ unsigned int data : 24; }; 

或者,如果分离更容易,只需使用3个字节( unsigned char s)。 如果您不希望填充结构,可以强制打包结构。

[编辑:我看到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.. */ 

干净,便携的方式,假设您的样本是小端和无符号的:

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

修复它以便为签名值工作它会更加有效,特别是如果你想保持它的可移植性。 请注意,如果在处理之前将整个样本窗口转换为saner格式,然后在完成后转换回来,性能可能会好得多。

这些方面的东西:

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

使用足够大的数据类型来容纳24位数据。 这是在stdint.h中定义的int32_tuint32_t

您正在处理音频数据,因此您需要进行额外的工作(需要它才能进行混音)。 还有一些额外的8位可用并不是坏事,因为它为您提供大约24dB的额外动态范围,您需要混合多个充分利用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; } 

你可以这样做;

 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:这是第一个进入我脑海的事情,因此可能存在错误,并且可能有更好的方法来实现这一点。

这项工作对我来说:

 typedef unsigned char UInt24[3]; 

我这样做来处理有符号和无符号的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)