iOS: Pitch Shifting & Piping вывод из OpenAL в буфер
Недавно я заметил, что в iOS можно использовать OpenAL для изменения высоты тона.
Я смотрю на звуковой банк Hollance. он берет приблизительно 15 нот фортепьяно, разбросанных по диапазону, и воспроизводит любую ноту, выясняя, к какому сэмплу он ближе всего, и сдвигая высоту тона этого сэмпла на соответствующую величину. Это код, который делает это:
- (void) noteOn: (int) midiNoteNumber
gain: (float) gain
{
if (!initialized)
{
NSLog(@"SoundBankPlayer is not initialized yet");
return;
}
int sourceIndex = [self findAvailableSource];
if (sourceIndex != -1)
{
alGetError(); // clear any errors
Note* note = notes + midiNoteNumber;
if (note->bufferIndex != -1)
{
Buffer* buffer = buffers + note->bufferIndex;
Source* source = sources + sourceIndex;
source->noteIndex = midiNoteNumber;
alSourcef(source->sourceId, AL_PITCH, note->pitch / buffer->pitch);
alSourcei(source->sourceId, AL_LOOPING, AL_FALSE);
alSourcef(source->sourceId, AL_REFERENCE_DISTANCE, 100.0f);
alSourcef(source->sourceId, AL_GAIN, gain);
float sourcePos[] = { note->panning, 0.0f, 0.0f };
alSourcefv(source->sourceId, AL_POSITION, sourcePos);
alSourcei(source->sourceId, AL_BUFFER, AL_NONE);
alSourcei(source->sourceId, AL_BUFFER, buffer->bufferId);
ALenum error;
if ((error = alGetError()) != AL_NO_ERROR)
{
NSLog(@"Error attaching buffer to source: %x", error);
return;
}
alSourcePlay(source->sourceId);
if ((error = alGetError()) != AL_NO_ERROR)
{
NSLog(@"Error starting source: %x", error);
return;
}
}
}
}
Вы можете видеть, что эта строка выполняет сдвиг высоты тона:
alSourcef(source->sourceId, AL_PITCH, note->pitch / buffer->pitch);
к сожалению, это не годится для одновременного воспроизведения пачки нот, так как это требует слишком много ресурсов процессора. это изменение высоты тона динамически.
я хочу создать буфер для каждой ноты пианино и заполнить эти буферы с помощью этой технологии смещения высоты тона. но я не вижу, как заставить openAL воспроизводить звук в буфер, а не воспроизводить его через динамики.
есть ли способ передать вывод alSourcePlay(source->sourceId);
в буфер?
Если я не могу сделать это, каковы мои варианты? Я попытался использовать smbPitchShift из статьи DSPDimension, но это не дает хорошей точности: фаза атаки фортепианной ноты действительно потеряна. Думаю, я мог бы использовать бесплатную версию Dirac3... (у меня сейчас нет денег на полную версию, но я думаю, что бесплатная версия позволяет обрабатывать Mono, так что я могу взломать ее). есть ли другой вариант?
РЕДАКТИРОВАТЬ: С тех пор я проверил Dirac3, и он разделяет ту же проблему. похоже, окутывает атаку. кажется, что устройство изменения высоты звука OpenAL как-то делает то, чего не делает Dirac3.
1 ответ
alSourcePlayv позволяет воспроизводить несколько источников одновременно - максимальное количество источников зависит от платформы, но для iOS оно равно 32 (ответ на вопрос об аудио-ядре Apple, здесь для полноты)