Puppy
Puppy

Reputation: 146930

Cannot set the format on a format converter audio unit

I'm trying to set up a very simple AU graph- simply a RemoteIO with a varispeed attached to it for output. Unfortunately, I can't seem to set the audio format on the varispeed unit. The other examples I've seen seem to just set the format without trouble. The exact error that I get back is unsupported format. However, the format converter inside the RemoteIO unit will happily take my format, which is a dead simple 16khz mono 16bit signed int PCM format.

Here's the code that I'm currently trying to make work:

 // Describe the output unit.
AudioComponentDescription inputDescription = {0};   
inputDescription.componentType = kAudioUnitType_Output;
inputDescription.componentSubType = kAudioUnitSubType_RemoteIO;
inputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
AUNode ioNode;
CheckError(self, AUGraphAddNode(_graph, &inputDescription, &ioNode), "Couldn't add ioNode to AU graph");

AudioComponentDescription varispeedDescription = {0};
varispeedDescription.componentType = kAudioUnitType_FormatConverter;
varispeedDescription.componentSubType = kAudioUnitSubType_Varispeed;
varispeedDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
AUNode varispeedNode;
CheckError(self, AUGraphAddNode(_graph, &varispeedDescription, &varispeedNode), "Couldn't add varispeed node to AU graph");

CheckError(self, AUGraphOpen(_graph), "Couldn't open AU graph");
AudioUnit varispeedUnit;
CheckError(self, AUGraphNodeInfo(_graph, varispeedNode, NULL, &varispeedUnit), "Couldn't get varispeed audio unit");

CheckError(self, AUGraphNodeInfo(_graph, ioNode, NULL, &_ioUnit), "Couldn't get io unit");

// Enable input
// TODO: Conditionally disable input if option has not been specified
UInt32 one = 1;
CheckError(self, AudioUnitSetProperty(_ioUnit,
                                 kAudioOutputUnitProperty_EnableIO,
                                 kAudioUnitScope_Input,
                                 kInputBus,
                                 &one,
                                 sizeof(one)), "Couldn't enable IO on the input scope of output unit");

// TODO: check this works on iOS!
_format.mBitsPerChannel = 16;
_format.mBytesPerFrame = 2;
_format.mBytesPerPacket = 2;
_format.mChannelsPerFrame = 1;
_format.mFramesPerPacket = 1;
_format.mReserved = 0;
_format.mSampleRate = 16000.0;
_format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
_format.mFormatID = kAudioFormatLinearPCM;

self.samplingRate = _format.mSampleRate;
UInt32 size;
size = sizeof(AudioStreamBasicDescription);
CheckError(self, AudioUnitSetProperty(_ioUnit,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Output,
                                kInputBus,
                                &_format,
                                size),
           "Couldn't set the ASBD on the audio unit (after setting its sampling rate)");
size = sizeof(AudioStreamBasicDescription);
CheckError(self, AudioUnitSetProperty(varispeedUnit,
                                      kAudioUnitProperty_StreamFormat,
                                      kAudioUnitScope_Output,
                                      0,
                                      &_format,
                                      size),
           "Couldn't set the ASBD on the audio unit (after setting its sampling rate)");
size = sizeof(AudioStreamBasicDescription);
CheckError(self, AudioUnitSetProperty(varispeedUnit,
                                      kAudioUnitProperty_StreamFormat,
                                      kAudioUnitScope_Input,
                                      0,
                                      &_format,
                                      size),
           "Couldn't set the ASBD on the audio unit (after setting its sampling rate)");

I also tried simply setting the input format of the RemoteIO's output bus, which worked fine for the converter's output format, but it still won't accept my format as the input format for the converter.

How can I link up the varispeed and the RemoteIO with my desired format?

Upvotes: 2

Views: 410

Answers (1)

Puppy
Puppy

Reputation: 146930

I found buried on an old mailing list that the Varispeed unit is floating-point only, and doesn't actually do the format conversions, despite the main type being FormatConverter. The solution is to use another converter with the dedicated converter subtype to convert my own format into the FP format the Varispeed wants. The format restrictions for Varispeed are not actually documented anywhere as near as I can tell so I have little idea what format it actually wants, except that the speaker output format worked for me and it should be a floating-point linear PCM format.

And just for reference, the Varispeed unit does not do any pitch correction which sounds ridiculous when changing playback rate. You can use the NewTimePitch unit instead which responds to the same varispeed parameter but the outcome does not sound like crap.

Upvotes: 2

Related Questions