NSData против NSValue? как инкапсулировать C-структуру в sendMIDISysExEvent:(NSData *)midiData

Я пытаюсь найти способ использовать sendMIDISysExEvent:(NSData *)midiData как способ перенастроить несколько нот MIDI в музыкальном приложении. Я хочу, чтобы ViewController отправил MIDI-сообщение классу Synth, чтобы изменить настройку 10 нот. Стандартный формат сообщений MIDI System Exclusive для перенастройки одной ноты выглядит следующим образом.

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

legend: tt ll [kk xx yy zz] (примечание - немного отличается от подробной спецификации 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;

и еще в .ч

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

Однако, когда я прочитал этот ответ, я начал спрашивать, как я могу подготовить пакет данных MIDI на основе NSData, который sendMIDISysExEvent:(NSData *)midiData будет Отправить. И этот ответ рекомендует NSValue как лучший способ инкапсулировать C-структуру, подобную той, которую я пытаюсь создать, поэтому я я в замешательстве. Если это действительно так, я озадачен, почему Apple представила такой метод, как sendMIDISysExEvent:(NSData *)midiData.

Мой вопрос: исходя из формата сообщения (изложенного в моем коде выше), как мне подготовить midiData для отправки с помощью этого метода?

ЗАКЛЮЧЕНИЕ

В ответ на принятый ответ длина «необработанных двоичных данных», определяемая с точки зрения количества байтов, а не типа данных, объясняет существенную разницу между NSData и NSValue . Это также предполагает, что 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)

person Greg    schedule 06.07.2017    source источник


Ответы (1)


И NSData, и NSValue являются обертками для «необработанных двоичных данных», но с другим намерением и другим способом определения длины двоичных данных.

NSValue нацелен на скалярные типы бокса, такие как int, float (но также и struct), а интерфейс NSValue предназначен для передачи длины «данных» посредством типа. Следовательно, невозможно инкапсулировать объекты переменной длины (например, VLA или C-строки) в NSValue, поскольку размер данных не может быть получен из информации о типе (ср. NSValue):

Указанный тип должен иметь постоянную длину. Вы не можете хранить строки C, массивы и структуры переменной длины и другие типы данных неопределенной длины в NSValue — для этих типов следует использовать объекты NSString или NSData.

NSData подразумевается как поле для произвольных байтовых буферов. Таким образом, у него есть интерфейс, с помощью которого вы можете определить длину «необработанных двоичных данных» в байтах (а не в терминах типа).

Что касается вашего вопроса, поскольку MIDI-интерфейс в sendMIDISysExEvent:(NSData *)midiData ожидает объект NSData, единственный (значимый) способ - передать ваш объект как объект NSData.

Надеюсь, поможет.

person Stephan Lechner    schedule 06.07.2017
comment
Стивен, это определенно прояснило ситуацию. Смотрите мой ответ. Спасибо - person Greg; 07.07.2017