2012-11-27 41 views
1

我需要在iOS中手動組裝14bit MIDI Pitch Bend值。我想知道有沒有人有機會想出一個優雅的解決方案?這裏是我所在的地方 - 我將有機會在今天晚些時候測試這個,但如果我在此之前回聽,很棒:爲iOS計算CoreMIDI Pitch Bend值?

首先,對於任何人都好奇的一些MIDI預備。

MIDI滑音被分解成一個狀態字節後面數據字節(這是一個14位控制器),這兩個數據字節與他們的狀態字節用零種狀態都領先,MIDI相關規格有他們出現在MSB的順序 - > LSB

(編輯:更新,它實際上狀態 - >LSB - >MSB

(即1110 0000 0111 1 111,011111111)

面臨的挑戰是如何在iOS上將ARM/Intel 16bit UInt16拆分成兩個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 

此代碼運行正常,似乎創建所需的零個狀態位的兩個數據字節和小端周圍翻轉他們的Intel/ARM似乎是MIDI所必需的(MIDI是STATUS - > MSB - > LSB):稍後我可以用適當的MIDI通道拍攝領先的狀態字節。

那麼,這是否有意義?有沒有人想出一個更優雅的解決方案? (有沒有一個我可以忽略的圖書館?)...我稍後再回來查看,也讓人們知道這個實際上是否適用於我必須瞄準的採樣器。

感謝

回答

4

我覺得你的代碼是接近正確的,但它過於複雜。這個問題與iOS或Endianness或ARM或Intel無關;這只是普通的舊C比特。如果您正確編寫代碼,它將在any reasonable platform上工作,無需修改。你不需要圖書館;它只有幾行代碼。

最好在逐字節的基礎上使用MIDI。您需要一個採用16位無符號整數(我們將信任的值最多爲14位值)的函數並返回兩個單字節值,其中一個具有最高有效位,另一個具有最低有效位。

稍後,當您發送消息時,將按照適當的順序組合字節。 According to the specification,音輪信息是三個字節:狀態,然後是LSB,然後是MSB。你的問題讓他們倒退!

最不重要的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; 
} 

在您的代碼中,試圖將兩個字節的結果打包到一個16位整數中只會增加混淆。我不知道爲什麼你這樣做,因爲你將不得不再次提取單個字節,只要您發送MIDI其他地方。因爲你的打包和解包代碼必須同意,所以這就是對排序的擔憂。你可能不打擾。我敢打賭你的代碼是不正確的,但是你在交換MSB和LSB時的錯誤補償了它。

+0

嘿庫爾特,我得到的代碼運行的休息,有機會測試出我最好的猜測handywork在iOS採樣...它實際工作。有時是兩個錯誤的轉折作出正確的,在這種情況下,我通過翻轉字節到正確的順序錯誤的原因lucked了。感謝清理狀態 - > LSB - > MSB,並使其清晰離開字節序的編譯器...位操作是有點兒樂趣實際。 – OverToasty