⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 maccoreaudio.cxx

📁 sloedgy open sip stack source code
💻 CXX
📖 第 1 页 / 共 4 页
字号:

   return err;
}



/*
 * CoreAudio Player callback function
 */
OSStatus PSoundChannelCoreAudio::PlayRenderProc(
         void*                         inRefCon,
         AudioUnitRenderActionFlags*   ioActionFlags,
         const struct AudioTimeStamp*  TimeStamp,
         UInt32                        inBusNumber,
         UInt32                        inNumberFrames,
         struct AudioBufferList*       ioData)
{  
   OSStatus err = noErr;
   PSoundChannelCoreAudio *This = 
         static_cast<PSoundChannelCoreAudio *>(inRefCon);

   if( This->state != running_  || This->mCircularBuffer->Empty() ) {
   //   PTRACE(1, __func__ << " terminating");
      return noErr;
   }

   //PTRACE(4, __func__ << ", frames " << inNumberFrames);

   err = AudioConverterFillComplexBuffer(This->converter,
           PSoundChannelCoreAudio::ComplexBufferFillPlayback, 
           This, 
           &inNumberFrames, // should be packets
           ioData,
           NULL /*outPacketDescription*/);
   checkStatus(err);


   /* now that cpu intensive work is done, make stereo from mono
    * assume non-interleaved ==> 1 buffer per channel */
   UInt32 len = ioData->mBuffers[0].mDataByteSize;
   if(len > 0 && This->state == running_){
      unsigned i = 1;
      while(i < ioData->mNumberBuffers) {
         memcpy(ioData->mBuffers[i].mData, ioData->mBuffers[0].mData, len);  
         ioData->mBuffers[i].mDataByteSize = len;
         i++;
      }
   }

   return err;
}




OSStatus PSoundChannelCoreAudio::RecordProc(
           void*                        inRefCon,
           AudioUnitRenderActionFlags*  ioActionFlags,
           const AudioTimeStamp*        inTimeStamp,
           UInt32                       inBusNumber,
           UInt32                       inNumberFrames,
           AudioBufferList *            ioData)
{
   //PTRACE(2,  __func__ << ", frames  " << inNumberFrames );

   OSStatus err = noErr;
   PSoundChannelCoreAudio *This =
                static_cast<PSoundChannelCoreAudio *>(inRefCon);
   CircularBuffer* inCircBuf   = This->mInputCircularBuffer;
	AudioStreamBasicDescription asbd = This->hwASBD;
   
   if(This->state != running_){
      return noErr;
   }

	if( This->mRecordInputBufferSize < inNumberFrames * asbd.mFramesPerPacket){
		PTRACE(1, "Allocated ABL RecordBuffer is too small ");
		inNumberFrames = This->mRecordInputBufferSize / asbd.mFramesPerPacket;
	}

   /* fetch the data from the microphone or other input device */
   AudioBufferList*  inputData =  This->mInputBufferList;
   err= AudioUnitRender(This->mAudioUnit,
            ioActionFlags,
            inTimeStamp, 
            inBusNumber,
            inNumberFrames, //# of frames  requested
            inputData);// Audio Buffer List to hold data    
   checkStatus(err);

   /* in any case reduce to mono by taking only the first buffer */
   AudioBuffer *audio_buf = &inputData->mBuffers[0];
   inCircBuf->Fill((char *)audio_buf->mData, audio_buf->mDataByteSize, 
         false, true); // do not wait, overwrite oldest frames 

   /*
    * Sample Rate Conversion(SRC)
    */
   unsigned int frames = inCircBuf->size() / This->hwASBD.mBytesPerFrame;


   /* given the number of Microphone frames how many 8kHz frames are
    * to expect, keeping a minimum buffer fill of MIN_INPUT_FILL frames to 
    * have some data handy in case the converter requests more Data */
   if(frames > MIN_INPUT_FILL){
      UInt32 pullFrames = int(float(frames-MIN_INPUT_FILL)/This->rateTimes8kHz);
      UInt32 pullBytes = MIN( This->converter_buffer_size,
                              pullFrames * This->pwlibASBD.mBytesPerFrame);

      UInt32 pullPackets = pullBytes / This->pwlibASBD.mBytesPerPacket;

      PTRACE(5, __func__ << " going to pull " << pullPackets << " packets");

      /* now pull the frames through the converter */
      AudioBufferList* outputData = This->mOutputBufferList;
      err = AudioConverterFillComplexBuffer(This->converter,
              PSoundChannelCoreAudio::ComplexBufferFillRecord, 
              This, 
              &pullPackets, 
              outputData, 
              NULL /*outPacketDescription*/);
      checkStatus(err);

      /* put the converted data into the main CircularBuffer for later 
       * fetching by the public Read function */
      audio_buf = &outputData->mBuffers[0];
      This->mCircularBuffer->Fill((char*)audio_buf->mData, 
            audio_buf->mDataByteSize, 
            false, true); // do not wait, overwrite oldest frames
   }

   return err;
}

/** 
 * Callback function called by the converter to fetch more date 
 */
OSStatus PSoundChannelCoreAudio::ComplexBufferFillRecord( 
         AudioConverterRef            inAudioConverter,
         UInt32                   *ioNumberDataPackets,
         AudioBufferList           *ioData,
         AudioStreamPacketDescription **outDataPacketDesc,
         void             *inUserData)
{

   OSStatus err = noErr;
   PSoundChannelCoreAudio *This = 
        static_cast<PSoundChannelCoreAudio *>(inUserData);
   CircularBuffer* inCircBuf   = This->mInputCircularBuffer;
   AudioStreamBasicDescription& hwASBD = This->hwASBD;
   

   // make sure it's always a multiple of packets
   UInt32 minPackets = MIN(*ioNumberDataPackets,
             inCircBuf->size() / hwASBD.mBytesPerPacket );
   UInt32 ioBytes = minPackets * hwASBD.mBytesPerPacket;
   

   //PTRACE(5, __func__ << " " << *ioNumberDataPackets << " requested " 
   //     << " fetching " << minPackets << " packets");

   if(ioBytes > This->converter_buffer_size){
      PTRACE(1, "converter_buffer too small " << ioBytes << " requested "
             << " but only " << This->converter_buffer_size << " fit in");
      ioBytes = This->converter_buffer_size;
   }
   
   ioBytes = inCircBuf->Drain((char*)This->converter_buffer, ioBytes, false);
   
   if(ioBytes  != minPackets * hwASBD.mBytesPerPacket) {
      // no more a multiple of packet problably !!!
      PTRACE(1, "Failed to fetch the computed number of packets");
   }

   ioData->mBuffers[0].mData = This->converter_buffer;
   ioData->mBuffers[0].mDataByteSize = ioBytes;

   // assuming non-interleaved or mono 
   *ioNumberDataPackets = ioBytes / hwASBD.mBytesPerPacket;

   return err;
   
}


OSStatus PSoundChannelCoreAudio::CallbackSetup(){
   OSStatus err = noErr;
   AURenderCallbackStruct callback;

   callback.inputProcRefCon = this;
   if (direction == Recorder) {
      callback.inputProc = RecordProc;
      /* kAudioOutputUnit stands for both Microphone/Speaker */
      err = AudioUnitSetProperty(mAudioUnit,
               kAudioOutputUnitProperty_SetInputCallback,
               kAudioUnitScope_Global,
               0,
               &callback,
               sizeof(callback));
            
   }
   else {
      callback.inputProc = PlayRenderProc;
      err = AudioUnitSetProperty(mAudioUnit, 
               kAudioUnitProperty_SetRenderCallback,
               kAudioUnitScope_Input,
               0,
               &callback,
               sizeof(callback));
   }
   checkStatus(err);
   return err;
}


/********* Function for configuring & initialization of audio units *********/

/**
 * Functions to open an AUHAL component and assign it the device indicated 
 * by deviceID. Conigures the unit for match user desired format  as close as
 * possible while not assuming special hardware. (able to change sampling rate)
 */
OSStatus PSoundChannelCoreAudio::SetupInputUnit(AudioDeviceID in)
{  
   OSStatus err = noErr;
         
   Component comp;            
   ComponentDescription desc;

   //There are several different types of Audio Units.
   //Some audio units serve as Outputs, Mixers, or DSP
   //units. See AUComponent.h for listing
   desc.componentType = kAudioUnitType_Output;

   //Every Component has a subType, which will give a clearer picture
   //of what this components function will be.
   desc.componentSubType = kAudioUnitSubType_HALOutput;

   //all Audio Units in AUComponent.h must use 
   //"kAudioUnitManufacturer_Apple" as the Manufacturer
   desc.componentManufacturer = kAudioUnitManufacturer_Apple;
   desc.componentFlags = 0;
   desc.componentFlagsMask = 0;

   //Finds a component that meets the desc spec's
   comp = FindNextComponent(NULL, &desc);
   if (comp == NULL) return kAudioCodecUnspecifiedError;

   //gains access to the services provided by the component
   err = OpenAComponent(comp, &mAudioUnit);
   checkStatus(err);

   err = EnableIO();
   checkStatus(err);

   err= SetDeviceAsCurrent(in);
   checkStatus(err);

   return err;
}
          
/**
 * By default all units are configured for output. If we want to use a 
 * unit for input we must configure it, before assigning the corresponding
 * device to it. This to make sure that it asks the device driver for the ASBD
 * of the input direction.
 */
OSStatus PSoundChannelCoreAudio::EnableIO()
{
   OSStatus err = noErr;
   UInt32 enableIO;

   ///////////////
   //ENABLE IO (INPUT)
   //You must enable the Audio Unit (AUHAL) for input and disable output 
   //BEFORE setting the AUHAL's current device.

   //Enable input on the AUHAL
   enableIO = 1;
   err =  AudioUnitSetProperty(mAudioUnit,
          kAudioOutputUnitProperty_EnableIO,
          kAudioUnitScope_Input,
          1, // input element
          &enableIO,
          sizeof(enableIO));
   checkStatus(err);

   //disable Output on the AUHAL
   enableIO = 0;
   err = AudioUnitSetProperty(mAudioUnit,
         kAudioOutputUnitProperty_EnableIO,
         kAudioUnitScope_Output,
         0,   //output element
         &enableIO,
         sizeof(enableIO));
   return err;
}
          

/*
 * Functions to open an AUHAL component and assign it the device indicated 
 * by deviceID. The builtin converter is configured to accept non-interleaved
 * data.
 */
OSStatus PSoundChannelCoreAudio::SetupOutputUnit(AudioDeviceID out){
   OSStatus err;

  //An Audio Unit is a OS component
  //The component description must be setup, then used to 
  //initialize an AudioUnit
  ComponentDescription desc;  

  desc.componentType = kAudioUnitType_Output;
  desc.componentSubType = kAudioUnitSubType_HALOutput;
  //desc.componentSubType = kAudioUnitSubType_DefaultOutput;
  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  desc.componentFlags = 0;
  desc.componentFlagsMask = 0;
  
  //Finds an component that meets the desc spec's 
  Component comp = FindNextComponent(NULL, &desc);  
  if (comp == NULL) return kAudioCodecUnspecifiedError;
    
  //gains access to the services provided by the component
  err = OpenAComponent(comp, &mAudioUnit);  
  checkStatus(err);

  //enableIO not needed, because output is default

  err = SetDeviceAsCurrent(out);
  return err;
}


OSStatus PSoundChannelCoreAudio::SetDeviceAsCurrent(AudioDeviceID id)
{                       
   UInt32 size = sizeof(AudioDeviceID);
   OSStatus err = noErr;

   //get the default device if the device id not specified // bogus 
   if(id == kAudioDeviceUnknown) 
   {  
      if(direction == Recorder) {
         err = AudioHardwareGetProperty(
                  kAudioHardwarePropertyDefaultOutputDevice, &size, &id);
      } else {
         err = AudioHardwareGetProperty(
               kAudioHardwarePropertyDefaultInputDevice, &size, &id);
      }
      checkStatus(err);   
   }                   

   mDeviceID = id;

   // Set the Current Device to the AUHAL.
   // this should be done only after IO has been enabled on the AUHAL.
   // This means the direction selected, to make sure the ASBD for the proper
	// direction is requested
   err = AudioUnitSetProperty(mAudioUnit,
       kAudioOutputUnitProperty_CurrentDevice,
       kAudioUnitScope_Global,
       0,  
       &mDeviceID,
       sizeof(mDeviceID));
   checkStatus(err);

   return err;
}  



/*
 * The major task of Open() is to find the matching device ID.
 *
 */
BOOL PSoundChannelCoreAudio::Open(const PString & deviceName,
              Directions dir,
              unsigned numChannels,
              unsigned sampleRate,
              unsigned bitsPerSample)
{
  OSStatus err;

  /* Save whether this is a Player or Recorder */
  this->direction = dir;

  /*
   * Init the AudioUnit and assign it to the requested AudioDevice
   */
  if (strcmp(deviceName, CA_DUMMY_DEVICE_NAME) == 0) {
     /* Dummy device */
     PTRACE(6, "Dummy device " << direction);
     mDeviceID = kAudioDeviceUnknown;
  } else {

    AudioDeviceID deviceID = GetDeviceID(deviceName, direction == Recorder);
    if(direction == Player)
       err = SetupOutputUnit(deviceID);
    else 
       err = SetupInputUnit(deviceID);
    checkStatus(err);
  }


  /*
   * Add a listener to print current volume setting in case it is changed
   */
  err = AudioDeviceAddPropertyListener(mDeviceID,  1, 	
					kAudioPropertyWildcardSection, kAudioDevicePropertyVolumeScalar, 
					VolumeChangePropertyListener, (void *)this);
  checkStatus(err);


  //os_handle = mDeviceID;  // tell PChanne::IsOpen() that the channel is open.
  os_handle = 8;  // tell PChannel::IsOpen() that the channel is open.
  state = open_;
  return SetFormat(numChannels, sampleRate, bitsPerSample);
}

/* 
 * Audio Unit for the Hardware Abstraction Layer(AUHAL) have builtin 
 * converters. It would be nice if we could configure it to spit out/consume 
 * the data in the format the data are passed by Read/Write function calls.
 *
 * Unfortunately this is not possible for the microphone, because this 
 * converter does not have a buffer inside, so it cannot do any Sample
 * Rate Conversion(SRC). We would have to set the device nominal sample
 * rate itself to 8kHz. Unfortunately not all microphones can do that,
 * so this is not an option. Maybe there will be some change in the future
 * by Apple, so we leave it here. 
 *
 * For the output we have the problem that we do not know currently how
 * to configure the channel map so that a mono input channel gets copied 
 * to all output channels, so we still have to do the conversion ourselves
 * to copy the result onto all output channels.
 *
 * Still the builtin converters can be used for something useful, such as 
 * converting from interleaved -> non-interleaved and to reduce the number of 
 * bits per sample to save space and time while copying 
 */

/* 
 * Configure the builtin AudioConverter to accept non-interleaved data.
 * Turn off SRC by setting the same sample rate at both ends.
 * See also general notes above

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -