Ich werde viel mit 24-Bit-Audiodaten arbeiten müssen. Was ist nur unsigned int aber statt 32bit, es ist 24bit. Also, was ist der einfachere Weg zu definieren und arbeiten mit 24-Bit-Daten in C?
Antworten
Zu viele Anzeigen?Je nach Anforderung würde ich dafür entweder ein Bitfeld verwenden:
struct int24{
unsigned int data : 24;
};
Oder, wenn eine Trennung einfacher ist, verwenden Sie einfach 3 Bytes ( unsigned char
s). Sie können erzwingen, dass die Struktur gepackt wird, wenn Sie nicht wollen, dass sie aufgefüllt wird.
[edit: Ich sehe die C++
Tag wurde entfernt, aber ich lasse es trotzdem hier] Wenn Sie sich mit C++ besser auskennen, können Sie etwas wie das Folgende verwenden:
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.. */
Der saubere und portable Weg, vorausgesetzt, Ihre Samples sind Little Endian und 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;
}
Die Anpassung an die Signaturwerte erfordert etwas mehr Arbeit, vor allem, wenn Sie das System portabel halten wollen. Beachten Sie, dass die Leistung viel besser sein könnte, wenn Sie ein ganzes Fenster von Samples in ein vernünftigeres Format konvertieren, bevor Sie es verarbeiten, und dann zurück konvertieren, wenn Sie fertig sind.
Verwenden Sie einen Datentyp, der groß genug ist, um 24 Bit an Daten zu speichern. Das sind int32_t
o uint32_t
beide definiert in stdint.h
Da Sie mit Audiodaten arbeiten, wollen Sie, dass die Addition funktioniert (Sie brauchen sie zum Mischen). Außerdem ist es nicht schlecht, einige zusätzliche 8 Bit zur Verfügung zu haben, da man dadurch etwa 24 dB zusätzlichen Dynamikbereich erhält, den man beim Mischen mehrerer Quellen benötigt, die den 24-Bit-Dynamikbereich voll ausnutzen.
Beachten Sie, dass Sie gerade nach einem Datentyp für 24-Bit-Samples gefragt haben. Wenn Sie einen dicht gepackten Strom von 24-Bit-Sample-Frames lesen wollen, müssen Sie diesen aufteilen. Zuerst müssen Sie wissen, ob der Stream Big Endian o Low-Endian . Dann können Sie den Datenstrom mit einer ähnlichen Methode in Samples umwandeln:
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;
}
Etwas in dieser Art:
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;
}
};
Sie könnten etwa so vorgehen;
union u32to24 {
unsigned int i;
char c[3];
};
Geben Sie in Ihrem Code die Werte mit u32to24.i
zum Beispiel
u32to24 val = //...
val.i += foo
val.i -= bar
// etc
dann die Zeichen ausgeben, um die 24 Bits zu erhalten
fprintf("%c%c%c",val.c[0],val.c[1],val.c[2]);
Vorbehalt: Dies ist das erste, was mir in den Sinn kommt, daher kann es Fehler geben und es gibt vielleicht bessere Methoden, dies zu tun.