📄 pa_mac_core.c
字号:
UInt32 size = pahsc->input.bytesPerUserNativeBuffer; err = AudioConverterFillBuffer( pahsc->input.converter, PaOSX_InputConverterCallbackProc, past, &size, pahsc->input.converterBuffer); if( err != noErr ) return err; inputBuffer = pahsc->input.converterBuffer; } /* Fill part of audio converter buffer by converting input to user format, * calling user callback, then converting output to native format. */ if( PaConvert_Process( past, inputBuffer, outputBuffer )) { past->past_StopSoon = 1; } } return err;}/******************************************************************************* This is the proc that supplies the data to the AudioConverterFillBuffer call*/static OSStatus PaOSX_OutputConverterCallbackProc (AudioConverterRef inAudioConverter, UInt32* outDataSize, void** outData, void* inUserData){ internalPortAudioStream *past = (internalPortAudioStream *) inUserData; PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; *outData = pahsc->output.converterBuffer; *outDataSize = pahsc->output.bytesPerUserNativeBuffer; return PaOSX_LoadAndProcess ( past, pahsc->input.converterBuffer, pahsc->output.converterBuffer );}/************************************************************************ Fill any available output buffers and use any available** input buffers by calling user callback.** Will set past->past_StopSoon if user callback indicates that it is finished.*/static OSStatus PaOSX_HandleInputOutput( internalPortAudioStream *past, const AudioBufferList* inInputData, AudioBufferList* outOutputData ){ OSStatus err = noErr; char *inputNativeBufferfPtr = NULL; char *outputNativeBufferfPtr = NULL; PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; /* If we are using output, then we need an empty output buffer. */ if( outOutputData->mNumberBuffers > 0 ) { outputNativeBufferfPtr = (char*)outOutputData->mBuffers[0].mData; } if( inInputData->mNumberBuffers > 0 ) { inputNativeBufferfPtr = (char*)inInputData->mBuffers[0].mData; /* Write to FIFO here if we are only using this callback. */ if( (pahsc->mode == PA_MODE_INPUT_ONLY) || (pahsc->mode == PA_MODE_IO_ONE_DEVICE) ) { long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); long numBytes = inInputData->mBuffers[0].mDataByteSize; if( numBytes <= writeRoom ) { RingBuffer_Write( &pahsc->ringBuffer, inputNativeBufferfPtr, numBytes ); DBUGBACK(("PaOSX_HandleInputOutput: wrote %ld bytes to FIFO.\n", inInputData->mBuffers[0].mDataByteSize)); } // FIXME else drop samples on floor, remember overflow??? } } if( pahsc->mode == PA_MODE_INPUT_ONLY ) { /* Generate user buffers as long as we have a half full input FIFO. */ long halfSize = pahsc->ringBuffer.bufferSize / 2; while( (RingBuffer_GetReadAvailable( &pahsc->ringBuffer ) >= halfSize) && (past->past_StopSoon == 0) ) { err = PaOSX_LoadAndProcess ( past, NULL, NULL ); if( err != noErr ) goto error; } } else { UInt32 size = outOutputData->mBuffers[0].mDataByteSize; err = AudioConverterFillBuffer( pahsc->output.converter, PaOSX_OutputConverterCallbackProc, past, &size, outputNativeBufferfPtr); if( err != noErr ) { PRINT_ERR("PaOSX_HandleInputOutput: AudioConverterFillBuffer failed", err); goto error; } } error: return err;}/****************************************************************** * This callback is used when two separate devices are used for input and output. * This often happens when using USB devices which present as two devices: input and output. * It just writes its data to a FIFO so that it can be read by the main callback * proc PaOSX_CoreAudioIOCallback(). */static OSStatus PaOSX_CoreAudioInputCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* contextPtr){ internalPortAudioStream *past = (internalPortAudioStream *) contextPtr; PaHostSoundControl *pahsc; pahsc = (PaHostSoundControl *) past->past_DeviceData; /* If there is a FIFO for input then write to it. */ if( pahsc->ringBufferData != NULL ) { long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); long numBytes = inInputData->mBuffers[0].mDataByteSize; if( numBytes <= writeRoom ) { RingBuffer_Write( &pahsc->ringBuffer, inInputData->mBuffers[0].mData, inInputData->mBuffers[0].mDataByteSize ); } else { DBUGBACK(("PaOSX_CoreAudioInputCallback: FIFO too full to write!\n")); } } return noErr;}/****************************************************************** * This is the primary callback for CoreAudio. * It can handle input and/or output for a single device. * It takes input from CoreAudio, converts it and passes it to the * PortAudio user callback. Then takes the PA results and passes it * back to CoreAudio. */static OSStatus PaOSX_CoreAudioIOCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* contextPtr){ OSStatus err = noErr; internalPortAudioStream *past; PaHostSoundControl *pahsc; past = (internalPortAudioStream *) contextPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; /* Has someone asked us to abort by calling Pa_AbortStream()? */ if( past->past_StopNow ) { past->past_IsActive = 0; /* Will cause thread to return. */ } /* Has someone asked us to stop by calling Pa_StopStream() * OR has a user callback returned '1' to indicate finished. */ else if( past->past_StopSoon ) { // FIXME - Pretend all done. Should wait for audio to play out but CoreAudio latency very low. past->past_IsActive = 0; /* Will cause thread to return. */ } else { /* use time stamp from CoreAudio if valid */ /* dmazzoni: this is unreliable! if( inOutputTime->mFlags & kAudioTimeStampSampleTimeValid) { past->past_FrameCount = inOutputTime->mSampleTime; } else if( inInputTime->mFlags & kAudioTimeStampSampleTimeValid) { past->past_FrameCount = inInputTime->mSampleTime; } */ /* Measure CPU load. */ Pa_StartUsageCalculation( past ); past->past_NumCallbacks += 1; /* Process full input buffer and fill up empty output buffers. */ err = PaOSX_HandleInputOutput( past, inInputData, outOutputData ); Pa_EndUsageCalculation( past ); } if( err != 0 ) DBUG(("PaOSX_CoreAudioIOCallback: returns %ld.\n", err )); return err;}/*******************************************************************//** Attempt to set device sample rate. * This is not critical because we use an AudioConverter but we may * get better fidelity if we can avoid resampling. * * Only set format once because some devices take time to settle. * Return flag indicating whether format changed so we know whether to wait * for DevicePropertyListener to get called. * * @return negative error, zero if no change, or one if changed successfully. */static PaError PaOSX_SetFormat( AudioDeviceID devID, Boolean isInput, double desiredRate, int desiredNumChannels ){ AudioStreamBasicDescription origDesc; AudioStreamBasicDescription formatDesc; AudioStreamID *streams; PaError result = 0; OSStatus err; Boolean outWritable; UInt32 outSize; UInt32 dataSize; UInt32 supportsMixing; Float64 originalRate; int originalChannels; int numStreams; pid_t hog_pid; pid_t this_pid = getpid(); int did_set_hog_mode = 0; int i; /* Get current device format. This is critical because if we pass * zeros for unspecified fields then the iMic device gets switched to a 16 bit * integer format!!! I don't know if this is a Mac bug or not. But it only * started happening when I upgraded from OS X V10.1 to V10.2 (Jaguar). */ dataSize = sizeof(formatDesc); err = AudioDeviceGetProperty( devID, 0, isInput, kAudioDevicePropertyStreamFormat, &dataSize, &formatDesc); if( err != noErr ) { PRINT_ERR("PaOSX_SetFormat: Could not get format.", err); sSavedHostError = err; return paHostError; } origDesc = formatDesc; originalRate = origDesc.mSampleRate; originalChannels = origDesc.mChannelsPerFrame; // Is it already set to the correct format? if( (originalRate != desiredRate) || (originalChannels != desiredNumChannels) || (origDesc.mFormatID != kAudioFormatLinearPCM) || (origDesc.mFormatFlags & kLinearPCMFormatFlagIsFloat)==0 ) { if (originalRate != desiredRate) DBUG(("PaOSX_SetFormat: try to change sample rate to %f.\n", desiredRate )); if (originalChannels != desiredNumChannels) DBUG(("PaOSX_SetFormat: try to set number of channels to %d\n", desiredNumChannels)); if (formatDesc.mFormatID != kAudioFormatLinearPCM) DBUG(("PaOSX_SetFormat: try to set formatID to linear PCM\n")); if ((formatDesc.mFormatFlags & kLinearPCMFormatFlagIsFloat)==0) DBUG(("PaOSX_SetFormat: try to set formatFlags to float32\n")); // First we try to grab hog mode on the device, because otherwise it won't let // us change the format. -dmazzoni DBUG(("Checking hog mode\n")); outSize = sizeof(hog_pid); err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyHogMode, &outSize, &hog_pid); if (!err) { DBUG(("Current status of hog mode: pid=%d this_pid=%d\n", (int)hog_pid, (int)this_pid)); if (hog_pid != this_pid) { hog_pid = this_pid; err = AudioDeviceSetProperty( devID, 0, 0, isInput, kAudioDevicePropertyHogMode, sizeof(hog_pid), &hog_pid); if (!err) { DBUG(("Successfully set hog mode - new pid=%d!\n", (int)hog_pid)); err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyHogMode, &outSize, &hog_pid); if (!err) { DBUG(("Checking new status of hog mode: pid=%d this_pid=%d\n", (int)hog_pid, (int)this_pid)); did_set_hog_mode = 1; } } } } // Get a list of the device's streams err = AudioDeviceGetPropertyInfo( devID, 0, isInput, kAudioDevicePropertyStreams, &outSize, &outWritable ); streams = (AudioStreamID *)malloc(outSize); err = AudioDeviceGetProperty( devID, 0, isInput, kAudioDevicePropertyStreams, &outSize, streams); // If that was successful, loop through each stream and set its format to // linear PCM float. if (err) { DBUG(("Couldn't get device's streams! err=%d\n", err)); } else { numStreams = outSize / sizeof(AudioStreamID); for(i=0; i<numStreams; i++) { memset(&formatDesc, 0, sizeof(AudioStreamBasicDescription)); formatDesc.mFormatID = kAudioFormatLinearPCM; outSize = sizeof(AudioStreamBasicDescription); err = AudioDeviceGetProperty(devID, 0, isInput, kAudioStreamPropertyPhysicalFormatMatch, &outSize, &formatDesc); DBUG(("Asked for stream physical format match, got " "err=%d, rate=%1f, formatID=%s, flags=%d channels=%d\n", err, formatDesc.mSampleRate, FourCharCode2Str(formatDesc.mFormatID), formatDesc.mFormatFlags, formatDesc.mChannelsPerFrame)); if (formatDesc.mFormatID != kAudioFormatLinearPCM) { DBUG(("Couldn't even get linear PCM!\n")); } else { err = AudioDeviceSetProperty( devID, 0, 0, isInput, kAudioStreamPropertyPhysicalFormat, sizeof(formatDesc), &formatDesc);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -