Skip to content

Commit

Permalink
Adding channel detection to CoreAudio backend (#1056)
Browse files Browse the repository at this point in the history
* Adding channel detection to CoreAudio backend

* Improvements to CoreAudio surround detection

* Switching to Audio Channel Layout attribute

kAudioDevicePropertyPreferredChannelLayout seems to be unavailable on iOS
  • Loading branch information
colincornaby authored Nov 9, 2024
1 parent 4784183 commit 068a542
Showing 1 changed file with 91 additions and 10 deletions.
101 changes: 91 additions & 10 deletions alc/backends/coreaudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,36 @@ namespace {
constexpr auto OutputElement = 0;
constexpr auto InputElement = 1;

// These following arrays should always be defined in ascending AudioChannelLabel value order
static constexpr std::array<AudioChannelLabel, 1> MonoChanMap { kAudioChannelLabel_Mono };
static constexpr std::array<AudioChannelLabel, 2> StereoChanMap { kAudioChannelLabel_Left, kAudioChannelLabel_Right};
static constexpr std::array<AudioChannelLabel, 4> QuadChanMap {
kAudioChannelLabel_Left, kAudioChannelLabel_Right,
kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround
};
static constexpr std::array<AudioChannelLabel, 6> X51ChanMap {
kAudioChannelLabel_Left, kAudioChannelLabel_Right,
kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround
};
static constexpr std::array<AudioChannelLabel, 6> X51RearChanMap {
kAudioChannelLabel_Left, kAudioChannelLabel_Right,
kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft
};
static constexpr std::array<AudioChannelLabel, 7> X61ChanMap {
kAudioChannelLabel_Left, kAudioChannelLabel_Right,
kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
kAudioChannelLabel_CenterSurround,
kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft
};
static constexpr std::array<AudioChannelLabel, 8> X71ChanMap {
kAudioChannelLabel_Left, kAudioChannelLabel_Right,
kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround,
kAudioChannelLabel_LeftCenter, kAudioChannelLabel_RightCenter
};

struct FourCCPrinter {
char mString[sizeof(UInt32) + 1]{};

Expand Down Expand Up @@ -167,7 +197,7 @@ std::string GetDeviceName(AudioDeviceID devId)
UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
{
UInt32 propSize{};
auto err = GetDevPropertySize(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0,
auto err = GetDevPropertySize(devId, kAudioUnitProperty_AudioChannelLayout, isCapture, 0,
&propSize);
if(err)
{
Expand All @@ -176,23 +206,19 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
return 0;
}

auto buflist_data = std::make_unique<char[]>(propSize);
auto *buflist = reinterpret_cast<AudioBufferList*>(buflist_data.get());
auto channel_data = std::make_unique<char[]>(propSize);
auto *channel_layout = reinterpret_cast<AudioChannelLayout*>(channel_data.get());

err = GetDevProperty(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0, propSize,
buflist);
err = GetDevProperty(devId, kAudioUnitProperty_AudioChannelLayout, isCapture, 0, propSize,
channel_layout);
if(err)
{
ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n",
FourCCPrinter{err}.c_str(), err);
return 0;
}

UInt32 numChannels{0};
for(size_t i{0};i < buflist->mNumberBuffers;++i)
numChannels += buflist->mBuffers[i].mNumberChannels;

return numChannels;
return channel_layout->mNumberChannelDescriptions;
}


Expand Down Expand Up @@ -505,6 +531,22 @@ bool CoreAudioPlayback::reset()
mDevice->Frequency = static_cast<uint>(streamFormat.mSampleRate);
}

struct ChannelMap {
DevFmtChannels fmt;
al::span<const AudioChannelLabel> map;
bool is_51rear;
};

static constexpr std::array<ChannelMap,7> chanmaps{{
{ DevFmtX71, X71ChanMap, false },
{ DevFmtX61, X61ChanMap, false },
{ DevFmtX51, X51ChanMap, false },
{ DevFmtX51, X51RearChanMap, true },
{ DevFmtQuad, QuadChanMap, false },
{ DevFmtStereo, StereoChanMap, false },
{ DevFmtMono, MonoChanMap, false }
}};

/* FIXME: How to tell what channels are what in the output device, and how
* to specify what we're giving? e.g. 6.0 vs 5.1
*/
Expand Down Expand Up @@ -552,6 +594,45 @@ bool CoreAudioPlayback::reset()
err);
return false;
}

if(!mDevice->Flags.test(ChannelsRequest))
{
UInt32 propSize{};
Boolean writable;
err = AudioUnitGetPropertyInfo(mAudioUnit,
kAudioUnitProperty_AudioChannelLayout,
kAudioUnitScope_Output,
OutputElement,
&propSize,
&writable);
auto layout = std::make_unique<AudioChannelLayout[]>(propSize);

if(err == noErr)
{
err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, OutputElement, layout.get(), &propSize);

std::vector labels = std::vector<AudioChannelLayoutTag>(layout.get()->mNumberChannelDescriptions);

for(UInt32 i=0; i<layout.get()->mNumberChannelDescriptions; i++) {
labels[i] = layout.get()->mChannelDescriptions[i].mChannelLabel;
}
sort(labels.begin(), labels.end(), std::less<int>());

if(err == noErr)
{
auto chaniter = std::find_if(chanmaps.cbegin(), chanmaps.cend(),
[&labels](const ChannelMap &chanmap) -> bool
{
return std::includes(labels.begin(), labels.end(), chanmap.map.begin(), chanmap.map.end());
}
);
if(chaniter != chanmaps.cend())
{
mDevice->FmtChans = chaniter->fmt;
}
}
}
}

setDefaultWFXChannelOrder();

Expand Down

0 comments on commit 068a542

Please sign in to comment.