Allow audio devices with up to 64 channels on Mac.
Does not increase memory requirements. Adds an additional check to ensure configurations requiring more memory per IO block than the input ring buffer contains are rejected. BUG=1904 TESTED=Using Soundflower (64 channels) at 48 kHz as input gives good quality. Selecting a higher sample rate (96 kHz), which would otherwise give choppy audio, instead results in an error. R=henrika@webrtc.org, xians@webrtc.org Review URL: https://webrtc-codereview.appspot.com/1628004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4198 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
1064cf06b0
commit
6f69eb78dd
@ -373,10 +373,10 @@ int32_t AudioDeviceMac::Init()
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting RunLoop to NULL here instructs HAL to manage its own thread for
|
// Setting RunLoop to NULL here instructs HAL to manage its own thread for
|
||||||
// notifications. This was the default behaviour on OS X 10.5 and earlier, but now
|
// notifications. This was the default behaviour on OS X 10.5 and earlier,
|
||||||
// must be explicitly specified. HAL would otherwise try to use the main thread to
|
// but now must be explicitly specified. HAL would otherwise try to use the
|
||||||
// issue notifications.
|
// main thread to issue notifications.
|
||||||
AudioObjectPropertyAddress propertyAddress = {
|
AudioObjectPropertyAddress propertyAddress = {
|
||||||
kAudioHardwarePropertyRunLoop,
|
kAudioHardwarePropertyRunLoop,
|
||||||
kAudioObjectPropertyScopeGlobal,
|
kAudioObjectPropertyScopeGlobal,
|
||||||
@ -495,8 +495,8 @@ int32_t AudioDeviceMac::SpeakerIsAvailable(bool& available)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given that InitSpeaker was successful, we know that a valid speaker exists
|
// Given that InitSpeaker was successful, we know that a valid speaker
|
||||||
//
|
// exists.
|
||||||
available = true;
|
available = true;
|
||||||
|
|
||||||
// Close the initialized output mixer
|
// Close the initialized output mixer
|
||||||
@ -554,8 +554,8 @@ int32_t AudioDeviceMac::MicrophoneIsAvailable(bool& available)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given that InitMicrophone was successful, we know that a valid microphone exists
|
// Given that InitMicrophone was successful, we know that a valid microphone
|
||||||
//
|
// exists.
|
||||||
available = true;
|
available = true;
|
||||||
|
|
||||||
// Close the initialized input mixer
|
// Close the initialized input mixer
|
||||||
@ -1363,13 +1363,13 @@ int32_t AudioDeviceMac::InitPlayout()
|
|||||||
_id, "MacBook Pro not using internal speakers");
|
_id, "MacBook Pro not using internal speakers");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a listener to determine if the status changes.
|
// Add a listener to determine if the status changes.
|
||||||
WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(_outputDeviceID,
|
WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(_outputDeviceID,
|
||||||
&propertyAddress, &objectListenerProc, this));
|
&propertyAddress, &objectListenerProc, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current stream description
|
// Get current stream description
|
||||||
propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
|
propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
|
||||||
memset(&_outStreamFormat, 0, sizeof(_outStreamFormat));
|
memset(&_outStreamFormat, 0, sizeof(_outStreamFormat));
|
||||||
size = sizeof(_outStreamFormat);
|
size = sizeof(_outStreamFormat);
|
||||||
@ -1387,8 +1387,8 @@ int32_t AudioDeviceMac::InitPlayout()
|
|||||||
if (_outStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS)
|
if (_outStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS)
|
||||||
{
|
{
|
||||||
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
||||||
"Too many channels on device -> mChannelsPerFrame = %d",
|
"Too many channels on output device (mChannelsPerFrame = %d)",
|
||||||
_outStreamFormat.mChannelsPerFrame);
|
_outStreamFormat.mChannelsPerFrame);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1415,13 +1415,12 @@ int32_t AudioDeviceMac::InitPlayout()
|
|||||||
_outStreamFormat.mBytesPerFrame,
|
_outStreamFormat.mBytesPerFrame,
|
||||||
_outStreamFormat.mBitsPerChannel);
|
_outStreamFormat.mBitsPerChannel);
|
||||||
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
||||||
"mFormatFlags = %u, mChannelsPerFrame = %u",
|
"mFormatFlags = %u",
|
||||||
_outStreamFormat.mFormatFlags,
|
_outStreamFormat.mFormatFlags);
|
||||||
_outStreamFormat.mChannelsPerFrame);
|
|
||||||
logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
||||||
(const char *) &_outStreamFormat.mFormatID);
|
(const char *) &_outStreamFormat.mFormatID);
|
||||||
|
|
||||||
// Our preferred format to work with
|
// Our preferred format to work with
|
||||||
_outDesiredFormat.mSampleRate = N_PLAY_SAMPLES_PER_SEC;
|
_outDesiredFormat.mSampleRate = N_PLAY_SAMPLES_PER_SEC;
|
||||||
if (_outStreamFormat.mChannelsPerFrame >= 2 && (_playChannels == 2))
|
if (_outStreamFormat.mChannelsPerFrame >= 2 && (_playChannels == 2))
|
||||||
{
|
{
|
||||||
@ -1447,7 +1446,7 @@ int32_t AudioDeviceMac::InitPlayout()
|
|||||||
|
|
||||||
_outDesiredFormat.mBytesPerPacket = _outDesiredFormat.mChannelsPerFrame
|
_outDesiredFormat.mBytesPerPacket = _outDesiredFormat.mChannelsPerFrame
|
||||||
* sizeof(SInt16);
|
* sizeof(SInt16);
|
||||||
_outDesiredFormat.mFramesPerPacket = 1; // In uncompressed audio,
|
_outDesiredFormat.mFramesPerPacket = 1; // In uncompressed audio,
|
||||||
// a packet is one frame.
|
// a packet is one frame.
|
||||||
_outDesiredFormat.mBytesPerFrame = _outDesiredFormat.mChannelsPerFrame
|
_outDesiredFormat.mBytesPerFrame = _outDesiredFormat.mChannelsPerFrame
|
||||||
* sizeof(SInt16);
|
* sizeof(SInt16);
|
||||||
@ -1595,7 +1594,7 @@ int32_t AudioDeviceMac::InitRecording()
|
|||||||
_captureDeviceIsAlive = 1;
|
_captureDeviceIsAlive = 1;
|
||||||
_doStopRec = false;
|
_doStopRec = false;
|
||||||
|
|
||||||
// Get current stream description
|
// Get current stream description
|
||||||
AudioObjectPropertyAddress
|
AudioObjectPropertyAddress
|
||||||
propertyAddress = { kAudioDevicePropertyStreamFormat,
|
propertyAddress = { kAudioDevicePropertyStreamFormat,
|
||||||
kAudioDevicePropertyScopeInput, 0 };
|
kAudioDevicePropertyScopeInput, 0 };
|
||||||
@ -1615,8 +1614,18 @@ int32_t AudioDeviceMac::InitRecording()
|
|||||||
if (_inStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS)
|
if (_inStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS)
|
||||||
{
|
{
|
||||||
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
||||||
", Too many channels on device (mChannelsPerFrame = %d)",
|
"Too many channels on input device (mChannelsPerFrame = %d)",
|
||||||
_inStreamFormat.mChannelsPerFrame);
|
_inStreamFormat.mChannelsPerFrame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int io_block_size_samples = _inStreamFormat.mChannelsPerFrame *
|
||||||
|
_inStreamFormat.mSampleRate / 100 * N_BLOCKS_IO;
|
||||||
|
if (io_block_size_samples > _captureBufSizeSamples)
|
||||||
|
{
|
||||||
|
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
||||||
|
"Input IO block size (%d) is larger than ring buffer (%u)",
|
||||||
|
io_block_size_samples, _captureBufSizeSamples);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1634,9 +1643,8 @@ int32_t AudioDeviceMac::InitRecording()
|
|||||||
_inStreamFormat.mBytesPerFrame,
|
_inStreamFormat.mBytesPerFrame,
|
||||||
_inStreamFormat.mBitsPerChannel);
|
_inStreamFormat.mBitsPerChannel);
|
||||||
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
||||||
" mFormatFlags = %u, mChannelsPerFrame = %u",
|
" mFormatFlags = %u",
|
||||||
_inStreamFormat.mFormatFlags,
|
_inStreamFormat.mFormatFlags);
|
||||||
_inStreamFormat.mChannelsPerFrame);
|
|
||||||
logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
||||||
(const char *) &_inStreamFormat.mFormatID);
|
(const char *) &_inStreamFormat.mFormatID);
|
||||||
|
|
||||||
@ -1931,7 +1939,7 @@ bool AudioDeviceMac::PlayoutIsInitialized() const
|
|||||||
|
|
||||||
int32_t AudioDeviceMac::StartPlayout()
|
int32_t AudioDeviceMac::StartPlayout()
|
||||||
{
|
{
|
||||||
|
|
||||||
CriticalSectionScoped lock(&_critSect);
|
CriticalSectionScoped lock(&_critSect);
|
||||||
|
|
||||||
if (!_playIsInitialized)
|
if (!_playIsInitialized)
|
||||||
@ -2283,7 +2291,7 @@ AudioDeviceMac::GetNumberDevices(const AudioObjectPropertyScope scope,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Happy ending
|
// Happy ending
|
||||||
if (deviceIds)
|
if (deviceIds)
|
||||||
{
|
{
|
||||||
free(deviceIds);
|
free(deviceIds);
|
||||||
@ -2435,7 +2443,7 @@ int32_t AudioDeviceMac::InitDevice(const uint16_t userDeviceIndex,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Obtain device name and manufacturer for logging.
|
// Obtain device name and manufacturer for logging.
|
||||||
// Also use this as a test to ensure a user-set device ID is valid.
|
// Also use this as a test to ensure a user-set device ID is valid.
|
||||||
char devName[128];
|
char devName[128];
|
||||||
char devManf[128];
|
char devManf[128];
|
||||||
memset(devName, 0, sizeof(devName));
|
memset(devName, 0, sizeof(devName));
|
||||||
@ -2488,7 +2496,7 @@ OSStatus AudioDeviceMac::implObjectListenerProc(
|
|||||||
{
|
{
|
||||||
WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
||||||
"AudioDeviceMac::implObjectListenerProc()");
|
"AudioDeviceMac::implObjectListenerProc()");
|
||||||
|
|
||||||
for (UInt32 i = 0; i < numberAddresses; i++)
|
for (UInt32 i = 0; i < numberAddresses; i++)
|
||||||
{
|
{
|
||||||
if (addresses[i].mSelector == kAudioHardwarePropertyDevices)
|
if (addresses[i].mSelector == kAudioHardwarePropertyDevices)
|
||||||
@ -2545,7 +2553,7 @@ int32_t AudioDeviceMac::HandleDeviceChange()
|
|||||||
logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
||||||
"Error in AudioDeviceGetProperty()", (const char*) &err);
|
"Error in AudioDeviceGetProperty()", (const char*) &err);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpeakerIsInitialized())
|
if (SpeakerIsInitialized())
|
||||||
@ -2629,13 +2637,24 @@ int32_t AudioDeviceMac::HandleStreamFormatChange(
|
|||||||
"mBytesPerFrame = %u, mBitsPerChannel = %u",
|
"mBytesPerFrame = %u, mBitsPerChannel = %u",
|
||||||
streamFormat.mBytesPerFrame, streamFormat.mBitsPerChannel);
|
streamFormat.mBytesPerFrame, streamFormat.mBitsPerChannel);
|
||||||
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
||||||
"mFormatFlags = %u, mChannelsPerFrame = %u",
|
"mFormatFlags = %u",
|
||||||
streamFormat.mFormatFlags, streamFormat.mChannelsPerFrame);
|
streamFormat.mFormatFlags);
|
||||||
logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
||||||
(const char *) &streamFormat.mFormatID);
|
(const char *) &streamFormat.mFormatID);
|
||||||
|
|
||||||
if (propertyAddress.mScope == kAudioDevicePropertyScopeInput)
|
if (propertyAddress.mScope == kAudioDevicePropertyScopeInput)
|
||||||
{
|
{
|
||||||
|
const int io_block_size_samples = streamFormat.mChannelsPerFrame *
|
||||||
|
streamFormat.mSampleRate / 100 * N_BLOCKS_IO;
|
||||||
|
if (io_block_size_samples > _captureBufSizeSamples)
|
||||||
|
{
|
||||||
|
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
||||||
|
"Input IO block size (%d) is larger than ring buffer (%u)",
|
||||||
|
io_block_size_samples, _captureBufSizeSamples);
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(&_inStreamFormat, &streamFormat, sizeof(streamFormat));
|
memcpy(&_inStreamFormat, &streamFormat, sizeof(streamFormat));
|
||||||
|
|
||||||
if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2))
|
if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2))
|
||||||
|
@ -26,35 +26,26 @@ namespace webrtc
|
|||||||
class EventWrapper;
|
class EventWrapper;
|
||||||
class ThreadWrapper;
|
class ThreadWrapper;
|
||||||
|
|
||||||
const uint32_t N_REC_SAMPLES_PER_SEC = 48000;
|
const int N_REC_SAMPLES_PER_SEC = 48000;
|
||||||
const uint32_t N_PLAY_SAMPLES_PER_SEC = 48000;
|
const int N_PLAY_SAMPLES_PER_SEC = 48000;
|
||||||
|
|
||||||
const uint32_t N_REC_CHANNELS = 1; // default is mono recording
|
const int N_REC_CHANNELS = 1; // default is mono recording
|
||||||
const uint32_t N_PLAY_CHANNELS = 2; // default is stereo playout
|
const int N_PLAY_CHANNELS = 2; // default is stereo playout
|
||||||
const uint32_t N_DEVICE_CHANNELS = 8;
|
const int N_DEVICE_CHANNELS = 64;
|
||||||
|
|
||||||
const uint32_t ENGINE_REC_BUF_SIZE_IN_SAMPLES = (N_REC_SAMPLES_PER_SEC / 100);
|
const int ENGINE_REC_BUF_SIZE_IN_SAMPLES = (N_REC_SAMPLES_PER_SEC / 100);
|
||||||
const uint32_t ENGINE_PLAY_BUF_SIZE_IN_SAMPLES = (N_PLAY_SAMPLES_PER_SEC / 100);
|
const int ENGINE_PLAY_BUF_SIZE_IN_SAMPLES = (N_PLAY_SAMPLES_PER_SEC / 100);
|
||||||
|
|
||||||
enum
|
const int N_BLOCKS_IO = 2;
|
||||||
{
|
const int N_BUFFERS_IN = 2; // Must be at least N_BLOCKS_IO.
|
||||||
N_BLOCKS_IO = 2
|
const int N_BUFFERS_OUT = 3; // Must be at least N_BLOCKS_IO.
|
||||||
};
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
N_BUFFERS_IN = 10
|
|
||||||
};
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
N_BUFFERS_OUT = 3
|
|
||||||
}; // Must be at least N_BLOCKS_IO
|
|
||||||
|
|
||||||
const uint32_t TIMER_PERIOD_MS = (2 * 10 * N_BLOCKS_IO * 1000000);
|
const int TIMER_PERIOD_MS = (2 * 10 * N_BLOCKS_IO * 1000000);
|
||||||
|
|
||||||
const uint32_t REC_BUF_SIZE_IN_SAMPLES = (ENGINE_REC_BUF_SIZE_IN_SAMPLES
|
const int REC_BUF_SIZE_IN_SAMPLES =
|
||||||
* N_DEVICE_CHANNELS * N_BUFFERS_IN);
|
ENGINE_REC_BUF_SIZE_IN_SAMPLES * N_DEVICE_CHANNELS * N_BUFFERS_IN;
|
||||||
const uint32_t PLAY_BUF_SIZE_IN_SAMPLES =
|
const int PLAY_BUF_SIZE_IN_SAMPLES =
|
||||||
(ENGINE_PLAY_BUF_SIZE_IN_SAMPLES * N_PLAY_CHANNELS * N_BUFFERS_OUT);
|
ENGINE_PLAY_BUF_SIZE_IN_SAMPLES * N_PLAY_CHANNELS * N_BUFFERS_OUT;
|
||||||
|
|
||||||
class AudioDeviceMac: public AudioDeviceGeneric
|
class AudioDeviceMac: public AudioDeviceGeneric
|
||||||
{
|
{
|
||||||
@ -384,8 +375,8 @@ private:
|
|||||||
semaphore_t _renderSemaphore;
|
semaphore_t _renderSemaphore;
|
||||||
semaphore_t _captureSemaphore;
|
semaphore_t _captureSemaphore;
|
||||||
|
|
||||||
uint32_t _captureBufSizeSamples;
|
int _captureBufSizeSamples;
|
||||||
uint32_t _renderBufSizeSamples;
|
int _renderBufSizeSamples;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
Loading…
Reference in New Issue
Block a user