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

📄 maccoreaudio.cxx

📁 sloedgy open sip stack source code
💻 CXX
📖 第 1 页 / 共 4 页
字号:
/*
 * maccoreaudio.cxx
 *
 * Copyright (c) 2004 Network for Educational Technology ETH
 *
 * Written by Hannes Friederich, Andreas Fenkart.
 * Based on work of Shawn Pai-Hsiang Hsiao
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *          
 */
 
#pragma implementation "maccoreaudio.h" 

#include <ptlib/unix/ptlib/maccoreaudio.h>
#include <iostream>  // used for Volume Listener
 


PCREATE_SOUND_PLUGIN(CoreAudio, PSoundChannelCoreAudio);
 
namespace PWLibStupidOSXHacks
{
	int loadCoreAudioStuff;
}


/************** util *******************/


#ifdef PTRACING

// should also produce output when ptracing is disabled
#define checkStatus( err ) \
    if(err) {\
      OSStatus error = static_cast<OSStatus>(err);\
      PTRACE(1, "CoreAudio Error " << __func__ << " "  \
           <<  error   << "("  << (char*)&err <<  ")" ); \
    }         
   
ostream& operator<<(ostream &os, AudioStreamBasicDescription &inDesc)
{
   os << "- - - - - - - - - - - - - - - - - - - -\n";
   os << "  Sample Rate: " << inDesc.mSampleRate << endl;
   os << "  Format ID: " << (char*)&inDesc.mFormatID << endl;
   os << "  Format Flags: " << hex << inDesc.mFormatFlags << dec << endl;
   os << "  Bytes per Packet: " << inDesc.mBytesPerPacket << endl;
   os << "  Frames per Packet: " << inDesc.mFramesPerPacket << endl;
   os << "  Bytes per Frame: " << inDesc.mBytesPerFrame << endl;
   os << "  Channels per Frame: " << inDesc.mChannelsPerFrame << endl; 
   os << "  Bits per Channel: " << inDesc.mBitsPerChannel << endl;
   os << "- - - - - - - - - - - - - - - - - - - -\n";
   return os;
}


ostream& operator<<(ostream &os, PSoundChannel::Directions &dir)
{
   if(dir == PSoundChannel::Player)
      os << " Player ";
   else if(dir == PSoundChannel::Recorder)
      os << " Recorder ";
   else
      os << " Unknown direction ";
   return os;
}

ostream& operator<<(ostream &os, AudioValueRange range)
{
   os << range.mMinimum << " " << range.mMaximum ;
   return os;

}

ostream& operator<<(ostream &os, PSoundChannelCoreAudio::State &state)
{
   switch(state){
      case PSoundChannelCoreAudio::init_:
         os << "init";
         break;
      case PSoundChannelCoreAudio::open_:
         os << "open";
         break;
      case PSoundChannelCoreAudio::setformat_:
         os << "setformat";
         break;
      case PSoundChannelCoreAudio::setbuffer_:
         os << "setbuffer";
         break;
      case PSoundChannelCoreAudio::running_:
         os << "running";
         break;
      case PSoundChannelCoreAudio::destroy_:
         os << "destroy";
         break;
      default:
      os << " Unknown state ";
   }
   return os;
}

#else

#define checkStatus( err ) \
    if(err) {\
      OSStatus error = static_cast<OSStatus>(err);\
      cout << "CoreAudio Error " << __func__ << " "  \
           <<  error   << "("  << (char*)&err <<  ")" << endl;  \
    }         
#endif


/***** PSound implementation *****/

PSound::PSound(unsigned   channels,
            unsigned   sampleRate,
            unsigned   bitsPerSample,
            PINDEX     bufferSize,
            const BYTE *data)
{
  PAssert(0, PUnimplementedFunction); 
}

PSound::PSound(const PFilePath & filename)
{
  PAssert(0, PUnimplementedFunction); 
}

PSound & PSound::operator=(const PBYTEArray & data)
{
  PAssert(0, PUnimplementedFunction); 
  return *this;
}

BOOL PSound::Load(const PFilePath & filename)
{  
  PAssert(0, PUnimplementedFunction); 
  return false;
}

BOOL PSound::Save(const PFilePath & filename)
{  
   PAssert(0, PUnimplementedFunction); 
   return false;
}

BOOL PSound::Play()
{
   PAssert(0, PUnimplementedFunction); 
   return false;
}

void PSound::SetFormat(unsigned numChannels,
                  unsigned sampleRate,
                  unsigned bitsPerSample)
{
   this->numChannels = numChannels;
   this->sampleRate = sampleRate;
   this->sampleSize = bitsPerSample;
   formatInfo.SetSize(0);
}

BOOL PSound::PlayFile(const PFilePath & file,
           BOOL wait)
{
   PAssert(0, PUnimplementedFunction); 
   return false;
}

void PSound::Beep()
{
   PAssert(0, PUnimplementedFunction); 
}


#include "maccoreaudio/circular_buffer.inl"

/***** PSoundChannel implementation *****/

PSoundChannelCoreAudio::PSoundChannelCoreAudio() 
   : state(init_), mCircularBuffer(NULL), converter_buffer(NULL),
     mInputCircularBuffer(NULL), mInputBufferList(NULL), mOutputBufferList(NULL)
{
   CommonConstruct();

}

PSoundChannelCoreAudio::PSoundChannelCoreAudio(const PString & device,
                Directions dir,
                unsigned numChannels,
                unsigned sampleRate,
                unsigned bitsPerSample)
   : state(init_), mCircularBuffer(NULL), converter_buffer(NULL),
     mInputCircularBuffer(NULL), mInputBufferList(NULL), mOutputBufferList(NULL)
{
   CommonConstruct();
   Open(device, dir, numChannels, sampleRate, bitsPerSample);
}



PSoundChannelCoreAudio::~PSoundChannelCoreAudio()
{
   OSStatus err = noErr;
   State curr(state);
   state = destroy_;
   usleep(1000*20); // about the intervall between two callbacks
                    // ensures that current callback has terminated 

   /* OutputUnit also for input device,
    * Stop everything before deallocating buffers */
   switch(curr) {
      case running_:
         err = AudioOutputUnitStop(mAudioUnit);
         checkStatus(err);
         PTRACE(1, direction << " AudioUnit stopped" );
         usleep(1000*20); // ensure that all callbacks terminated
         /* fall through */
		case mute_:
      case setbuffer_:
         /* check for all buffers unconditionally */
         err = AudioUnitUninitialize(mAudioUnit);
         checkStatus(err);
         /* fall through */
      case setformat_:
         err = AudioConverterDispose(converter);
         checkStatus(err);
         /* fall through */
      case open_:
         err = CloseComponent(mAudioUnit);
         checkStatus(err);
			err = AudioDeviceRemovePropertyListener(mDeviceID,  1, 	
					kAudioPropertyWildcardSection, kAudioDevicePropertyVolumeScalar, 
					VolumeChangePropertyListener);
			checkStatus(err);
         /* fall through */
      case init_:
      case destroy_:
         /* nop */;
   }

   /* now free all buffers */
   if(this->converter_buffer != NULL){
      free(this->converter_buffer);
      this->converter_buffer = NULL;
   }
   if(this->mCircularBuffer != NULL){
      delete this->mCircularBuffer;
      this->mCircularBuffer = NULL;
   }
   if(this->mInputCircularBuffer !=NULL) {
      delete this->mInputCircularBuffer;
      this->mInputCircularBuffer = NULL;
   }
   if(this->mInputBufferList != NULL){
      free(this->mInputBufferList);
      this->mInputBufferList = NULL;
   }
   if(this->mOutputBufferList != NULL){
      free(this->mOutputBufferList);
      this->mOutputBufferList = NULL;
   }

   // tell PChannel::IsOpen() that the channel is closed.
   os_handle = -1;  
}


unsigned PSoundChannelCoreAudio::GetChannels() const
{
   if(state >= setformat_)
      return pwlibASBD.mChannelsPerFrame;
   else 
      return 0;
}

unsigned PSoundChannelCoreAudio::GetSampleRate() const
{
   if(state >= setformat_)
      return (unsigned)(pwlibASBD.mSampleRate);
   else 
      return 0;
}

unsigned PSoundChannelCoreAudio::GetSampleSize() const
{
   if(state >= setformat_)
      return (pwlibASBD.mBitsPerChannel);
   else 
      return 0;
}


/*
 * Functions for retrieving AudioDevice list
 */
#include "maccoreaudio/maccoreaudio_devices.inl"


PString PSoundChannelCoreAudio::GetDefaultDevice(Directions dir)
{
  OSStatus err = noErr;
  UInt32 theSize;
  AudioDeviceID theID;

  theSize = sizeof(AudioDeviceID);

  if (dir == Player) {
    err = AudioHardwareGetProperty(
          kAudioHardwarePropertyDefaultOutputDevice,
               &theSize, &theID);
  }
  else {
    err =  AudioHardwareGetProperty(
           kAudioHardwarePropertyDefaultInputDevice,
                &theSize, &theID);
  }

  if (err == 0) {
     return CADeviceName(theID);
  } else {
     return CA_DUMMY_DEVICE_NAME;
  }
}

PStringList PSoundChannelCoreAudio::GetDeviceNames(Directions dir)
{
  PStringList devices;

  int numDevices;
  AudioDeviceID *deviceList;
  bool isInput = (dir == Recorder);

  numDevices = CADeviceList(&deviceList);

  for (int i = 0; i < numDevices; i++) {
    PString s = CADeviceName(deviceList[i]);
    if (CADeviceSupportDirection(deviceList[i], isInput) > 0) {
      devices.AppendString(s);
    }
  }
  devices.AppendString(CA_DUMMY_DEVICE_NAME);

  if(deviceList != NULL) {
     free(deviceList);
     deviceList = NULL;
  }

  return devices;
}

/*
 * Functions responsible for converting 8kHz to 44k1Hz. It works like this. 
 * The AudioHardware is abstracted by an AudioUnit(AUHAL). Each time the device
 * has more data available or needs new data a callback function is called.
 * These are PlayRenderProc and RecordProc. 
 *
 * The user data is stored in a format a set by ::SetFormat. Usually 8kHz, mono,
 * 16bit unsigned. The AudioUnit usually provides 44,1kHz, stereo, 32bit float.
 * So conversion is needed. The conversion is done by the AUConverter from 
 * the AudioToolbox framework.
 *
 * Currently inside the callback functions from the AUHAL, we pass the request
 * to the Converter, that in turn uses another callback function to grab some
 * of data in the input format. All this is done on-the-fly, which means inside
 * the thread managing the AudioHardware. The callback functions of the 
 * converter are ComplexBufferFillPlayback, ComplexbufferFillRecord.
 *
 * The problem we have that 44,1kHz is not a multiple of 8kHz, so we can never 
 * be sure about how many data the converter is going to ask exactly, 
 * sometimes it might be 102, 106.. This is especially true in case of the 
 * first request, where it might ask some additional data depending on 
 * PrimeMethod that garantuees smoth resampling at the border.
 *
 * To summarize, when the AudioUnit device is ready to handle more data. It 
 * calls its callback function, within these functions the data is processed or 
 * prepared by pulling through AUConverter. The converter in turn calls its 
 * callback function to request more input data. Depending on whether we talk 
 * about Read or Write, this includes more or less complex buffering. 
 */



/*
 *  Callback function called by the converter to request more data for 
 *  playback.
 *  
 *  outDataPacketDesc is unused, because all our packets have the same
 *  format and do not need individual description
 */
OSStatus PSoundChannelCoreAudio::ComplexBufferFillPlayback( 
         AudioConverterRef            inAudioConverter,
         UInt32                   *ioNumberDataPackets,
         AudioBufferList           *ioData,
         AudioStreamPacketDescription **outDataPacketDesc,
         void             *inUserData)
{
   OSStatus err = noErr;
   PSoundChannelCoreAudio *This = 
             static_cast<PSoundChannelCoreAudio*>(inUserData);
   AudioStreamBasicDescription pwlibASBD = This->pwlibASBD;
   CircularBuffer* circBuf = This->mCircularBuffer;

   // output might stop in case there is a complete buffer underrun!!!
   UInt32 minPackets = MIN(*ioNumberDataPackets,
             circBuf->size() / pwlibASBD.mBytesPerPacket );
   UInt32 outBytes = minPackets* pwlibASBD.mBytesPerPacket;

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

   if(outBytes > This->converter_buffer_size){
      PTRACE(1, This->direction << " Converter buffer too small");

      // doesn't matter converter will ask right again for remaining data
      // converter buffer multiple of packet size
      outBytes = This->converter_buffer_size;
   }

   // dequeue data from circular buffer, without locking(false)
   outBytes = circBuf->Drain(This->converter_buffer, outBytes, false);

   UInt32 reqBytes = *ioNumberDataPackets * pwlibASBD.mBytesPerPacket;
   if(outBytes < reqBytes && outBytes < This->converter_buffer_size) {
      reqBytes = MIN(reqBytes, This->converter_buffer_size);
      PTRACE(1, "Buffer underrun, filling up with silence " 
            << (reqBytes - outBytes) << " bytes ");

      bzero(This->converter_buffer + outBytes, reqBytes - outBytes );
      outBytes = reqBytes;
   }

   // fill structure that gets returned to converter
   ioData->mBuffers[0].mData = (char*)This->converter_buffer;
   ioData->mBuffers[0].mDataByteSize = outBytes;

   *ioNumberDataPackets = outBytes / pwlibASBD.mBytesPerPacket;

⌨️ 快捷键说明

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