在 iPhone 上以编程方式设置音频平移不起作用
Setting audio pan programmatically on iPhone not working
在我的代码中,我设置了一个包含两个音频单元的音频处理图:一个 I/O 单元和一个多声道混音器单元。首先是I/O单元:
bool RtApiIos::setupAU(void *handle, AURenderCallbackStruct inRenderProc, AudioStreamBasicDescription &outFormat)
{
AudioUnitHandle *auHandle = (AudioUnitHandle *)handle;
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
AudioComponent comp = AudioComponentFindNext( NULL, &desc );
if (AudioComponentInstanceNew(comp, &auHandle->audioUnit))
return false;
if (AudioUnitSetProperty(auHandle->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inRenderProc, sizeof(inRenderProc)))
return false;
然后是多通道混频器单元:
AudioComponentDescription mixerDesc;
mixerDesc.componentType = kAudioUnitType_Mixer;
mixerDesc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
mixerDesc.componentFlags = 0;
mixerDesc.componentFlagsMask = 0;
comp = AudioComponentFindNext( NULL, &mixerDesc );
if (AudioComponentInstanceNew(comp, &auHandle->mixerUnit))
return false;
UInt32 busCount = 1;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(busCount)) )
return false;
if (AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inRenderProc, sizeof(inRenderProc)))
return false;
size = sizeof(localFormat);
if (AudioUnitGetProperty(auHandle->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &localFormat, &size ))
return false;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &localFormat.mSampleRate, sizeof(localFormat.mSampleRate)))
return false;
我启动处理图,然后开始。音频播放正常,但例程的最后一次调用对声像没有任何声音效果(并且 "err" == 0)。
// Declare and instantiate an audio processing graph
NewAUGraph(&auHandle->processingGraph);
AUNode mixerNode;
AUGraphAddNode(auHandle->processingGraph, &mixerDesc, &mixerNode);
AUNode ioNode;
AUGraphAddNode(auHandle->processingGraph, &desc, &ioNode);
// Indirectly performs audio unit instantiation
if (AUGraphOpen(auHandle->processingGraph))
return false;
if( AUGraphConnectNodeInput(auHandle->processingGraph, mixerNode, 0, ioNode, 0) )
return false;
if (AUGraphInitialize(auHandle->processingGraph))
return false;
AudioUnitParameterValue panValue = 0.9; // panned almost dead-right. possible values are between -1 and 1
OSStatus err = AudioUnitSetParameter(auHandle->mixerUnit, kMultiChannelMixerParam_Pan, kAudioUnitScope_Output, 0, panValue, 0);
if (err != noErr) {
//error setting pan
}
}
我做错了什么?
谢谢!!
在 Apple 的旧 MixerHost 示例项目的大力帮助下,完成了这项工作。
首先创建了一个结构来捆绑我的资源:
struct AudioUnitHandle {
AudioUnit audioUnit;
AUGraph processingGraph;
AudioUnit mixerUnit;
};
指定我的音频单元描述,创建一个音频处理图,并添加两个节点 - 一个用于我的 I/O 节点,一个用于多声道混音器:
bool setupAU(void *handle, AURenderCallbackStruct inRenderProc, AudioStreamBasicDescription &outFormat)
{
AudioUnitHandle *auHandle = (AudioUnitHandle *)handle;
// I/O Unit
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
// Multichannel mixer unit
AudioComponentDescription mixerDesc;
mixerDesc.componentType = kAudioUnitType_Mixer;
mixerDesc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
mixerDesc.componentFlags = 0;
mixerDesc.componentFlagsMask = 0;
//............................................................................
// Create a new audio processing graph.
if( NewAUGraph(&auHandle->processingGraph) )
return false;
//............................................................................
// Add nodes to the audio processing graph.
AUNode ioNode;
if( AUGraphAddNode(auHandle->processingGraph, &desc, &ioNode) )
return false;
AUNode mixerNode;
if( AUGraphAddNode(auHandle->processingGraph, &mixerDesc, &mixerNode) )
return false;
//............................................................................
// Open the audio processing graph
// Following this call, the audio units are instantiated but not initialized
// (no resource allocation occurs and the audio units are not in a state to
// process audio).
if( AUGraphOpen(auHandle->processingGraph) )
return false;
然后对多通道调音台本身进行大量设置:
// Obtain the mixer unit instance from its corresponding node.
if( AUGraphNodeInfo(auHandle->processingGraph, mixerNode, NULL, &auHandle->mixerUnit) )
return false;
//............................................................................
// Multichannel Mixer unit Setup
UInt32 busCount = 1; // bus count for mixer unit input
UInt16 busNumber = 0;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, busNumber, &busCount, sizeof(busCount)) )
return false;
// Increase the maximum frames per slice allows the mixer unit to accommodate the
// larger slice size used when the screen is locked.
UInt32 maximumFramesPerSlice = 4096;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, busNumber, &maximumFramesPerSlice, sizeof(maximumFramesPerSlice)) )
return false;
// Set a callback for the mixer node's specified input
if( AUGraphSetNodeInputCallback(auHandle->processingGraph, mixerNode, busNumber, &inRenderProc) )
return false;
// Set the format of bus 0 of the mixer unit
AudioStreamBasicDescription localFormat = {0};
UInt32 size = sizeof(localFormat);
if (AudioUnitGetProperty(auHandle->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, busNumber, &localFormat, &size ))
return false;
localFormat.mSampleRate = outFormat.mSampleRate;
localFormat.mChannelsPerFrame = outFormat.mChannelsPerFrame;
localFormat.mFormatID = outFormat.mFormatID;
localFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved ;
localFormat.mBitsPerChannel = outFormat.mBitsPerChannel;
localFormat.mFramesPerPacket = outFormat.mFramesPerPacket;
localFormat.mBytesPerFrame = outFormat.mBytesPerFrame;
localFormat.mBytesPerPacket = outFormat.mBytesPerPacket;
OSStatus err = 0;
err = AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, busNumber, &localFormat, size);
if(noErr != err) //kAudioUnitErr_FormatNotSupported = -10868,
return false;
// Set the mixer unit's output sample rate format. This is the only aspect of the output stream
// format that must be explicitly set.
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &localFormat.mSampleRate, sizeof(localFormat.mSampleRate)) )
return false;
最后,连接节点并初始化处理图:
// Connect the nodes of the audio processing graph
// Connect the mixer output to the input of the I/O unit output element
if( AUGraphConnectNodeInput(auHandle->processingGraph,
mixerNode, //Source node
0, //Source node output bus number
ioNode, //Destination node
0) ) //Destination node input bus number
return false;
//............................................................................
// Initialize the audio processing graph, configure audio data stream formats for
// each input and output, and validate the connections between audio units.
if (AUGraphInitialize(auHandle->processingGraph))
return false;
return true;
}
从这里开始和停止这整个混乱的事情非常简单:
//Start
OSStatus err = AUGraphStart( auHandle->processingGraph );
//Stop
OSStatus err = AUGraphStop( auHandle->processingGraph );
哇哦!
在我的代码中,我设置了一个包含两个音频单元的音频处理图:一个 I/O 单元和一个多声道混音器单元。首先是I/O单元:
bool RtApiIos::setupAU(void *handle, AURenderCallbackStruct inRenderProc, AudioStreamBasicDescription &outFormat)
{
AudioUnitHandle *auHandle = (AudioUnitHandle *)handle;
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
AudioComponent comp = AudioComponentFindNext( NULL, &desc );
if (AudioComponentInstanceNew(comp, &auHandle->audioUnit))
return false;
if (AudioUnitSetProperty(auHandle->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inRenderProc, sizeof(inRenderProc)))
return false;
然后是多通道混频器单元:
AudioComponentDescription mixerDesc;
mixerDesc.componentType = kAudioUnitType_Mixer;
mixerDesc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
mixerDesc.componentFlags = 0;
mixerDesc.componentFlagsMask = 0;
comp = AudioComponentFindNext( NULL, &mixerDesc );
if (AudioComponentInstanceNew(comp, &auHandle->mixerUnit))
return false;
UInt32 busCount = 1;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(busCount)) )
return false;
if (AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inRenderProc, sizeof(inRenderProc)))
return false;
size = sizeof(localFormat);
if (AudioUnitGetProperty(auHandle->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &localFormat, &size ))
return false;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &localFormat.mSampleRate, sizeof(localFormat.mSampleRate)))
return false;
我启动处理图,然后开始。音频播放正常,但例程的最后一次调用对声像没有任何声音效果(并且 "err" == 0)。
// Declare and instantiate an audio processing graph
NewAUGraph(&auHandle->processingGraph);
AUNode mixerNode;
AUGraphAddNode(auHandle->processingGraph, &mixerDesc, &mixerNode);
AUNode ioNode;
AUGraphAddNode(auHandle->processingGraph, &desc, &ioNode);
// Indirectly performs audio unit instantiation
if (AUGraphOpen(auHandle->processingGraph))
return false;
if( AUGraphConnectNodeInput(auHandle->processingGraph, mixerNode, 0, ioNode, 0) )
return false;
if (AUGraphInitialize(auHandle->processingGraph))
return false;
AudioUnitParameterValue panValue = 0.9; // panned almost dead-right. possible values are between -1 and 1
OSStatus err = AudioUnitSetParameter(auHandle->mixerUnit, kMultiChannelMixerParam_Pan, kAudioUnitScope_Output, 0, panValue, 0);
if (err != noErr) {
//error setting pan
}
}
我做错了什么? 谢谢!!
在 Apple 的旧 MixerHost 示例项目的大力帮助下,完成了这项工作。
首先创建了一个结构来捆绑我的资源:
struct AudioUnitHandle {
AudioUnit audioUnit;
AUGraph processingGraph;
AudioUnit mixerUnit;
};
指定我的音频单元描述,创建一个音频处理图,并添加两个节点 - 一个用于我的 I/O 节点,一个用于多声道混音器:
bool setupAU(void *handle, AURenderCallbackStruct inRenderProc, AudioStreamBasicDescription &outFormat)
{
AudioUnitHandle *auHandle = (AudioUnitHandle *)handle;
// I/O Unit
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
// Multichannel mixer unit
AudioComponentDescription mixerDesc;
mixerDesc.componentType = kAudioUnitType_Mixer;
mixerDesc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixerDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
mixerDesc.componentFlags = 0;
mixerDesc.componentFlagsMask = 0;
//............................................................................
// Create a new audio processing graph.
if( NewAUGraph(&auHandle->processingGraph) )
return false;
//............................................................................
// Add nodes to the audio processing graph.
AUNode ioNode;
if( AUGraphAddNode(auHandle->processingGraph, &desc, &ioNode) )
return false;
AUNode mixerNode;
if( AUGraphAddNode(auHandle->processingGraph, &mixerDesc, &mixerNode) )
return false;
//............................................................................
// Open the audio processing graph
// Following this call, the audio units are instantiated but not initialized
// (no resource allocation occurs and the audio units are not in a state to
// process audio).
if( AUGraphOpen(auHandle->processingGraph) )
return false;
然后对多通道调音台本身进行大量设置:
// Obtain the mixer unit instance from its corresponding node.
if( AUGraphNodeInfo(auHandle->processingGraph, mixerNode, NULL, &auHandle->mixerUnit) )
return false;
//............................................................................
// Multichannel Mixer unit Setup
UInt32 busCount = 1; // bus count for mixer unit input
UInt16 busNumber = 0;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, busNumber, &busCount, sizeof(busCount)) )
return false;
// Increase the maximum frames per slice allows the mixer unit to accommodate the
// larger slice size used when the screen is locked.
UInt32 maximumFramesPerSlice = 4096;
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, busNumber, &maximumFramesPerSlice, sizeof(maximumFramesPerSlice)) )
return false;
// Set a callback for the mixer node's specified input
if( AUGraphSetNodeInputCallback(auHandle->processingGraph, mixerNode, busNumber, &inRenderProc) )
return false;
// Set the format of bus 0 of the mixer unit
AudioStreamBasicDescription localFormat = {0};
UInt32 size = sizeof(localFormat);
if (AudioUnitGetProperty(auHandle->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, busNumber, &localFormat, &size ))
return false;
localFormat.mSampleRate = outFormat.mSampleRate;
localFormat.mChannelsPerFrame = outFormat.mChannelsPerFrame;
localFormat.mFormatID = outFormat.mFormatID;
localFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved ;
localFormat.mBitsPerChannel = outFormat.mBitsPerChannel;
localFormat.mFramesPerPacket = outFormat.mFramesPerPacket;
localFormat.mBytesPerFrame = outFormat.mBytesPerFrame;
localFormat.mBytesPerPacket = outFormat.mBytesPerPacket;
OSStatus err = 0;
err = AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, busNumber, &localFormat, size);
if(noErr != err) //kAudioUnitErr_FormatNotSupported = -10868,
return false;
// Set the mixer unit's output sample rate format. This is the only aspect of the output stream
// format that must be explicitly set.
if( AudioUnitSetProperty(auHandle->mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &localFormat.mSampleRate, sizeof(localFormat.mSampleRate)) )
return false;
最后,连接节点并初始化处理图:
// Connect the nodes of the audio processing graph
// Connect the mixer output to the input of the I/O unit output element
if( AUGraphConnectNodeInput(auHandle->processingGraph,
mixerNode, //Source node
0, //Source node output bus number
ioNode, //Destination node
0) ) //Destination node input bus number
return false;
//............................................................................
// Initialize the audio processing graph, configure audio data stream formats for
// each input and output, and validate the connections between audio units.
if (AUGraphInitialize(auHandle->processingGraph))
return false;
return true;
}
从这里开始和停止这整个混乱的事情非常简单:
//Start
OSStatus err = AUGraphStart( auHandle->processingGraph );
//Stop
OSStatus err = AUGraphStop( auHandle->processingGraph );
哇哦!