MIDI-синтез в iOS неправильно ведет себя с изменением высоты тона WRT: LSB игнорируется

Либо в коде синтеза MIDI от Apple есть серьезная ошибка, либо я делаю что-то не так. Вот мое понимание этого. Когда вы отправляете MIDI-команду изменения высоты тона, диапазон изменения составляет от -8192 до 8191, транспонированный до 0. (Так что фактический диапазон составляет от 0 до 16383.) Это число разделено на два 7-битных поля, так что на самом деле это означает, что у вас есть 128 значений грубого управления и 128 значений точного управления.

Вот пример изменения высоты тона, который я написал, аналогичный командам в Apple ЗагрузитьPresetDemo.

// '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-синтезатор Apple игнорирует 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 или я что-то упустил? (Возможно, параметр настройки?) А может, это вообще не баг? Может быть, синтезатор Apple просто не имеет такого уровня детализации по дизайну? Это законно по стандарту MIDI?! Помощь!


РЕДАКТИРОВАТЬ:

Когда диапазон изменения высоты тона установлен на 40 полутонов, каждое грубое изменение имеет слышимое изменение. Когда диапазон изменения высоты тона установлен на 10 полутонов, только каждое второе грубое изменение имеет значение. При 2 полутонах (по умолчанию) требуется 4 или более грубых изменений, чтобы изменить ситуацию.

Другими словами, мало того, что LSB очевидно игнорируется, но также, кажется, есть минимальное количество центов для изменения высоты тона. Можно ли устранить любое из этих ограничений? И если нет, то существуют ли программные синтезаторы для iOS с более высоким разрешением?

Хм... возможно, применение kAudioUnitSubType_Varispeed или kAudioUnitSubType_NewTimePitch даст лучшие результаты...


person Archagon    schedule 18.03.2013    source источник


Ответы (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. Затем, выбрав звук тромбона, нажмите и удерживайте кнопку «Средняя нота». Удерживая ее, нажмите кнопки bendUp или bendDown. Я слышу изменения высоты тона, когда LSB меняется, а MSB остается прежним.

person SSteve    schedule 19.03.2013
comment
Эй, спасибо! Я все еще очень плохо знаком с этим материалом MIDI. Я исправлю свой код как можно скорее. Но по-прежнему кажется, что AudioUnitSetParameter, а также регулировка высоты тона игнорируют точную настройку - если я тоже не облажался. - person Archagon; 20.03.2013
comment
Я не пробовал AudioUnitSetParameter, только MusicDeviceMIDIEvent. Когда я устанавливал диапазон изгиба, он определенно учитывал значение в центах. например когда я установил диапазон изменения тона на 12 полутонов и 99 центов, высота тона была изменена на октаву плюс полшага, когда изменение высоты тона было максимальным. У меня не хватило времени, прежде чем я смог окончательно проверить, игнорирует ли он LSB при отправке сообщения о изменении высоты тона. Возможно, что это так. Спецификация MIDI не требует, чтобы устройство учитывало все 14 бит сообщения изменения высоты тона. - person SSteve; 20.03.2013
comment
Я подтвердил, что младший бит сообщения об изгибе не игнорируется. Смотрите мой обновленный ответ. - person SSteve; 21.03.2013