iOS:音高变换和将 OpenAL 的输出传送到缓冲区
我最近发现在 iOS 中可以使用 OpenAL 进行音调转换。
我正在看霍兰斯的声音库播放器。它吸收散布在整个音域中的 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);
不幸的是,这对于同时播放一堆音符来说并不好,因为它占用了太多的 CPU。它是动态变调的。
我想要的是为每个钢琴音符创建一个缓冲区,并使用这种音高转换技术填充这些缓冲区。但我不知道如何让 openAL 将声音播放到缓冲区中,而不是通过扬声器播放。
有什么办法可以通过管道输出 alSourcePlay(源->sourceId);
进入缓冲区?
如果我不能做到这一点,我有什么选择?我尝试使用 DSPDimension 文章中的 smbPitchShift,但它没有提供良好的保真度:钢琴音符的起音阶段确实丢失了。我想我可以使用 Dirac3 的免费版本...(我目前没有钱购买完整版本,但我认为免费版本允许 Mono 处理,所以我可以破解它)。还有其他选择吗?
编辑:我已经测试了 Dirac3,它也有同样的问题。似乎笼罩了攻击。看起来 OpenAL 的变调器以某种方式做了 Dirac3 没有做的事情。
I have recently spotted that it is possible in iOS to use OpenAL to pitch shift.
I am looking at Hollance's sound bank player. it takes in 15 or so piano notes spattered through the range, and plays any note by figuring out which sample it is closest to, and pitch shifting that sample an appropriate amount. This is the code that does it:
- (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;
}
}
}
}
you can see this line does the pitch shifting:
alSourcef(source->sourceId, AL_PITCH, note->pitch / buffer->pitch);
unfortunately this is no good for playing a bundle of notes simultaneously, as it takes too much CPU. it is pitch shifting dynamically.
what I want is to create a buffer for each piano note, and populate these buffers using this pitch shifting technology. but I can't see how to get openAL to play the sound into a buffer as opposed to play it out through the speakers.
is there any way to pipe the output of
alSourcePlay(source->sourceId);
into a buffer?
If I cannot do this, what are my options? I have tried using smbPitchShift from the DSPDimension article, but it doesn't give good fidelity: the attack phase of the piano note is really lost. I guess I could use the free version of Dirac3... ( I don't have money for the full version at the moment, but I think the free version allows Mono processing, so I can hack that ). is there any other option?
EDIT: I have since tested Dirac3, and it shares the same problem. it seems to envelope the attack. it seems that OpenAL's pitch shifter somehow does something that Dirac3 does not.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
alSourcePlayv 允许您同时播放多个源 - 源的最大数量取决于平台,但在 iOS 上为 32(在 Apple core-audio 列表上回答,为了完整起见,在此处)
alSourcePlayv allows you to play multiple sources concurrently - the maximum number of sources is platform dependent, but is 32 on iOS (answered on the apple core-audio list, here for completeness)