From 6f69eb78dd316ab4c32b2f8b3f4ff3cd9016c613 Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Fri, 7 Jun 2013 17:56:50 +0000 Subject: [PATCH] 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 --- .../audio_device/mac/audio_device_mac.cc | 79 ++++++++++++------- .../audio_device/mac/audio_device_mac.h | 43 ++++------ 2 files changed, 66 insertions(+), 56 deletions(-) diff --git a/webrtc/modules/audio_device/mac/audio_device_mac.cc b/webrtc/modules/audio_device/mac/audio_device_mac.cc index 78555bd74..a7e78baf0 100644 --- a/webrtc/modules/audio_device/mac/audio_device_mac.cc +++ b/webrtc/modules/audio_device/mac/audio_device_mac.cc @@ -373,10 +373,10 @@ int32_t AudioDeviceMac::Init() return -1; } - // 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 - // must be explicitly specified. HAL would otherwise try to use the main thread to - // issue notifications. + // 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 must be explicitly specified. HAL would otherwise try to use the + // main thread to issue notifications. AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, @@ -495,8 +495,8 @@ int32_t AudioDeviceMac::SpeakerIsAvailable(bool& available) 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; // Close the initialized output mixer @@ -554,8 +554,8 @@ int32_t AudioDeviceMac::MicrophoneIsAvailable(bool& available) 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; // Close the initialized input mixer @@ -1363,13 +1363,13 @@ int32_t AudioDeviceMac::InitPlayout() _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, &propertyAddress, &objectListenerProc, this)); } } - // Get current stream description + // Get current stream description propertyAddress.mSelector = kAudioDevicePropertyStreamFormat; memset(&_outStreamFormat, 0, sizeof(_outStreamFormat)); size = sizeof(_outStreamFormat); @@ -1387,8 +1387,8 @@ int32_t AudioDeviceMac::InitPlayout() if (_outStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) { WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Too many channels on device -> mChannelsPerFrame = %d", - _outStreamFormat.mChannelsPerFrame); + "Too many channels on output device (mChannelsPerFrame = %d)", + _outStreamFormat.mChannelsPerFrame); return -1; } @@ -1415,13 +1415,12 @@ int32_t AudioDeviceMac::InitPlayout() _outStreamFormat.mBytesPerFrame, _outStreamFormat.mBitsPerChannel); WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mFormatFlags = %u, mChannelsPerFrame = %u", - _outStreamFormat.mFormatFlags, - _outStreamFormat.mChannelsPerFrame); + "mFormatFlags = %u", + _outStreamFormat.mFormatFlags); logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID", (const char *) &_outStreamFormat.mFormatID); - // Our preferred format to work with + // Our preferred format to work with _outDesiredFormat.mSampleRate = N_PLAY_SAMPLES_PER_SEC; if (_outStreamFormat.mChannelsPerFrame >= 2 && (_playChannels == 2)) { @@ -1447,7 +1446,7 @@ int32_t AudioDeviceMac::InitPlayout() _outDesiredFormat.mBytesPerPacket = _outDesiredFormat.mChannelsPerFrame * sizeof(SInt16); - _outDesiredFormat.mFramesPerPacket = 1; // In uncompressed audio, + _outDesiredFormat.mFramesPerPacket = 1; // In uncompressed audio, // a packet is one frame. _outDesiredFormat.mBytesPerFrame = _outDesiredFormat.mChannelsPerFrame * sizeof(SInt16); @@ -1595,7 +1594,7 @@ int32_t AudioDeviceMac::InitRecording() _captureDeviceIsAlive = 1; _doStopRec = false; - // Get current stream description + // Get current stream description AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, 0 }; @@ -1615,8 +1614,18 @@ int32_t AudioDeviceMac::InitRecording() if (_inStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) { WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - ", Too many channels on device (mChannelsPerFrame = %d)", - _inStreamFormat.mChannelsPerFrame); + "Too many channels on input device (mChannelsPerFrame = %d)", + _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; } @@ -1634,9 +1643,8 @@ int32_t AudioDeviceMac::InitRecording() _inStreamFormat.mBytesPerFrame, _inStreamFormat.mBitsPerChannel); WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " mFormatFlags = %u, mChannelsPerFrame = %u", - _inStreamFormat.mFormatFlags, - _inStreamFormat.mChannelsPerFrame); + " mFormatFlags = %u", + _inStreamFormat.mFormatFlags); logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID", (const char *) &_inStreamFormat.mFormatID); @@ -1931,7 +1939,7 @@ bool AudioDeviceMac::PlayoutIsInitialized() const int32_t AudioDeviceMac::StartPlayout() { - + CriticalSectionScoped lock(&_critSect); if (!_playIsInitialized) @@ -2283,7 +2291,7 @@ AudioDeviceMac::GetNumberDevices(const AudioObjectPropertyScope scope, return -1; } - // Happy ending + // Happy ending if (deviceIds) { free(deviceIds); @@ -2435,7 +2443,7 @@ int32_t AudioDeviceMac::InitDevice(const uint16_t userDeviceIndex, } // 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 devManf[128]; memset(devName, 0, sizeof(devName)); @@ -2488,7 +2496,7 @@ OSStatus AudioDeviceMac::implObjectListenerProc( { WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, "AudioDeviceMac::implObjectListenerProc()"); - + for (UInt32 i = 0; i < numberAddresses; i++) { if (addresses[i].mSelector == kAudioHardwarePropertyDevices) @@ -2545,7 +2553,7 @@ int32_t AudioDeviceMac::HandleDeviceChange() logCAMsg(kTraceError, kTraceAudioDevice, _id, "Error in AudioDeviceGetProperty()", (const char*) &err); return -1; - } + } } if (SpeakerIsInitialized()) @@ -2629,13 +2637,24 @@ int32_t AudioDeviceMac::HandleStreamFormatChange( "mBytesPerFrame = %u, mBitsPerChannel = %u", streamFormat.mBytesPerFrame, streamFormat.mBitsPerChannel); WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mFormatFlags = %u, mChannelsPerFrame = %u", - streamFormat.mFormatFlags, streamFormat.mChannelsPerFrame); + "mFormatFlags = %u", + streamFormat.mFormatFlags); logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID", (const char *) &streamFormat.mFormatID); 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)); if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2)) diff --git a/webrtc/modules/audio_device/mac/audio_device_mac.h b/webrtc/modules/audio_device/mac/audio_device_mac.h index 258216c44..f5f573ca8 100644 --- a/webrtc/modules/audio_device/mac/audio_device_mac.h +++ b/webrtc/modules/audio_device/mac/audio_device_mac.h @@ -26,35 +26,26 @@ namespace webrtc class EventWrapper; class ThreadWrapper; -const uint32_t N_REC_SAMPLES_PER_SEC = 48000; -const uint32_t N_PLAY_SAMPLES_PER_SEC = 48000; +const int N_REC_SAMPLES_PER_SEC = 48000; +const int N_PLAY_SAMPLES_PER_SEC = 48000; -const uint32_t N_REC_CHANNELS = 1; // default is mono recording -const uint32_t N_PLAY_CHANNELS = 2; // default is stereo playout -const uint32_t N_DEVICE_CHANNELS = 8; +const int N_REC_CHANNELS = 1; // default is mono recording +const int N_PLAY_CHANNELS = 2; // default is stereo playout +const int N_DEVICE_CHANNELS = 64; -const uint32_t 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_REC_BUF_SIZE_IN_SAMPLES = (N_REC_SAMPLES_PER_SEC / 100); +const int ENGINE_PLAY_BUF_SIZE_IN_SAMPLES = (N_PLAY_SAMPLES_PER_SEC / 100); -enum -{ - N_BLOCKS_IO = 2 -}; -enum -{ - N_BUFFERS_IN = 10 -}; -enum -{ - N_BUFFERS_OUT = 3 -}; // Must be at least N_BLOCKS_IO +const int N_BLOCKS_IO = 2; +const int N_BUFFERS_IN = 2; // Must be at least N_BLOCKS_IO. +const int 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 - * N_DEVICE_CHANNELS * N_BUFFERS_IN); -const uint32_t PLAY_BUF_SIZE_IN_SAMPLES = - (ENGINE_PLAY_BUF_SIZE_IN_SAMPLES * N_PLAY_CHANNELS * N_BUFFERS_OUT); +const int REC_BUF_SIZE_IN_SAMPLES = + ENGINE_REC_BUF_SIZE_IN_SAMPLES * N_DEVICE_CHANNELS * N_BUFFERS_IN; +const int PLAY_BUF_SIZE_IN_SAMPLES = + ENGINE_PLAY_BUF_SIZE_IN_SAMPLES * N_PLAY_CHANNELS * N_BUFFERS_OUT; class AudioDeviceMac: public AudioDeviceGeneric { @@ -384,8 +375,8 @@ private: semaphore_t _renderSemaphore; semaphore_t _captureSemaphore; - uint32_t _captureBufSizeSamples; - uint32_t _renderBufSizeSamples; + int _captureBufSizeSamples; + int _renderBufSizeSamples; }; } // namespace webrtc