【问题标题】:NSData vs NSValue ? how to encapsulate a C-structure in sendMIDISysExEvent:(NSData *)midiDataNSData vs NSValue ?如何在 sendMIDISysExEvent:(NSData *)midiData 中封装 C 结构
【发布时间】:2017-12-10 04:49:33
【问题描述】:

我正在尝试使用sendMIDISysExEvent:(NSData *)midiData 作为在音乐应用程序中重新调整少数 MIDI 音符的方法。我希望 ViewControllerSynth 类发送 MIDI 消息以更改 10 个音符的调音。用于重调一个音符的标准 MIDI System Exclusive 消息格式如下所示

                 F0 7F <device ID> 08 02 tt ll [kk xx yy zz] F7.

legend: tt ll [kk xx yy zz](NB - 与 MIDI 1.0 详细规范 4.2,第 49 页略有不同)

使用typedef struct 可以使用单个float 而不是uint8_t 来表示调谐频率,例如

PlayViewController.h

    typedef struct
    {
    uint8_t SYSEX_SysexHeader;                // oxF0h;     // System Exclusive Header
    uint8_t SYSEX_UniversalRealTimeHeader;    // ox7Fh;     // Universal RealTime Header
    uint8_t SYSEX_myPhone;                    // ox00h;     // ID of target device (e.g. iPhone)
    uint8_t SYSEX_subID1;                     // ox08h;     // sub-ID #1 (MIDI Tuning Standard)
    uint8_t SYSEX_subID2;                     // ox02h;     // sub-ID #2 (note change)
    uint8_t SYSEX_tuningProgramNumber;        // ox0h;      // tuning program number (0 -127)
    uint8_t SYSEX_numberOfKeys;               // ox10h;     // number of changes

    uint8_t SYSEX_key0;
    float   TUNING_pitch_0;

    uint8_t SYSEX_key1;
    float   TUNING_pitch_1;

    uint8_t SYSEX_key2;
    float   TUNING_pitch_2;

    uint8_t SYSEX_key3;
    float   TUNING_pitch_3;

    uint8_t SYSEX_key4;
    float   TUNING_pitch_4;

    uint8_t SYSEX_key5;
    float   TUNING_pitch_5;

    uint8_t SYSEX_key6;
    float   TUNING_pitch_6;

    uint8_t SYSEX_key7;
    float   TUNING_pitch_7;

    uint8_t SYSEX_key8;
    float   TUNING_pitch_8;

    uint8_t SYSEX_key9;
    float   TUNING_pitch_9;

    uint8_t eox;                                // OxF7h;       //

    }
    TuneEvent;

并且还在.h中

    typedef NS_ENUM(NSInteger, SYSEX)
    {
    SYSEX_SysexHeader               = 240,
    SYSEX_UniversalRealTimeHeader   = 127,
    SYSEX_myPhone                   = 0,
    SYSEX_subID1                    = 8,
    SYSEX_subID2                    = 2,
    SYSEX_tuningProgramNumber       = 0,
    SYSEX_numberOfKeysToBeChanged   = 1,
    SYSEX_key0                      = 61,
    SYSEX_key1                      = 62,
    SYSEX_key2                      = 63,
    SYSEX_key3                      = 64,
    SYSEX_key4                      = 65,
    SYSEX_key5                      = 66,
    SYSEX_key6                      = 67,
    SYSEX_key7                      = 68,
    SYSEX_key8                      = 69,
    SYSEX_key9                      = 70,
    SYSEX_eox                       = 247
    };

    typedef NS_ENUM(NSInteger, TUNING)
    {
    TUNING_pitch0,
    TUNING_pitch1,
    TUNING_pitch2,
    TUNING_pitch3,
    TUNING_pitch4,
    TUNING_pitch5,
    TUNING_pitch6,
    TUNING_pitch7,
    TUNING_pitch8,
    TUNING_pitch9
    };

    float TUNING_float(TUNING micro);

最后是浮点值... (thanks to this answer)

PlayViewController.m

    float TUNING_float(TUNING micro) 
    {
    switch (micro) 
        {
        case TUNING_pitch0:
            return  579.4618f;
        case TUNING_pitch1:
            return  607.0552f;
        case TUNING_pitch2:
            return  662.2421f;
        case TUNING_pitch3:
            return  708.2311f;
        case TUNING_pitch4:
            return  772.6157f;
        case TUNING_pitch5:
            return  809.4070f;
        case TUNING_pitch6:
            return  882.9894f;
        case TUNING_pitch7:
            return  910.5828f;
        case TUNING_pitch8:
            return  993.3631f;
        case TUNING_pitch9:
            return  1030.1540f;
        default:
            return   0.0f;
        }
    }

然而,当我读到this answer 时,我开始询问如何根据NSData 准备一个由sendMIDISysExEvent:(NSData *)midiData 发送的MIDI 数据包。 this answer 推荐 NSValue 作为封装 C 结构的更好方法,就像我正在尝试创建的那样,所以我很困惑。如果真的是这样,我很奇怪为什么苹果会引入像sendMIDISysExEvent:(NSData *)midiData 这样的方法。

我的问题是:基于消息格式(在我上面的代码中概述),我将如何准备midiData 以便使用这种方法发送它?

结论

作为对已接受答案的回应,“原始二进制数据”的长度以字节数而非数据类型定义,解释了两者之间的显着区别NSDataNSValue。这也表明sendMIDISysExEvent:(NSData *)midiData 仅设计用于处理数据字节,即uint8_t 而不是float.。换句话说,明智的选择是根据 MIDI 调音标准的以下摘录使用字节来表达频率值

    yy = MSB of fractional part (1/128 semitone = 100/128 cents = .78125 cent units)
    zz = LSB of fractional part (1/16384 semitone = 100/16384 cents = .0061 cent units)

【问题讨论】:

    标签: ios objective-c c nsdata nsvalue


    【解决方案1】:

    NSDataNSValue 都是围绕“原始二进制数据”的包装器,但在定义二进制数据长度时具有不同的意图和不同的方式。

    NSValue 目标是装箱标量类型,如intfloat(还有struct),NSValue 的接口旨在通过类型。因此,不可能将可变长度的对象(如 VLA 或 C 字符串)封装在 NSValue 中,因为数据的大小不能从类型信息中得出(cf. NSValue):

    您指定的类型必须是恒定长度。你不能存储 C 字符串、可变长度数组和结构以及其他数据类型 NSValue 中长度不确定的——你应该使用 NSString 或 这些类型的 NSData 对象。

    NSData 表示任意字节缓冲区的框。因此它有一个接口,您可以通过该接口以字节为单位定义“原始二进制数据”的长度(不是以类型为单位)。

    关于您的问题,由于sendMIDISysExEvent:(NSData *)midiData 中的 MIDI 接口需要 NSData-object,唯一(有意义的)方法是将您的对象作为 NSData-object 传递。

    希望对你有帮助。

    【讨论】:

    • 斯蒂芬,它确实澄清了一些事情。看我的回复。谢谢
    猜你喜欢
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多