如何 enable/disable 从聚合 CoreAudio 设备输入或输出通道?

How to enable/disable input or output channels from an aggregate CoreAudio device?

我已经仔细阅读了这个帖子中的问答:

它似乎缺少有关解决方案的信息: 我创建了一个包含多个音频设备的聚合设备。当调用核心音频以接收流的数量(使用 kAudioDevicePropertyStreams)时,return 值始终为 1。我还尝试了 CoreAudio Utility 类 中的实现:CAHALAudioDevice::GetIOProcStreamUsage。我仍然看不到如何访问子流和 disable/enable 它们,如此处所述。 完成 disable/enable 个子流需要做什么?

编辑

这里CAHALAudioDevice::GetIOProcStreamUsage供参考:

void    CAHALAudioDevice::GetIOProcStreamUsage(AudioDeviceIOProcID 
inIOProcID, bool inIsInput, bool* outStreamUsage) const
{
    //  make an AudioHardwareIOProcStreamUsage the right size
    UInt32 theNumberStreams = GetNumberStreams(inIsInput);
    UInt32 theSize = SizeOf32(void*) + SizeOf32(UInt32) + (theNumberStreams * SizeOf32(UInt32));
    CAAutoFree<AudioHardwareIOProcStreamUsage> theStreamUsage(theSize);

    //  set it up
    theStreamUsage->mIOProc = reinterpret_cast<void*>(inIOProcID);
    theStreamUsage->mNumberStreams = theNumberStreams;

    //  get the property
    CAPropertyAddress theAddress(kAudioDevicePropertyIOProcStreamUsage, inIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput);
    GetPropertyData(theAddress, 0, NULL, theSize, theStreamUsage);

    //  fill out the return value
    for(UInt32 theIndex = 0; theIndex < theNumberStreams; ++theIndex)
    {
        outStreamUsage[theIndex] = (theStreamUsage->mStreamIsOn[theIndex] != 0);
    }
}

作为参考,这里是我的程序使用的函数来完成 中描述的结果:

// Tell CoreAudio which input (or output) streams we actually want to use in our device
// @param devID the CoreAudio audio device ID of the aggregate device to modify
// @param ioProc the rendering callback-function (as was passed to AudioDeviceCreateIOProcID()'s second argument)
// @param scope either kAudioObjectPropertyScopeInput or kAudioObjectPropertyScopeOutput depending on which type of channels we want to modify
// @param numValidChannels how many audio channels in the aggregate device we want to actually use
// @param rightJustify if true, we want to use the last (numValidChannels) in the device; if false we want to use the first (numValidChannels) in the device
// @returns 0 on success or -1 on error
// @note this function doesn't change the layout of the audio-sample data in the audio-render callback; rather it causes some channels of audio in the callback to become zero'd out/unused.
int SetProcStreamUsage(AudioDeviceID devID, void * ioProc, AudioObjectPropertyScope scope, int numValidChannels, bool rightJustify)
{  
   const AudioObjectPropertyAddress sizesAddress =
   {                    
       kAudioDevicePropertyStreamConfiguration, 
       scope, 
       kAudioObjectPropertyElementMaster
   };    
   Uint32 streamSizesDataSize = 0;
   OSStatus err = AudioObjectGetPropertyDataSize(devID, &sizesAddress, 0, NULL, &streamSizesDataSize);
   if (err != noErr)
   {        
      printf("SetProcStreamUsage(%u,%i,%i):  AudioObjectGetPropertyDataSize(kAudioDevicePropertyStreamConfiguration) failed!\n", (unsigned int) devID, scope, rightJustify);
      return -1;  // ("AudioObjectGetPropertyDataSize(kAudioDevicePropertyStreamConfiguration) failed");
   }           
               
   const AudioObjectPropertyAddress usageAddress = 
   {              
       kAudioDevicePropertyIOProcStreamUsage,
       scope,        
       kAudioObjectPropertyElementMaster 
   };                
                        
   Uint32 streamUsageDataSize = 0;
   err = AudioObjectGetPropertyDataSize(devID, &usageAddress, 0, NULL, &streamUsageDataSize);
   if (err != noErr)
   {              
      printf("SetProcStreamUsage(%u,%i,%i):  AudioObjectGetPropertyDataSize(kAudioDevicePropertyIOProcStreamUsage) failed!\n", (unsigned int) devID, scope, rightJustify);
      return -1;  // ("AudioObjectGetPropertyDataSize(kAudioDevicePropertyIOProcStreamUsage) failed");
   }                 
                     
   AudioBufferList * bufList = (AudioBufferList*) malloc(streamSizesDataSize);  // using malloc() because the object-size is variable
   if (bufList)         
   {                 
      int ret;    
      
      err = AudioObjectGetPropertyData(devID, &sizesAddress, 0, NULL, &streamSizesDataSize, bufList);
      if (err == noErr) 
      {           
         AudioHardwareIOProcStreamUsage * streamUsage = (AudioHardwareIOProcStreamUsage *) malloc(streamUsageDataSize);  // using malloc() because the object-size is variable
         if (streamUsage)
         {           
            streamUsage->mIOProc = ioProc;
            err = AudioObjectGetPropertyData(devID, &usageAddress, 0, NULL, &streamUsageDataSize, streamUsage);
            if (err == noErr)
            {  
               if (bufList->mNumberBuffers == streamUsage->mNumberStreams)
               {  
                  Int32 numChannelsLeft = numValidChannels;
                  if (rightJustify)
                  {
                     // We only want streams corresponding to the last (N) channels to be enabled
                     for (Int32 i=streamUsage->mNumberStreams-1; i>=0; i--) 
                     {
                        streamUsage->mStreamIsOn[i] = (numChannelsLeft > 0);
                        numChannelsLeft -= bufList->mBuffers[i].mNumberChannels;
                     }
                  }
                  else
                  {
                     // We only want streams corresponding to the first (N) channels to be enabled
                     for (Uint32 i=0; i<streamUsage->mNumberStreams; i++)
                     {
                        streamUsage->mStreamIsOn[i] = (numChannelsLeft > 0);
                        numChannelsLeft -= bufList->mBuffers[i].mNumberChannels;
                     }
                  }

                  // Now set the stream-usage per our update, above
                  err = AudioObjectSetPropertyData(devID, &usageAddress, 0, NULL, streamUsageDataSize, streamUsage);
                  if (err != noErr)
                  {
                     printf("SetProcStreamUsage(%u,%i,%i):  AudioObjectSetPropertyData(kAudioDevicePropertyIOProcStreamUsage) failed!\n", (unsigned int) devID, scope, rightJustify);
                     ret = -1;  // ("AudioObjectSetPropertyData(kAudioDevicePropertyIOProcStreamUsage) failed");
                  }
               }
               else
               {
                  printf("SetProcStreamUsage(%u,%i,%i):  #Buffers (%u) doesn't match #Streams (%u)!\n", (unsigned int) devID, scope, rightJustify, bufList->mNumberBuffers, streamUsage->mNumberStreams);
                  ret = -1;
               }
            }
            else
            {
               printf("SetProcStreamUsage(%u,%i,%i):  AudioObjectSetPropertyData(kAudioDevicePropertyIOProcStreamUsage) failed!\n", (unsigned int) devID, scope, rightJustify);
               ret = -1;  // ("AudioObjectGetPropertyData(kAudioDevicePropertyIOProcStreamUsage) failed");
            }

            free(streamUsage);
         }
         else ret = -1;  // out of memory?
      }
      else
      {
         printf("SetProcStreamUsage(%u,%i,%i):  AudioObjectGetPropertyData(kAudioDevicePropertyStreamConfiguration) failed!\n", (unsigned int) devID, scope, rightJustify);
         ret = -1;  // ("AudioObjectGetPropertyData(kAudioDevicePropertyStreamConfiguration) failed");
      }

      free(bufList);
      return ret;
   }
   else return -1; // out of memory?
}