NewTimePitch with Mixer

2

I have a graph working that is very similar to the example app provided by Apple.

https://developer.apple.com/library/ios/samplecode/MixerHost/Listings/Classes_MixerHostAudio_m.html#//apple_ref/doc/uid/DTS40010210-Classes_MixerHostAudio_m-DontLinkElementID_6

My mixerNode is being fed by custom data (rather than guitar/beats) - but the setup is similar. Both buses are stereo on the mixer.

I am trying to time shift the content, but so far have been unsuccessful. I have tried adding a kAudioUnitSubType_NewTimePitch to the graph, but the graph fails to create whenever I add it. Is there any source example of how I might time shift with a mixer unit (shifting all buses)?

Here is some working code:

// Describe audio component
AudioComponentDescription output_desc;
bzero(&output_desc, sizeof(output_desc));
output_desc.componentType = kAudioUnitType_Output;
output_desc.componentSubType = self.componentSubType;
output_desc.componentFlags = 0;
output_desc.componentFlagsMask = 0;
output_desc.componentManufacturer = kAudioUnitManufacturer_Apple;


// multichannel mixer unit
AudioComponentDescription mixer_desc;
bzero(&mixer_desc, sizeof(mixer_desc));
mixer_desc.componentType = kAudioUnitType_Mixer;
mixer_desc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixer_desc.componentFlags = 0;
mixer_desc.componentFlagsMask = 0;
mixer_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

// Describe NewTimePitch component
AudioComponentDescription speed_desc;
bzero(&speed_desc, sizeof(speed_desc));
speed_desc.componentType = kAudioUnitType_FormatConverter;
speed_desc.componentSubType = kAudioUnitSubType_NewTimePitch;
speed_desc.componentFlags = 0;
speed_desc.componentFlagsMask = 0;
speed_desc.componentManufacturer = kAudioUnitManufacturer_Apple;


result = AUGraphAddNode(mGraph, &output_desc, &outputNode);
if (result) { printf("AUGraphNewNode 1 result %ld %4.4s\n", (long)result, (char*)&result); return; }

result = AUGraphAddNode(mGraph, &speed_desc, &timeNode );
if (result) { printf("AUGraphNewNode 2 result %ld %4.4s\n", (long)result, (char*)&result); return; }

result = AUGraphAddNode(mGraph, &mixer_desc, &mixerNode );
if (result) { printf("AUGraphNewNode 3 result %ld %4.4s\n", (long)result, (char*)&result); return; }

result = AUGraphConnectNodeInput(mGraph, mixerNode, 0, outputNode, 0);
if (result) { printf("AUGraphConnectNodeInput mixer-> time result %ld %4.4s\n", (long)result, (char*)&result); return; }

// open the graph AudioUnits are open but not initialized (no resource allocation occurs here)

result = AUGraphOpen(mGraph);
if (result) { printf("AUGraphOpen result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }

result = AUGraphNodeInfo(mGraph, mixerNode, NULL, &mMixer);
if (result) { printf("AUGraphNodeInfo mixer result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }

result = AUGraphNodeInfo(mGraph, timeNode, NULL, &mTime);
if (result) { printf("AUGraphNodeInfo time result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }

result = AUGraphNodeInfo(mGraph, outputNode, NULL, &mOutput);
if (result) { printf("AUGraphNodeInfo output result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }


UInt32 numbuses = 1;

result = AudioUnitSetProperty(mMixer, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &numbuses, sizeof(numbuses));
if (result) { printf("AudioUnitSetProperty bus result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }


for (int i = 0; i < numbuses; ++i) {
    // setup render callback struct
    AURenderCallbackStruct rcbs;
    rcbs.inputProc = &mixerInput;
    rcbs.inputProcRefCon = (__bridge void *)(outputStream);

    printf("set kAudioUnitProperty_SetRenderCallback for mixer input bus %d\n", i);

    // Set a callback for the specified node's specified input
    result = AUGraphSetNodeInputCallback(mGraph, mixerNode, i, &rcbs);
    // equivalent to AudioUnitSetProperty(mMixer, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, i, &rcbs, sizeof(rcbs));
    if (result) { printf("AUGraphSetNodeInputCallback result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }

    // set input stream format to what we want
    printf("set mixer input kAudioUnitProperty_StreamFormat for bus %d\n", i);

result = AudioUnitSetProperty(mMixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, mAudioFormat.streamDescription, sizeof(AudioStreamBasicDescription));
    if (result) { printf("AudioUnitSetProperty result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }
}

result = AudioUnitSetProperty(mMixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamInAudioFormat, sizeof(streamInAudioFormat));
if (result) { printf("AudioUnitSetProperty mixer result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }

result = AudioUnitSetProperty(mOutput, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &streamInAudioFormat, sizeof(streamInAudioFormat));
if (result) { printf("AudioUnitSetProperty output result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }

CAShow(mGraph);
// now that we've set everything up we can initialize the graph, this will also validate the connections
result = AUGraphInitialize(mGraph);
if (result) { printf("AUGraphInitialize result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }

This code works - I have a mixer that I can pump data into via the callback. You can see i have the time node created, but no matter where I insert it into the graph, it kills it. I can't set stream formats or anything else on it either.

Ideally i would want to do something like this:

result = AUGraphConnectNodeInput(mGraph, mixerNode, 0, timeNode, 0);
result = AUGraphConnectNodeInput(mGraph, timeNode, 0, outputNode, 0);

But that doesn't work.

Here is the output from that setup:

AudioUnitGraph 0x385003:
  Member Nodes:
    node 1: 'auou' 'vpio' 'appl', instance 0x134f40b10 O  
    node 2: 'aufc' 'nutp' 'appl', instance 0x134e733b0 O  
    node 3: 'aumx' 'mcmx' 'appl', instance 0x134ea71d0 O  
  Connections:
    node   3 bus   0 => node   2 bus   0  [ 2 ch,  44100 Hz, 'lpcm' (0x00000029) 32-bit little-endian float, deinterleaved]
    node   2 bus   0 => node   1 bus   0  [ 1 ch,      0 Hz, 'lpcm' (0x00000029) 32-bit little-endian float, deinterleaved]
  Input Callbacks:
    {0x100038ea0, 0x134f7f900} => node   3 bus   0  [2 ch, 44100 Hz]
  CurrentState:
    mLastUpdateError=0, eventsToProcess=F, isInitialized=F, isRunning=F
2016-01-07 23:21:32.230 R5ProTestbed[901:503908] 23:21:32.229 ERROR:    [0x19ff25000] 2776: ConnectAudioUnit failed with error -10868
2016-01-07 23:21:32.230 R5ProTestbed[901:503908] 23:21:32.230 ERROR:    [0x19ff25000] 1682: Initialize failed with error -10868
ios
core-audio
asked on Stack Overflow Jan 8, 2016 by toast • edited Jan 8, 2016 by toast

3 Answers

0

Your current graph is this according to CAShow: Mixer -> TimePitch -> VoiceProcess (Your output node is not in the graph)

You could not additionally connect the mixer output to something else In your code, you have so

result = AUGraphConnectNodeInput(mGraph, mixerNode, 0, timeNode, 0);

so you could not also add

result = AUGraphConnectNodeInput(mGraph, mixerNode, 0, outputNode, 0);

Having both of the above lines causes confuses the graph and it does not know where you want the output of the mixer to go.

Similarly, you have the mixer output connected to output node

result = AUGraphConnectNodeInput(mGraph, mixerNode, 0, outputNode, 0);

So you cannot also have the time node connected to the outputNode

result = AUGraphConnectNodeInput(mGraph, timeNode, 0, outputNode, 0);

Having both of these two confuses the graph because output Node has two inputs and it can only have one. It's like you are trying to make "Y" connections which you cannot do as far as connections are concerned.

You can put the output to one input so having both lines would conflict. Figure out where you want it in the chain and connect one output to exactly one input.
Then set the render callback to be the first node in the chain.

From your comments "I am trying to do mixer->newtimepitch->IO" You need to make three nodes,

  • connect the mixer output to the Time Pitch
  • connect the Time Pitch to the RemoteIO

You need 3 nodes. Two AUGraphConnectNodeInput() calls. Connect your render callback to the mixer. Something like this:

result = AUGraphConnectNodeInput(mGraph, mixerNode, 0, timeNode, 0);
result = AUGraphConnectNodeInput(mGraph, timeNode, 0, outputNode, 0); 

as you did. Ensure the other node connections are removed from your code. I could not tell if you removed the other connections or left them in and added more connections.

answered on Stack Overflow Jan 8, 2016 by jaybers • edited Jan 8, 2016 by jaybers
0

The default audio formats might not be appropriate if not manually configured with error checking. You need to set the output format of the mixer to the input format of the time-pitch unit, and the input format of your output node (RemoteIO?) to the output format of the time-pitch unit. The formats need to be configured before connecting units.

Do not set the format on the time pitch unit. Get the time pitch unit's formats, and set those formats on everything to which time pitch connects.

answered on Stack Overflow Jan 9, 2016 by hotpaw2 • edited Jan 12, 2016 by hotpaw2
0

hotpaw2 answered right after I figured it out myself. Thanks so much. I was setting the StreamFormat on the outputs of the mixer and IO, by stopping that, the converter was able to function.

//    result = AudioUnitSetProperty(mMixer, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamInAudioFormat, sizeof(streamInAudioFormat));
//    if (result) { printf("AudioUnitSetProperty mixer result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }

//    result = AudioUnitSetProperty(mOutput, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &streamInAudioFormat, sizeof(streamInAudioFormat));
//    if (result) { printf("AudioUnitSetProperty output result %ld %08lX %4.4s\n", (long)result, (long)result, (char*)&result); return; }
answered on Stack Overflow Jan 12, 2016 by toast

User contributions licensed under CC BY-SA 3.0