1

iOS で生の UInt16 値から14 ビットMIDI ピッチ ベンド値を手作業で組み立てる必要があります。誰かがエレガントな解決策を思い付く機会があったかどうか疑問に思っていますか? これが私の現在の状況です。これをテストする機会は、おそらく今日中に得られるでしょうが、それまでに連絡があれば、すばらしいことです。

まず、興味のある方のために MIDI の予備知識をいくつか。

MIDI ピッチベンドは 1 つのステータスバイトに分割され、その後に2 つのデータ バイトが続きます (これは 14 ビット コントローラーです)。MSB -> LSB

(編集: 更新、実際にはStatus -> LSB -> MSBです)

(つまり、1110 0000、0111 1111、0111 1111)

課題は、iOS で ARM/Intel 16 ビット UInt16 を 2 つの 7 ビット セグメントに分割し、それを MIDI で意味のあるものにする方法です。

ここでは符号なし整数を扱っているため、値 0 はニュートラル ピッチ ベンドではなく、フル ピッチ ダウン (ニュートラル ピッチ ベンドは 8192 と定義されています) であり、16,383 はフル ピッチ アップであることを覚えておいてください。

したがって、これを行う方法についての私の最善の推測は次のとおりです。

UInt16 msbAnd = base10ValueUInt16 & 16256; //clearing out LSB 
UInt16 msbAndShift = msbAnd << 1; //shift into leading Byte, with 0 status bit

UInt16 lsbAnd = base10ValueUInt16 & 127; //isolating LSB
UInt16 finalTwoBytePitchWord = msbFinalAndShift | lsbAnd; //make UInt16 word

UInt16 finalTwoBytePitchWordFlipped = CFSwapInt16HostToBig(finalTwoBytePitchWord); //Endian tweak

このコードは正常に動作し、必要なゼロ ステータス ビットを使用して 2 つのデータ バイトを作成し、MIDI に必要と思われるリトル エンディアンの Intel/ARM から反転させているようです (MIDI は STATUS -> MSB -> LSB です): I can slap後で適切な MIDI チャンネルを使用して先頭のステータス バイトに書き込みます。

それで、これは理にかなっていますか?よりエレガントなソリューションを思いついた人はいますか? (私が見落としているライブラリはありますか?) ... 後でまたチェックインし、これが実際にターゲットにしなければならないサンプラーで実際に機能したかどうかを人々に知らせます。

ありがとう

4

1 に答える 1

4

あなたのコードはほぼ正しいと思いますが、非常に複雑です。この質問は、iOS、エンディアン、ARM、Intel とは関係ありません。それは単なる昔ながらの C ビットいじりです。コードを正しく記述すれば、変更を加えなくても適切なプラットフォームで動作します。ライブラリは必要ありません。ほんの数行のコードです。

バイト単位で MIDI を操作するのが最善です。16 ビットの符号なし整数 (最大で 14 ビット相当の値があると信頼されます) を取り、1 つは最上位ビット、もう 1 つは最下位ビットの 2 つのシングルバイト値を返す関数が必要です。

後でメッセージを送信するときに、適切な順序でバイトを組み立てます。仕様によると、ピッチ ホイール メッセージは、STATUS、LSB、MSB の 3 バイトです。あなたはあなたの質問でそれらを逆に持っています!

最下位の 7 ビットは簡単です。これらのビットを元の値からマスクするだけです。最上位の 7 ビットも同様です。次の上位 7 ビットを元の値からマスクしてから、シフト ダウンします。

マシンのメモリ内で 16 ビット整数がリトルエンディアンかビッグ エンディアンかは問題ではありません。コンパイラがそれを処理します。

これが関数とテストツールです。

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

コードで、結果の 2 バイトを 1 つの 16 ビット整数にパックしようとすると、混乱が生じるだけです。MIDIを他の場所に送信するたびに、個々のバイトを再度抽出する必要があるため、なぜそうしているのかわかりません。パッキングとアンパッキングのコードが一致している必要があるため、ここでエンディアンに関する懸念が生じます。気にしない方がいいかもしれません。あなたのコードは間違っていたに違いありませんが、MSB と LSB を交換する際のエラーがそれを補っています。

于 2012-11-28T06:55:50.183 に答える