2013-03-18 1 views
4

Apple의 MIDI 합성 코드에 심각한 버그가 있거나 잘못된 것이 있습니다. 여기 그것에 대한 나의 이해가있다. 피치 벤드 MIDI 명령을 보내면 벤드 범위는 -8192에서 8191이며 0으로 바뀝니다 (실제 범위는 0에서 16383입니다).이 숫자는 두 개의 7 비트 필드로 나뉘어져 있으므로 실제로 이것이 의미하는 바는 128 개의 거친 제어 값과 128 개의 미세 제어 값이 있음을 의미합니다.iOS의 MIDI 합성이 잘못되어 WRT 피치 벤딩 : LSB 무시

여기에 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 (미세 제어)가 변경되면 아무 일도 일어나지 않습니다. 즉, 애플의 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 메시지는 무시되고 피치 휠 범위는 제공된 반음 수만큼 변경됩니다.

여기 무슨 일 이니? 이 버그가 애플 버그입니까, 아니면 뭔가 빠졌습니까? (설치 매개 변수, 아마도?) 아니면 전혀 버그가 아니에요? 어쩌면 애플의 신디는 그 정도의 디테일을 디자인하지 않았을까? MIDI 표준에 의해 합법적인가?! 도움!


편집 :

피치 벤드 범위 40 개 반음 설정

각 조 변경 청각 적 효과를 낸다. 피치 벤드 범위가 10 반음으로 설정되면 매 두 번째 큰 변화 만 차이를 만듭니다. 2 반음 (기본값)에서는 차이를 만들기 위해 4 가지 이상의 거친 변경이 필요합니다.

즉, LSB가 명백하게 무시 될뿐만 아니라 피치가 변경 될 최소 센트가있는 것으로 보입니다. 이러한 제한 중 하나를 고칠 수 있습니까? 그렇지 않은 경우, 높은 굽힘 해상도를 가진 iOS 용 소프트웨어 신디 프레임 워크가 있습니까?

답변

6

귀하의 피치 벤드 메시지가 잘못된 것입니다 ...

는 흠 ... 어쩌면 kAudioUnitSubType_Varispeed을 적용하거나 kAudioUnitSubType_NewTimePitch 더 나은 결과를 얻을 것입니다. 대신이의 :

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"버튼을 누르고 있습니다. 버튼을 누른 상태에서 bendUp 또는 bendDown 버튼을 누릅니다. LSB가 변경되고 MSB가 동일하게 유지되면 음조의 변화를들을 수 있습니다.

+0

안녕하세요, 감사합니다. 나는 아직도이 미디에 새로운 것들이다. 최대한 빨리 코드를 수정하겠습니다. 하지만 여전히 AudioUnitSetParameter와 피치 벤드가 미세 조정을 무시하는 경우 인 것처럼 보입니다. – Archagon

+1

MusicUnitSetParameter를 사용하지 않고 MusicDeviceMIDIEvent 만 시도했습니다. 나는 벤드 범위를 설정할 때 확실히 센트 값을 존중했습니다. 예 : 벤드 범위를 12 반음과 99 센트로 설정했을 때 피치는 피치 벤드가 최대 인 옥타브와 반 스텝으로 변경되었습니다. 피치 벤드 메시지를 보낼 때 LSB를 무시하는지 여부를 결정할 수 있기 전에 시간이 부족했습니다. 그것이 가능할 수도 있습니다. MIDI 스펙은 장치가 피치 벤드 메시지의 14 비트 모두를 존중할 것을 요구하지 않습니다. – SSteve

+0

벤드 메시지의 LSB가 무시되지 않는다는 것을 확인했습니다. 내 업데이트 답변을 참조하십시오. – SSteve