NSData 与 NSValue?如何在 sendMIDISysExEvent:(NSData *)midiData 中封装 C 结构

NSData vs NSValue ? how to encapsulate a C-structure in sendMIDISysExEvent:(NSData *)midiData

我正在尝试使用 sendMIDISysExEvent:(NSData *)midiData 来重新调整音乐应用程序中的一些 MIDI 音符。我希望 ViewControllerSynth class 发送 MIDI 消息以更改 10 个音符的调音。用于重新调整一个音符的标准 MIDI 系统专用消息格式如下所示

                 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,p.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);

最后是浮点值...

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 I began to ask how I might go about preparing a MIDI data packet based on NSData that sendMIDISysExEvent:(NSData *)midiData will send. And this answer recommends NSValue as the better way to encapsulate a C-structure like the one I'm trying to create so I'm confused. If that really is the case, I'm puzzled why Apple would introduce a method like sendMIDISysExEvent:(NSData *)midiData.

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

结论

响应已接受的答案,"raw binary data" 的长度根据字节数而不是数据类型[=66]定义=],解释了 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)

NSDataNSValue 都是 "raw binary data" 的包装器,但具有不同的意图和不同的方式来定义二进制数据的长度。

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

The type you specify must be of constant length. You cannot store C strings, variable-length arrays and structures, and other data types of indeterminate length in an NSValue—you should use NSString or NSData objects for these types.

NSData 表示任意字节缓冲区的框。所以它有一个接口,通过它你可以根据字节定义 "raw binary data" 的长度(并且 而不是类型)。

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

希望对您有所帮助。