iOS中的MIDI合成行为不正确WRT俯仰弯曲:LSB被忽略

Arc*_*gon 4 midi core-audio audiounit ios coremidi

Apple的MIDI合成代码中有一个严重的错误,或者我做错了什么.这是我对它的理解.当您发送弯音MIDI命令时,弯曲的范围是-8192到8191,转换为0.(所以实际范围是0到16383.)这个数字被分成两个7位字段,所以真的这意味着你有128个粗控制值和128个精细控制值.

这是我写的弯音样本,类似于Apple的LoadPresetDemo中的命令.

// 'ratio' is the % amount to bend in current pitch range, from -1.0 to 1.0
// 'note' is the MIDI note to bend

NSUInteger bendValue = 8191 + 1 + (8191 * ratio);
NSUInteger bendMSB = (bendValue >> 7) & 0x7F;
NSUInteger bendLSB = bendValue & 0x7F;

UInt32 noteNum = note;
UInt32 noteCommand = kMIDIMessage_PitchBend << 4 | 0;

OSStatus result = MusicDeviceMIDIEvent(self.samplerUnit, noteCommand, noteNum, bendMSB, bendLSB);
Run Code Online (Sandbox Code Playgroud)

当弯曲MSB(粗调)改变时,音高弯曲得很好.但是当bendLSB(精细控制)改变时,没有任何反应.换句话说,似乎Apple的MIDI合成器忽略了LSB,这意味着音符仅在丑陋的离散块中弯曲.

这是另一种做同样事情的方式:

// 'ratio' is the % amount to bend in current pitch range, from -1.0 to 1.0

AudioUnitParameterValue bendValue = 63 + 1 + (63 * ratio); // this is a CGFloat under the hood

AudioUnitSetParameter(self.samplerUnit,
                      kAUGroupParameterID_PitchBend,
                      kAudioUnitScope_Group,
                      0,
                      bendValue,
                      0);
Run Code Online (Sandbox Code Playgroud)

这表现出与前一个例子相同的行为.关于这种做法的另外有趣的是,kAUGroupParameterID_PitchBend的文档指定值范围应为-8192到8191,这完全不起作用.实际范围显示为0到127,浮点(精细控制)被忽略.

最后,如果您进行以下调用以调整弯音范围:

// 'semitones' is the number of semitones (100 cents) to set the pitch bend range to
// 'cents' is the additional number of cents to set the pitch bend range to

UInt32 status = 0xB0 | 0;

MusicDeviceMIDIEvent(self.samplerUnit, status, 0x64, 0x00, 0);        // RPN pitch bend range.
MusicDeviceMIDIEvent(self.samplerUnit, status, 0x65, 0x00, 0);
MusicDeviceMIDIEvent(self.samplerUnit, status, 0x06, semitones, 0);   // Data entry MSB
MusicDeviceMIDIEvent(self.samplerUnit, status, 0x26, cents, 0);       // Data entry LSB (optional)
MusicDeviceMIDIEvent(self.samplerUnit, status, 0x64, 0x7F, 0);        // RPN reset
MusicDeviceMIDIEvent(self.samplerUnit, status, 0x65, 0x7F, 0);
Run Code Online (Sandbox Code Playgroud)

你能猜出会发生什么吗?这是正确的,LSB消息被忽略,并且音高轮范围仅改变所提供的半音的数量.

这里发生了什么?这是Apple的错误还是我错过了什么?(可能是一个设置参数?)或者它可能根本不是一个bug?也许Apple的合成器在设计上没有那么高的细节水平?MIDI标准是否合法?!救命!


编辑:

当弯音范围设定为40个半音时,每个粗略的变化都会产生可听见的差异.当弯音范围设置为10个半音时,只有每一秒的粗略变化都会产生差异.在2个半音(默认值)下,需要4个或更多粗略变化才能产生差异.

换句话说,LSB不仅显然被忽略了,而且似乎还有一个最小的分数来改变.可以修复这些限制吗?如果没有,是否有适用于iOS的软件合成框架具有更高的折弯分辨率?

嗯...也许应用kAudioUnitSubType_Varispeed或kAudioUnitSubType_NewTimePitch会产生更好的结果......

SSt*_*eve 6

您的弯音信息不正确.而不是这个:

UInt32 noteCommand = kMIDIMessage_PitchBend << 4 | 0;

OSStatus result = MusicDeviceMIDIEvent(self.samplerUnit, noteCommand, noteNum, bendMSB, bendLSB);
Run Code Online (Sandbox Code Playgroud)

做这个:

UInt32 bendCommand = kMIDIMessage_PitchBend << 4 | 0;

OSStatus result = MusicDeviceMIDIEvent(self.samplerUnit, bendCommand, bendLSB, bendMSB, 0);
Run Code Online (Sandbox Code Playgroud)

音符值不是弯音命令的一部分.(另外,我更名可变的noteCommand,以bendCommand更准确地反映其目的.)

在LoadPresetDemo中,我添加了一个属性MainViewController.m:

@property (readwrite) NSInteger bendValue;
Run Code Online (Sandbox Code Playgroud)

而这段代码:

- (void)sendBendValue:(NSInteger)bendValue {
    //bendValue in the range [-8192, 8191]
    const UInt32 bendCommand = kMIDIMessage_PitchBend << 4 | 0;
    bendValue += 8192;
    UInt32 bendMSB = (bendValue >> 7) & 0x7F;
    UInt32 bendLSB = bendValue & 0x7F;
    NSLog(@"MSB=%d, LSB=%d", (unsigned int)bendMSB, (unsigned int)bendLSB);
    OSStatus result = MusicDeviceMIDIEvent(self.samplerUnit, bendCommand, bendLSB, bendMSB, 0);
    NSAssert (result == noErr, @"Unable to send pitch bend message. Error code: %d '%.4s'", (int) result, (const char *)&result);
}

- (IBAction)bendDown:(id)sender {
    self.bendValue = MAX(-8192, self.bendValue - 0x20);
    [self sendBendValue:self.bendValue];
}

- (IBAction)bendCenter:(id)sender {
    self.bendValue = 0;
    [self setBendRange:50 cents:0];
    [self sendBendValue:self.bendValue];
}

- (IBAction)bendUp:(id)sender {
    self.bendValue = MIN(8191, self.bendValue + 0x20);
    [self sendBendValue:self.bendValue];
}

-(void)setBendRange:(UInt32)semitones cents:(UInt32)cents {
    MusicDeviceMIDIEvent(self.samplerUnit, 0xB0, 0x64, 0, 0);
    MusicDeviceMIDIEvent(self.samplerUnit, 0xB0, 0x65, 0, 0);
    MusicDeviceMIDIEvent(self.samplerUnit, 0xB0, 0x06, semitones, 0);
    MusicDeviceMIDIEvent(self.samplerUnit, 0xB0, 0x26, cents, 0);
    //The following two lines are not really necessary. They only matter if additional controller 0x06 or 0x26 messages are sent
    //MusicDeviceMIDIEvent(self.samplerUnit, 0xB0, 0x64, 0x7F, 0);
    //MusicDeviceMIDIEvent(self.samplerUnit, 0xB0, 0x65, 0x7F, 0);  
}
Run Code Online (Sandbox Code Playgroud)

我创建了三个按钮,并将它们分配到bendDown:,bendCenter:bendUp:.

运行程序,然后按bendCenter按钮.然后,选择长号音,按住"Mid Note"按钮.按住它,按bendUpbendDown按钮.当LSB改变且MSB保持不变时,我可以听到音调的变化.