iOS: Pitch Shifting & Piping output from OpenAL into a buffer
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)
{
alGetErro开发者_C百科r(); // 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.
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)
精彩评论