iOS 中的 MIDI 合成行为不端 WRT 弯音:忽略 LSB

Posted

技术标签:

【中文标题】iOS 中的 MIDI 合成行为不端 WRT 弯音:忽略 LSB【英文标题】:MIDI synthesis in iOS misbehaving WRT pitch bending: LSB ignored 【发布时间】:2013-03-18 01:42:04 【问题描述】:

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

当bendMSB(粗调控制)改变时,弯音就好了。但是当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);

这表现出与前一个示例相同的行为。这种处理方式特别有趣的是,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);

你能猜出会发生什么吗?没错,LSB 消息会被忽略,而音高轮范围仅会根据提供的半音数变化。

这里发生了什么?这是苹果的错误还是我错过了什么? (也许是一个设置参数?)或者它根本不是一个错误?也许 Apple 的合成器在设计上没有那么详细的细节?按照 MIDI 标准,这种事情合法吗?!救命!


编辑:

当弯音范围设置为 40 个半音时,每个粗略的变化都会产生听觉上的差异。当弯音范围设置为 10 个半音时,只有每秒钟的粗略变化才会产生影响。在 2 个半音(默认值)时,需要 4 次或更多次粗调才能产生影响。

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

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

【问题讨论】:

【参考方案1】:

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

UInt32 noteCommand = kMIDIMessage_PitchBend << 4 | 0;

OSStatus result = MusicDeviceMIDIEvent(self.samplerUnit, noteCommand, noteNum, bendMSB, bendLSB);

这样做:

UInt32 bendCommand = kMIDIMessage_PitchBend << 4 | 0;

OSStatus result = MusicDeviceMIDIEvent(self.samplerUnit, bendCommand, bendLSB, bendMSB, 0);

音符值不是弯音命令的一部分。 (另外,我将变量 noteCommand 的名称更改为 bendCommand 以更准确地反映其用途。)

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

@property (readwrite) NSInteger bendValue;

还有这段代码:

- (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);  

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

运行程序并按下bendCenter 按钮。然后,选择长号声音,按住“Mid Note”按钮。按住它的同时,按下bendUpbendDown 按钮。当 LSB 改变而 MSB 保持不变时,我可以听到音高的变化。

【讨论】:

嘿,谢谢!我对这个 MIDI 东西还是很陌生。我会尽快修复我的代码。但似乎仍然是 AudioUnitSetParameter 以及弯音调整忽略微调的情况——假设我也没有搞砸。 我没有尝试AudioUnitSetParameter,只是MusicDeviceMIDIEvent。当我设置弯曲范围时,它绝对尊重美分值。例如当我将弯音范围设置为 12 个半音和 99 分时,当弯音达到最大时,音高改变了一个八度加半步。在发送弯音消息时,我没有时间对它是否忽略 LSB 进行最终测试。有可能确实如此。 MIDI 规范不要求设备支持弯音消息的所有 14 位。 我确认没有忽略弯曲消息的 LSB。请参阅我的更新答案。

以上是关于iOS 中的 MIDI 合成行为不端 WRT 弯音:忽略 LSB的主要内容,如果未能解决你的问题,请参考以下文章

如何在 c 中正确发送 midi 弯音消息?

阻止行为不端的用户 facebook sdk ios

Flex 4.6 TextInput 提示 ios 行为不端

QT 中的 QSlider 在新的 MacOS Monterey (v12.0.1) 中行为不端。任何解决方法?

在 iOS7 中使用 UISearchBar 将子视图添加到 tableHeaderView 行为不端

UICollectionView 中的 UITableView 行为不端