首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >计算CoreMIDI基音弯曲值iOS?

计算CoreMIDI基音弯曲值iOS?
EN

Stack Overflow用户
提问于 2012-11-27 17:41:57
回答 1查看 982关注 0票数 1

我需要手工组装14位MIDI沥青弯曲的值从原始的UInt16值在iOS。我想知道有没有人有机会想出一个优雅的解决方案?今天晚些时候我会有机会测试这个,但是如果我在那之前听到的话,太好了:

首先,对好奇的人进行一些MIDI预演。

MIDI音调弯曲被分解为一个状态字节,然后是两个数据字节(它是一个14位控制器),这两个数据字节与它们的状态字节相关联,它们都与零状态相关联,MIDI Spec使它们以MSB -> LSB的顺序出现。

(编辑: Update,它实际上是Status -> LSB -> MSB )

(即1110 0000 0111 1111 0111 1111 )

挑战是如何将ARM/Intel 16位UInt16分解为iOS上的两个7位段,这对MIDI有意义吗?

请记住,因为我们处理的是一个无符号整数,0值不是中性音高弯曲,而是全音高下降,其中中性音高弯曲被定义为8192,16,383是全俯仰。

因此,我对如何做到这一点,最好的猜测是:

代码语言:javascript
复制
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

这段代码运行良好,似乎使用所需的零状态位创建了两个数据字节,并从小型endian Intel/ARM (MIDI是status -> MSB -> LSB )中翻转它们:我可以在以后使用适当的MIDI通道轻拍主导状态字节。

那么,这有道理吗?有谁想出了一个更优雅的解决方案吗?(是否有一个我正在忽略的图书馆?)我将在稍后再检查,并让人们知道,如果这真的有效的采样器,我必须瞄准它。

谢谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-11-28 06:55:50

我认为您的代码接近正确,但它过于复杂。这个问题与iOS、endianness、ARM或Intel无关,它只不过是个老套的C点绕圈子。如果您正确地编写了代码,它将在任何合理的平台上工作而无需修改。您不需要库;它只是几行代码。

最好是以逐字节的方式使用MIDI。您需要一个函数,它接受一个16位的无符号整数(我们相信它的值最多为14位),并返回两个单字节值,一个是最重要的位,一个是最小的位。

稍后,当您发送消息时,将按适当的顺序组装字节。按照规格,螺距轮消息有三个字节:状态,然后是LSB,然后是MSB。你的问题倒过来了!

最不重要的7位很简单:只需从原始值中屏蔽掉这些比特即可。最重要的7位是相似的:屏蔽下一个更高的7位从原始值,然后把它们向下移动。

不管机器上的16位整数是小端点还是大端字节,编译器都会处理这个问题。

这是一个函数和一个测试工具。

代码语言:javascript
复制
#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发送到其他地方时,你就必须再次提取单个字节。这就是任何关于endianness的担忧的地方,因为您的包装和拆包代码必须一致。你最好别麻烦了。我打赌您的代码是不正确的,但是您在交换MSB和LSB时的错误对此进行了补偿。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13589979

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档