Calculating CoreMIDI Pitch Bend Values For iOS?

送分小仙女□ 提交于 2019-11-30 16:56:43
Kurt Revis

I think your code is close to right, but it's overly complicated. This question has nothing to do with iOS or endianness or ARM or Intel; it's just plain old C bit-twiddling. If you write the code correctly, it will work on any reasonable platform without modification. You don't need a library; it's only a couple lines of code.

It's best to work with MIDI on a byte-by-byte basis. You want a function that takes a 16-bit unsigned integer (which we'll trust has at most 14 bits worth of value) and returns two single-byte values, one with the most significant bits, one with the least significant bits.

Later on, when you send the message, you assemble the bytes in the appropriate order. According to the specification, pitch wheel messages are three bytes: STATUS, then LSB, then MSB. You have them backwards in your question!

The least-significant 7 bits are easy: just mask off those bits from the original value. The most-significant 7 bits are similar: mask off the next higher 7 bits from the original value, then shift them down.

It doesn't matter whether the 16-bit integers are little-endian or big-endian in memory on your machine; the compiler takes care of that.

Here's a function and a test tool.

#include <stdio.h>
#include <stdint.h>  // for C standard uint8_t and uint16_t
// or, if you prefer, use unsigned char and unsigned short, or Byte and UInt16;
// they'll all work, although some are more portable than others

void encode14BitValue(uint16_t value, uint8_t *out_msb, uint8_t *out_lsb)
{
    uint16_t mask = 0x007F;  // low 7 bits on
                             // "(1 << 7) - 1" is arguably clearer
    *out_lsb = value & mask;
    *out_msb = (value & (mask << 7)) >> 7;
}

int main(int argc, const char * argv[])
{
    typedef struct {
        uint16_t in;
        uint8_t expected_msb;
        uint8_t expected_lsb;
    } test_case;

    test_case cases[] = {
        { 0x0000, 0x00, 0x00 },
        { 0x0001, 0x00, 0x01 },
        { 0x0002, 0x00, 0x02 },
        { 0x0004, 0x00, 0x04 },
        { 0x0008, 0x00, 0x08 },
        { 0x0009, 0x00, 0x09 },
        { 0x000F, 0x00, 0x0F },
        { 0x0010, 0x00, 0x10 },
        { 0x0011, 0x00, 0x11 },
        { 0x001F, 0x00, 0x1F },
        { 0x0020, 0x00, 0x20 },
        { 0x0040, 0x00, 0x40 },
        { 0x0070, 0x00, 0x70 },
        { 0x007F, 0x00, 0x7F },
        { 0x0080, 0x01, 0x00 },
        { 0x0081, 0x01, 0x01 },
        { 0x008F, 0x01, 0x0F },
        { 0x0090, 0x01, 0x10 },
        { 0x00FF, 0x01, 0x7F },
        { 0x0100, 0x02, 0x00 },
        { 0x0200, 0x04, 0x00 },
        { 0x0400, 0x08, 0x00 },
        { 0x0800, 0x10, 0x00 },
        { 0x1000, 0x20, 0x00 },
        { 0x1FFF, 0x3F, 0x7F },
        { 0x2000, 0x40, 0x00 },
        { 0x2001, 0x40, 0x01 },
        { 0x3FFF, 0x7F, 0x7F },
    };

    int passed = 1;
    for (int i = 0, c = sizeof(cases) / sizeof(cases[0]); i < c; i++) {
        uint8_t msb, lsb;
        encode14BitValue(cases[i].in, &msb, &lsb);

        if (cases[i].expected_msb != msb || cases[i].expected_lsb != lsb) {
            printf("failed: 0x%04hX expected 0x%02hhX 0x%02hhX got 0x%02hhX 0x%02hhX\n", cases[i].in, cases[i].expected_msb, cases[i].expected_lsb, msb, lsb);
            passed = 0;
        }
    }

    return passed ? 0 : 1;
}

In your code, trying to pack the two bytes of result into one 16-bit integer just adds confusion. I don't know why you're doing that, since you're going to have to extract individual bytes again, whenever you send the MIDI anywhere else. That's where any worries about endianness come up, since your packing and unpacking code have to agree. You might as well not bother. I bet your code was incorrect, but your error in swapping MSB and LSB compensated for it.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!