Pure Data (libpd) samplerate issue, iPhone 6s speaker audio clicks


We have an existing project based on (the excellent!) TAAE, a sample sequencer style app running at 44.1kHz. We recently added a simple TAAE <-> Pure Data (libpd) bridge to harness Pure Data processing into our signal chain.

However, some recent testing with the iPhone 6S internal speaker has exposed a problem, as the iPhone 6S internal speaker locks the hardware samplerate at 48kHz. For our app, this implies sample rate conversion which exposes an incompatibility at the libpd audio API.

An AEBlockFilter instance passes audio samples to/from libpd, based directly on this example libpd code:

static OSStatus AudioRenderCallback(void *inRefCon, // this is the libpd example code AU render callback
                                    AudioUnitRenderActionFlags *ioActionFlags,
                                    const AudioTimeStamp *inTimeStamp,
                                    UInt32 inBusNumber,
                                    UInt32 inNumberFrames,
                                    AudioBufferList *ioData) {

    PdAudioUnit *pdAudioUnit = (PdAudioUnit *)inRefCon;
    Float32 *auBuffer = (Float32 *)ioData->mBuffers[0].mData;

    // These two lines are ‘copied and pasted’ to form the AEBlockFilter behaviour:
    int ticks = inNumberFrames/DEFDACBLKSIZE; // DEFDACBLKSIZE is 64, one tick equals one block of 64 samples 
    [PdBase processFloatWithInputBuffer:auBuffer outputBuffer:auBuffer ticks:ticks]; // sends/receive the audio to Pure Data, in blocks of 64
    return noErr;

Crucially, libpd enforces that the audio buffers must be sent as ‘ticks’, defined as a block of DEFDACBLKSIZE (64) frames. So, everything works perfectly until the number of incoming frames is not a multiple of 64.

Using the iPhone 6S internal speaker implies sample rate conversion through the render callback chain, providing, for instance, 235/236 frames into the render callback. As this does not divide down into an integer number of ‘ticks’ for the libpd API call, it causes audible clicks. I would imagine this scenario would be similar for a Bluetooth device at a different samplerate etc.

I’m surprised that this problem is demonstrable in the published libpd example.

This thread discusses a similar scenario, explaining that the render callback size cannot be relied upon, and suggests using the AEAudioControllerOptionUseHardwareSampleRate option to run at the hardware sample rate:

My understanding is that this option essentially ‘bypasses’ the sample rate conversion, implying that the client code must also run at the hardware samplerate. Indeed, using this option provides clean audio but as our ecosystem (samples etc.) are at 44.1kHz, everything plays back slightly fast.

I wonder if anyone has run into this problem with libpd, or more generally a workaround for this scenario? Am I even barking up the right tree here?!

Any feedback would be hugely appreciated :)

Sign In or Register to comment.