I'm trying work out the way to use sendMIDISysExEvent:(NSData *)midiData
as a way to retune a handful of MIDI notes in a music app. I'd like the ViewController
to send a MIDI message to the Synth
class to change the tuning of 10 notes. The standard MIDI System Exclusive message format for retuning one note looks like this
F0 7F <device ID> 08 02 tt ll [kk xx yy zz] F7.
legend: tt ll [kk xx yy zz]
(NB -slightly different to MIDI 1.0 Detailed Specification 4.2, p.49)
With typedef struct
it would be possible to use a single float
instead of a uint8_t
to represent tuning frequency, e.g.
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;
and still in .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);
and finally for the floating point values ... (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;
}
}
However when I read 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
.
My question is: based on the message format (outlined in my code above), how would I prepare midiData
in order to send it using this method ?
CONCLUSION
In response to the accepted answer, length of "raw binary data", defined in terms of number of bytes rather than type of data, explains the salient difference between NSData
and NSValue
. This would also suggest sendMIDISysExEvent:(NSData *)midiData
was only designed to handle bytes of data i.e. uint8_t
and not float.
In other words, the sensible option is to express frequency values using bytes as per the following extract from the MIDI Tuning Standard
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)