📄 maccoreaudio.cxx
字号:
bufferSizeBytes += bufferSizeBytes / 10; // +10%
//calculate size of ABL given the last field, assum non-interleaved
UInt32 mChannelsPerFrame = hwASBD.mChannelsPerFrame;
UInt32 propsize = (UInt32) &(((AudioBufferList *)0)->mBuffers[mChannelsPerFrame]);
//malloc buffer lists
mInputBufferList = (AudioBufferList *)malloc(propsize);
mInputBufferList->mNumberBuffers = hwASBD.mChannelsPerFrame;
//pre-malloc buffers for AudioBufferLists
for(UInt32 i =0; i< mInputBufferList->mNumberBuffers ; i++) {
mInputBufferList->mBuffers[i].mNumberChannels = 1;
mInputBufferList->mBuffers[i].mDataByteSize = bufferSizeBytes;
mInputBufferList->mBuffers[i].mData = malloc(bufferSizeBytes);
}
mRecordInputBufferSize = bufferSizeBytes;
/** allocate ringbuffer to cache data before passing them to the converter */
// take only one buffer -> mono, use double buffering
mInputCircularBuffer = new CircularBuffer(bufferSizeBytes * 2);
/**
* Build buffer list that is passed to the Converter to be filled with
* the converted frames.
*/
// given the number of input bytes how many bytes to expect at the output?
bufferSizeBytes += MIN_INPUT_FILL * hwASBD.mBytesPerFrame;
propertySize = sizeof(UInt32);
err = AudioConverterGetProperty(converter,
kAudioConverterPropertyCalculateOutputBufferSize,
&propertySize,
&bufferSizeBytes);
checkStatus(err);
//calculate number of buffers from channels
mChannelsPerFrame = pwlibASBD.mChannelsPerFrame;
propsize = (UInt32) &(((AudioBufferList *)0)->mBuffers[mChannelsPerFrame]);
//malloc buffer lists
mOutputBufferList = (AudioBufferList *)malloc(propsize);
mOutputBufferList->mNumberBuffers = pwlibASBD.mChannelsPerFrame;
//pre-malloc buffers for AudioBufferLists
for(UInt32 i =0; i< mOutputBufferList->mNumberBuffers ; i++) {
mOutputBufferList->mBuffers[i].mNumberChannels = 1;
mOutputBufferList->mBuffers[i].mDataByteSize = bufferSizeBytes;
mOutputBufferList->mBuffers[i].mData = malloc(bufferSizeBytes);
}
mRecordOutputBufferSize = bufferSizeBytes;
return err;
}
BOOL PSoundChannelCoreAudio::GetBuffers(PINDEX & size,
PINDEX & count)
{
size = bufferSizeBytes;
count = bufferCount;
return TRUE;
}
OSStatus PSoundChannelCoreAudio::VolumeChangePropertyListener(AudioDeviceID id,
UInt32 chan, Boolean isInput, AudioDevicePropertyID propID,
void *user_data)
{
PSoundChannelCoreAudio *This =
static_cast<PSoundChannelCoreAudio*>(user_data);
OSStatus err = noErr;
UInt32 theSize = sizeof(Float32);
Float32 volume;
/*
* Function similar to GetVolume, but we are free to ask the volume
* for the intput/output direction
*/
// not all devices have a master channel
err = AudioDeviceGetProperty(This->mDeviceID, 0, isInput,
kAudioDevicePropertyVolumeScalar,
&theSize, &volume);
if(err != kAudioHardwareNoError) {
// take the value of first channel to be the volume
theSize = sizeof(volume);
err = AudioDeviceGetProperty(This->mDeviceID, 1, isInput,
kAudioDevicePropertyVolumeScalar,
&theSize, &volume);
}
std::cout << (isInput?"Recorder":"Player")
<< " volume updated " << unsigned(100*volume) << std::endl;
return noErr;
}
#include "maccoreaudio/mute_hack.inl"
/**
* Also check out this to see the difference between streams and channels.
* http://lists.apple.com/archives/coreaudio-api/2001/Nov/msg00155.html
* In short a stream contains several channels, e. g. 2 in case of stereo.
*/
BOOL PSoundChannelCoreAudio::GetVolume(unsigned & volume)
{
OSStatus err = noErr;
UInt32 theSize;
Float32 theValue;
bool isInput = (direction == Player ? false : true);
if(mDeviceID == kAudioDeviceDummy){
//in the case of a dummy device, we simply return 0 in all cases
PTRACE(1, "Dummy device");
volume = 0;
return TRUE;
}
theSize = sizeof(theValue);
// not all devices have a master channel
err = AudioDeviceGetProperty(mDeviceID, 0, isInput,
kAudioDevicePropertyVolumeScalar,
&theSize, &theValue);
if(err != kAudioHardwareNoError) {
// take the value of first channel to be the volume
theSize = sizeof(theValue);
err = AudioDeviceGetProperty(mDeviceID, 1, isInput,
kAudioDevicePropertyVolumeScalar,
&theSize, &theValue);
}
if (err == kAudioHardwareNoError) {
// volume is between 0 and 100?
volume = (unsigned) (theValue * 100);
return TRUE;
} else {
volume = 0;
return FALSE;
}
}
/**
* We assume that we are taking always the first stream and that it is stereo,
* which means it maps to the first two channels of the device.
* The Master Channel (0) is unusable because not all devices support it
*/
BOOL PSoundChannelCoreAudio::SetVolume(unsigned volume)
{
OSStatus err = noErr;
Boolean isWritable;
bool isInput = (direction == Player ? false : true);
bool useMaster = false;
if(mDeviceID == kAudioDeviceDummy) {
PTRACE(1, "Dummy device");
return FALSE;
}
if(state < setformat_){ // we need to know the stream format
PTRACE(2, __func__ << "AudioStreamBasicDescription not initialized yet");
return FALSE;
}
// not all devices have a master channel
err = AudioDeviceGetPropertyInfo(mDeviceID, 0, isInput,
kAudioDevicePropertyVolumeScalar,
NULL, &isWritable);
if(err != kAudioHardwareNoError)
{
// check if we can access the individual channels
err = AudioDeviceGetPropertyInfo(mDeviceID, 1, isInput,
kAudioDevicePropertyVolumeScalar,
NULL, &isWritable);
useMaster = false;
}
checkStatus(err);
if ((err == kAudioHardwareNoError) && isWritable)
{
// is the volume between 0 and 100 ?
float theValue = ((float)volume)/100.0;
if(useMaster){
err = AudioDeviceSetProperty(mDeviceID, NULL, 0, isInput,
kAudioDevicePropertyVolumeScalar,
sizeof(float), &theValue);
checkStatus(err);
} else {
// iterate over all channels
for(unsigned ch = 1; ch <= hwASBD.mChannelsPerFrame; ch++)
{
err = AudioDeviceSetProperty(mDeviceID, NULL, ch, isInput,
kAudioDevicePropertyVolumeScalar,
sizeof(float), &theValue);
checkStatus(err);
}
}
/** Not all devices can be muted, so it's no solution and wo drop the
* code alltogether
{
UInt32 mute = (volume == 0)? 1 * mute * : 0 * unmute * ;
err = AudioDeviceSetProperty(mDeviceID, NULL, useMaster?0:1,
isInput, kAudioDevicePropertyMute, sizeof(UInt32), &mute);
checkStatus(err);
err = AudioDeviceSetProperty(mDeviceID, NULL, useMaster?0:1,
isInput, kAudioDevicePropertySubMute, sizeof(UInt32), &mute);
checkStatus(err);
}
*/
}
// if necessary we mute device on the pwlib level by discarding frames.
// this works even when the device does not support muting,
// but we have to make sure that 'this' is either a singleton instance
// or that all threads/instances get the message to be quiet
{
pthread_mutex_lock(&GetIsMuteMutex());
isMute() = (volume == 0) ? TRUE : FALSE;
pthread_mutex_unlock(&GetIsMuteMutex());
/* No sense to mute this channel, it might opened just as a mixer.
* The playing/recording channel might be another instance of this
* class !!! */
}
if (!err)
return TRUE;
else
return FALSE;
}
BOOL PSoundChannelCoreAudio::Write(const void *buf,PINDEX len)
{
PTRACE(5, "Write called with len " << len);
if(state < setbuffer_){
PTRACE(1, __func__ << " Please initialize device first");
return FALSE;
}
pthread_mutex_lock(&GetIsMuteMutex());
if(isMute() && state != mute_){
PTRACE(3, __func__ << "muting the " << direction << " device");
state = mute_;
OSStatus err = AudioOutputUnitStop(mAudioUnit);
checkStatus(err);
/* isMute() => state==mute */
}
if (mDeviceID == kAudioDeviceDummy || isMute() && state == mute_ ) {
lastWriteCount = len;
// safe to assume non-interleaved or mono
UInt32 nr_samples = len / pwlibASBD.mBytesPerFrame;
usleep(UInt32(nr_samples/pwlibASBD.mSampleRate * 1000000)); // 10E-6 [s]
pthread_mutex_unlock(&GetIsMuteMutex());
return TRUE;
}
// Start the device before putting datA into the buffer
// Otherwise the thread could be locked in case the buffer is full
// and the device is not running and draining the buffer
if(state == setbuffer_ || (state == mute_ && !isMute())){
state = running_;
PTRACE(2, "Starting " << direction << " device.");
OSStatus err = AudioOutputUnitStart(mAudioUnit);
checkStatus(err);
}
pthread_mutex_unlock(&GetIsMuteMutex());
// Write to circular buffer with locking
lastWriteCount = mCircularBuffer->Fill((const char*)buf, len, true);
return (TRUE);
}
BOOL PSoundChannelCoreAudio::PlaySound(const PSound & sound,
BOOL wait)
{
if (!Write((const BYTE *)sound, sound.GetSize()))
return FALSE;
if (wait)
return WaitForPlayCompletion();
return TRUE;
}
BOOL PSoundChannelCoreAudio::PlayFile(const PFilePath & file,
BOOL wait)
{
PTRACE(1, __func__ );
PAssert(0, PUnimplementedFunction);
return TRUE;
}
BOOL PSoundChannelCoreAudio::HasPlayCompleted()
{
PTRACE(1, __func__ );
PAssert(0, PUnimplementedFunction);
return false;
}
BOOL PSoundChannelCoreAudio::WaitForPlayCompletion()
{
PTRACE(1, __func__ );
PAssert(0, PUnimplementedFunction);
return false;
}
BOOL PSoundChannelCoreAudio::Read(void *buf,
PINDEX len)
{
PTRACE(5, "Read called with len " << len);
if(state < setbuffer_){
PTRACE(1, __func__ << " Please initialize device first");
return FALSE;
}
pthread_mutex_lock(&GetIsMuteMutex());
if(isMute() && state != mute_){
PTRACE(2, __func__ << "muting the " << direction << " device");
state = mute_;
OSStatus err = AudioOutputUnitStop(mAudioUnit);
checkStatus(err);
/* isMute() => state==mute */
}
if (mDeviceID == kAudioDeviceDummy || isMute()) {
lastReadCount = len;
bzero(buf, len);
// we are working with non-interleaved or mono
UInt32 nr_samples = len / pwlibASBD.mBytesPerFrame;
usleep(UInt32(nr_samples/pwlibASBD.mSampleRate * 1000000)); // 10E-6 [s]
pthread_mutex_unlock(&GetIsMuteMutex());
return TRUE;
}
// Start the device before draining data or the thread might be locked
// on an empty buffer and never wake up, because no device is filling
// with data
if(state == setbuffer_ || (state == mute_ && !isMute())){
state = running_;
PTRACE(2, "Starting " << direction << " device.");
OSStatus err = AudioOutputUnitStart(mAudioUnit);
checkStatus(err);
}
pthread_mutex_unlock(&GetIsMuteMutex());
lastReadCount = mCircularBuffer->Drain((char*)buf, len, true);
return (TRUE);
}
BOOL PSoundChannelCoreAudio::RecordSound(PSound & sound)
{
PTRACE(1, __func__ );
PAssert(0, PUnimplementedFunction);
return false;
}
BOOL PSoundChannelCoreAudio::RecordFile(const PFilePath & file)
{
PTRACE(1, __func__ );
PAssert(0, PUnimplementedFunction);
return false;
}
BOOL PSoundChannelCoreAudio::StartRecording()
{
if(state != setbuffer_){
PTRACE(1, __func__ << " Initialize the device first");
return FALSE;
}
pthread_mutex_lock(&GetIsMuteMutex());
if(state == setbuffer_ || (state == mute_ && !isMute()) ){
state = running_;
PTRACE(2,__func__ << "Starting " << direction << " device.");
OSStatus err = AudioOutputUnitStart(mAudioUnit);
checkStatus(err);
}
pthread_mutex_unlock(&GetIsMuteMutex());
return false;
}
BOOL PSoundChannelCoreAudio::isRecordBufferFull()
{
PAssert(direction == Recorder, PInvalidParameter);
if(state != setbuffer_){
PTRACE(1, __func__ << " Initialize the device first");
return FALSE;
}
return (mCircularBuffer->size() > bufferSizeBytes);
}
BOOL PSoundChannelCoreAudio::AreAllRecordBuffersFull()
{
PAssert(direction == Recorder, PInvalidParameter);
if(state != setbuffer_){
PTRACE(1, __func__ << " Initialize the device first");
return FALSE;
}
return (mCircularBuffer->Full());
}
BOOL PSoundChannelCoreAudio::WaitForRecordBufferFull()
{
PTRACE(1, __func__ );
PAssert(0, PUnimplementedFunction);
if (os_handle < 0) {
return FALSE;
}
return PXSetIOBlock(PXReadBlock, readTimeout);
}
BOOL PSoundChannelCoreAudio::WaitForAllRecordBuffersFull()
{
PTRACE(1, __func__ );
PAssert(0, PUnimplementedFunction);
return false;
}
// End of file
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -