iOS: Pitch Shifting & Piping output from OpenAL into a buffer
Asked Answered
C

1

4

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.

Camion answered 6/12, 2010 at 9:29 Comment(2)
I love questions with illustrative source codes! Thanks a lot for that, it helped me a lot! :)Sanctimony
Don't forget to bump it up then ;)Camion
B
4

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)

Breckenridge answered 7/12, 2010 at 17:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.