在 C++ 中将 uint32_t 十六进制值的一部分拆分为更小的部分
Split parts of a uint32_t hex value into smaller parts in C++
我有一个uint32_t如下:
uint32_t midiData=0x9FCC00;
我需要将这个 uint32_t 分成更小的部分,以便 9 成为它自己的实体,F 成为它自己的实体,CC 成为它自己的实体。如果您想知道我在做什么,我正在尝试分解 MIDI 消息的各个部分,以便在我的程序中更容易管理它们。
我找到了 this solution,但问题是我不知道如何将它应用到 CC 部分,而且我不确定此方法是否适用于 C++。
这是我目前的情况:
uint32_t midiData=0x9FCC00;
uint32_t status = 0x0FFFFF & midiData; // Retrieve 9
uint32_t channel = (0xF0FFFF & midiData)>>4; //Retrieve F
uint32_t note = (0xFF00FF & midiData) >> 8; //Retrieve CC
这对 C++ 来说正确吗?我问的原因是因为我以前从未使用过 C++,它使用 > 和 < 的语法一直让我感到困惑(因此我倾向于避免使用它)。
你在某种程度上倒退了。
在布尔逻辑中(& 是按位与),与 0 将排除它。知道十六进制的 F
是二进制的 1111
,像 0x9FCC00 & 0x0FFFFF
这样的行会给你除 9 之外的所有十六进制数字,与你想要的相反。
因此,对于状态:
uint32_t status = 0xF000000 & midiData; // Retrieve 9
实际上,这会给你 0x900000。如果你想要 0x9(也是十进制的 9),你需要对结果进行位移。
现在,右移位运算符(例如,X >> 4)表示向右移动 X 4 位;除以 16。那是 4 位,而不是 4 个十六进制数字。 1 个十六进制数字 == 4 位,所以要从 0x900000 得到 9,你需要 0x900000 >> 20
.
因此,将它们放在一起,获得状态 9:
uint32_t status = (0xF000000 & midiData) >> 20;
类似的过程将为您提供所需的剩余值。
您也可以在 C++ 中使用位移运算符 >>
和位掩码运算符 &
。
但是,关于如何使用它存在一些问题:
运算符 v1 & v2
给出一个由 v1
和 v2
中设置的那些位构建的数字,例如,0x12 & 0xF0
给出 0x10
,而不是 0x02
。此外,位移运算符采用位数,十六进制数中的单个数字(通常称为半字节)由 4 位组成(0x0..0xF 需要 4 位)。所以,如果你有0x12
,想得到0x01
,就得写0x12 >>4
。
因此,您的班次也需要调整:
#define BITS_OF_A_NIBBLE 4
unsigned char status = (midiData & 0x00F00000) >> (5*BITS_OF_A_NIBBLE);
unsigned char channel = (midiData & 0x000F0000) >> (4*BITS_OF_A_NIBBLE);
unsigned char note = (midiData & 0x0000FF00) >> (2*BITS_OF_A_NIBBLE);
unsigned char theRest = (midiData & 0x000000FF);
一般来说,我建议先 shift,然后 mask - 这样不容易出错:
uint8_t cmd = (midiData >> 16) & 0xff;
uint8_t note = (midiData >> 8) & 0x7f; // MSB can't be set
uint8_t velocity = (midiData >> 0) & 0x7f; // ditto
然后拆分cmd
变量:
uint8_t status = (cmd & 0xf0); // range 0x00 .. 0xf0
uint8_t channel = (cmd & 0x0f); // range 0 .. 15
我个人不会费心将 status
值映射回范围 0 .. 15 - 通常理解为0x90
是 "note on",而不是普通值 9
。
我有一个uint32_t如下:
uint32_t midiData=0x9FCC00;
我需要将这个 uint32_t 分成更小的部分,以便 9 成为它自己的实体,F 成为它自己的实体,CC 成为它自己的实体。如果您想知道我在做什么,我正在尝试分解 MIDI 消息的各个部分,以便在我的程序中更容易管理它们。
我找到了 this solution,但问题是我不知道如何将它应用到 CC 部分,而且我不确定此方法是否适用于 C++。
这是我目前的情况:
uint32_t midiData=0x9FCC00;
uint32_t status = 0x0FFFFF & midiData; // Retrieve 9
uint32_t channel = (0xF0FFFF & midiData)>>4; //Retrieve F
uint32_t note = (0xFF00FF & midiData) >> 8; //Retrieve CC
这对 C++ 来说正确吗?我问的原因是因为我以前从未使用过 C++,它使用 > 和 < 的语法一直让我感到困惑(因此我倾向于避免使用它)。
你在某种程度上倒退了。
在布尔逻辑中(& 是按位与),与 0 将排除它。知道十六进制的 F
是二进制的 1111
,像 0x9FCC00 & 0x0FFFFF
这样的行会给你除 9 之外的所有十六进制数字,与你想要的相反。
因此,对于状态:
uint32_t status = 0xF000000 & midiData; // Retrieve 9
实际上,这会给你 0x900000。如果你想要 0x9(也是十进制的 9),你需要对结果进行位移。
现在,右移位运算符(例如,X >> 4)表示向右移动 X 4 位;除以 16。那是 4 位,而不是 4 个十六进制数字。 1 个十六进制数字 == 4 位,所以要从 0x900000 得到 9,你需要 0x900000 >> 20
.
因此,将它们放在一起,获得状态 9:
uint32_t status = (0xF000000 & midiData) >> 20;
类似的过程将为您提供所需的剩余值。
您也可以在 C++ 中使用位移运算符 >>
和位掩码运算符 &
。
但是,关于如何使用它存在一些问题:
运算符 v1 & v2
给出一个由 v1
和 v2
中设置的那些位构建的数字,例如,0x12 & 0xF0
给出 0x10
,而不是 0x02
。此外,位移运算符采用位数,十六进制数中的单个数字(通常称为半字节)由 4 位组成(0x0..0xF 需要 4 位)。所以,如果你有0x12
,想得到0x01
,就得写0x12 >>4
。
因此,您的班次也需要调整:
#define BITS_OF_A_NIBBLE 4
unsigned char status = (midiData & 0x00F00000) >> (5*BITS_OF_A_NIBBLE);
unsigned char channel = (midiData & 0x000F0000) >> (4*BITS_OF_A_NIBBLE);
unsigned char note = (midiData & 0x0000FF00) >> (2*BITS_OF_A_NIBBLE);
unsigned char theRest = (midiData & 0x000000FF);
一般来说,我建议先 shift,然后 mask - 这样不容易出错:
uint8_t cmd = (midiData >> 16) & 0xff;
uint8_t note = (midiData >> 8) & 0x7f; // MSB can't be set
uint8_t velocity = (midiData >> 0) & 0x7f; // ditto
然后拆分cmd
变量:
uint8_t status = (cmd & 0xf0); // range 0x00 .. 0xf0
uint8_t channel = (cmd & 0x0f); // range 0 .. 15
我个人不会费心将 status
值映射回范围 0 .. 15 - 通常理解为0x90
是 "note on",而不是普通值 9
。