mirror of
https://github.com/love2d/megasource.git
synced 2026-08-19 04:05:09 +02:00
update OpenAL-Soft to 1.24.3.
This commit is contained in:
@@ -24,7 +24,9 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -32,13 +34,13 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "alstring.h"
|
||||
#include "core/converter.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
#include "fmt/core.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
@@ -56,10 +58,40 @@ namespace {
|
||||
constexpr auto OutputElement = 0;
|
||||
constexpr auto InputElement = 1;
|
||||
|
||||
// These following arrays should always be defined in ascending AudioChannelLabel value order
|
||||
constexpr std::array<AudioChannelLabel, 1> MonoChanMap { kAudioChannelLabel_Mono };
|
||||
constexpr std::array<AudioChannelLabel, 2> StereoChanMap { kAudioChannelLabel_Left, kAudioChannelLabel_Right};
|
||||
constexpr std::array<AudioChannelLabel, 4> QuadChanMap {
|
||||
kAudioChannelLabel_Left, kAudioChannelLabel_Right,
|
||||
kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround
|
||||
};
|
||||
constexpr std::array<AudioChannelLabel, 6> X51ChanMap {
|
||||
kAudioChannelLabel_Left, kAudioChannelLabel_Right,
|
||||
kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
|
||||
kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround
|
||||
};
|
||||
constexpr std::array<AudioChannelLabel, 6> X51RearChanMap {
|
||||
kAudioChannelLabel_Left, kAudioChannelLabel_Right,
|
||||
kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
|
||||
kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft
|
||||
};
|
||||
constexpr std::array<AudioChannelLabel, 7> X61ChanMap {
|
||||
kAudioChannelLabel_Left, kAudioChannelLabel_Right,
|
||||
kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen,
|
||||
kAudioChannelLabel_CenterSurround,
|
||||
kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft
|
||||
};
|
||||
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]{};
|
||||
|
||||
constexpr FourCCPrinter(UInt32 code) noexcept
|
||||
explicit constexpr FourCCPrinter(UInt32 code) noexcept
|
||||
{
|
||||
for(size_t i{0};i < sizeof(UInt32);++i)
|
||||
{
|
||||
@@ -73,7 +105,7 @@ struct FourCCPrinter {
|
||||
code >>= 8;
|
||||
}
|
||||
}
|
||||
constexpr FourCCPrinter(int code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { }
|
||||
explicit constexpr FourCCPrinter(OSStatus code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { }
|
||||
|
||||
constexpr const char *c_str() const noexcept { return mString; }
|
||||
};
|
||||
@@ -159,19 +191,19 @@ std::string GetDeviceName(AudioDeviceID devId)
|
||||
/* Clear extraneous nul chars that may have been written with the name
|
||||
* string, and return it.
|
||||
*/
|
||||
while(!devname.back())
|
||||
while(!devname.empty() && !devname.back())
|
||||
devname.pop_back();
|
||||
return devname;
|
||||
}
|
||||
|
||||
UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
|
||||
auto GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) -> UInt32
|
||||
{
|
||||
UInt32 propSize{};
|
||||
auto propSize = UInt32{};
|
||||
auto err = GetDevPropertySize(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0,
|
||||
&propSize);
|
||||
if(err)
|
||||
{
|
||||
ERR("kAudioDevicePropertyStreamConfiguration size query failed: '%s' (%u)\n",
|
||||
ERR("kAudioDevicePropertyStreamConfiguration size query failed: '{}' ({})",
|
||||
FourCCPrinter{err}.c_str(), err);
|
||||
return 0;
|
||||
}
|
||||
@@ -183,15 +215,14 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
|
||||
buflist);
|
||||
if(err)
|
||||
{
|
||||
ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n",
|
||||
ERR("kAudioDevicePropertyStreamConfiguration query failed: '{}' ({})",
|
||||
FourCCPrinter{err}.c_str(), err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
UInt32 numChannels{0};
|
||||
auto numChannels = UInt32{0};
|
||||
for(size_t i{0};i < buflist->mNumberBuffers;++i)
|
||||
numChannels += buflist->mBuffers[i].mNumberChannels;
|
||||
|
||||
return numChannels;
|
||||
}
|
||||
|
||||
@@ -201,14 +232,14 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
||||
UInt32 propSize{};
|
||||
if(auto err = GetHwPropertySize(kAudioHardwarePropertyDevices, &propSize))
|
||||
{
|
||||
ERR("Failed to get device list size: %u\n", err);
|
||||
ERR("Failed to get device list size: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown);
|
||||
if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data()))
|
||||
{
|
||||
ERR("Failed to get device list: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
ERR("Failed to get device list: '{}' ({})", FourCCPrinter{err}.c_str(), err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -223,7 +254,7 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
||||
{
|
||||
newdevs.emplace_back(DeviceEntry{defaultId, GetDeviceName(defaultId)});
|
||||
const auto &entry = newdevs.back();
|
||||
TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId);
|
||||
TRACE("Got device: {} = ID {}", entry.mName, entry.mId);
|
||||
}
|
||||
for(const AudioDeviceID devId : devIds)
|
||||
{
|
||||
@@ -240,7 +271,7 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
||||
{
|
||||
newdevs.emplace_back(DeviceEntry{devId, GetDeviceName(devId)});
|
||||
const auto &entry = newdevs.back();
|
||||
TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId);
|
||||
TRACE("Got device: {} = ID {}", entry.mName, entry.mId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,14 +286,12 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
||||
{ return entry.mName == curitem->mName; };
|
||||
if(std::find_if(newdevs.begin(), curitem, check_match) != curitem)
|
||||
{
|
||||
std::string name{curitem->mName};
|
||||
size_t count{1};
|
||||
auto name = std::string{curitem->mName};
|
||||
auto count = 1_uz;
|
||||
auto check_name = [&name](const DeviceEntry &entry) -> bool
|
||||
{ return entry.mName == name; };
|
||||
do {
|
||||
name = curitem->mName;
|
||||
name += " #";
|
||||
name += std::to_string(++count);
|
||||
name = fmt::format("{} #{}", curitem->mName, ++count);
|
||||
} while(std::find_if(newdevs.begin(), curitem, check_name) != curitem);
|
||||
curitem->mName = std::move(name);
|
||||
}
|
||||
@@ -277,18 +306,18 @@ struct DeviceHelper {
|
||||
DeviceHelper()
|
||||
{
|
||||
AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
|
||||
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
|
||||
OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
|
||||
if (status != noErr)
|
||||
ERR("AudioObjectAddPropertyListener fail: %d", status);
|
||||
ERR("AudioObjectAddPropertyListener fail: {}", status);
|
||||
}
|
||||
~DeviceHelper()
|
||||
{
|
||||
AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
|
||||
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
|
||||
OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
|
||||
if (status != noErr)
|
||||
ERR("AudioObjectRemovePropertyListener fail: %d", status);
|
||||
ERR("AudioObjectRemovePropertyListener fail: {}", status);
|
||||
}
|
||||
|
||||
static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses,
|
||||
@@ -322,7 +351,7 @@ static constexpr char ca_device[] = "CoreAudio Default";
|
||||
|
||||
|
||||
struct CoreAudioPlayback final : public BackendBase {
|
||||
CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
explicit CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~CoreAudioPlayback() override;
|
||||
|
||||
OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
@@ -377,7 +406,7 @@ void CoreAudioPlayback::open(std::string_view name)
|
||||
auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name);
|
||||
if(devmatch == PlaybackList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
"Device name \"{}\" not found", name};
|
||||
|
||||
audioDevice = devmatch->mId;
|
||||
}
|
||||
@@ -385,8 +414,8 @@ void CoreAudioPlayback::open(std::string_view name)
|
||||
if(name.empty())
|
||||
name = ca_device;
|
||||
else if(name != ca_device)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||
name};
|
||||
#endif
|
||||
|
||||
/* open the default output unit */
|
||||
@@ -410,7 +439,7 @@ void CoreAudioPlayback::open(std::string_view name)
|
||||
OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"Could not create component instance: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
@@ -421,7 +450,7 @@ void CoreAudioPlayback::open(std::string_view name)
|
||||
err = AudioUnitInitialize(audioUnit);
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"Could not initialize audio unit: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
/* WARNING: I don't know if "valid" audio unit values are guaranteed to be
|
||||
* non-0. If not, this logic is broken.
|
||||
@@ -435,7 +464,7 @@ void CoreAudioPlayback::open(std::string_view name)
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(!name.empty())
|
||||
mDevice->DeviceName = name;
|
||||
mDeviceName = name;
|
||||
else
|
||||
{
|
||||
UInt32 propSize{sizeof(audioDevice)};
|
||||
@@ -444,8 +473,8 @@ void CoreAudioPlayback::open(std::string_view name)
|
||||
kAudioUnitScope_Global, OutputElement, &audioDevice, &propSize);
|
||||
|
||||
std::string devname{GetDeviceName(audioDevice)};
|
||||
if(!devname.empty()) mDevice->DeviceName = std::move(devname);
|
||||
else mDevice->DeviceName = "Unknown Device Name";
|
||||
if(!devname.empty()) mDeviceName = std::move(devname);
|
||||
else mDeviceName = "Unknown Device Name";
|
||||
}
|
||||
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
@@ -454,16 +483,16 @@ void CoreAudioPlayback::open(std::string_view name)
|
||||
err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false,
|
||||
kAudioObjectPropertyElementMaster, sizeof(type), &type);
|
||||
if(err != noErr)
|
||||
ERR("Failed to get audio device type: %u\n", err);
|
||||
WARN("Failed to get audio device type: '{}' ({})", FourCCPrinter{err}.c_str(), err);
|
||||
else
|
||||
{
|
||||
TRACE("Got device type '%s'\n", FourCCPrinter{type}.c_str());
|
||||
TRACE("Got device type '{}'", FourCCPrinter{type}.c_str());
|
||||
mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
mDevice->DeviceName = name;
|
||||
mDeviceName = name;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -471,7 +500,7 @@ bool CoreAudioPlayback::reset()
|
||||
{
|
||||
OSStatus err{AudioUnitUninitialize(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
ERR("AudioUnitUninitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
ERR("AudioUnitUninitialize failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
|
||||
|
||||
/* retrieve default output unit's properties (output side) */
|
||||
AudioStreamBasicDescription streamFormat{};
|
||||
@@ -480,33 +509,75 @@ bool CoreAudioPlayback::reset()
|
||||
OutputElement, &streamFormat, &size);
|
||||
if(err != noErr || size != sizeof(streamFormat))
|
||||
{
|
||||
ERR("AudioUnitGetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
|
||||
ERR("AudioUnitGetProperty(StreamFormat) failed: '{}' ({})", FourCCPrinter{err}.c_str(),
|
||||
err);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
TRACE("Output streamFormat of default output unit -\n");
|
||||
TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
|
||||
TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
|
||||
TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
|
||||
TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
|
||||
TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
|
||||
TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
|
||||
#endif
|
||||
|
||||
/* Use the sample rate from the output unit's current parameters, but reset
|
||||
* everything else.
|
||||
*/
|
||||
if(mDevice->Frequency != streamFormat.mSampleRate)
|
||||
if(mDevice->mSampleRate != streamFormat.mSampleRate)
|
||||
{
|
||||
mDevice->BufferSize = static_cast<uint>(mDevice->BufferSize*streamFormat.mSampleRate/
|
||||
mDevice->Frequency + 0.5);
|
||||
mDevice->Frequency = static_cast<uint>(streamFormat.mSampleRate);
|
||||
mDevice->mBufferSize = static_cast<uint>(mDevice->mBufferSize*streamFormat.mSampleRate/
|
||||
mDevice->mSampleRate + 0.5);
|
||||
mDevice->mSampleRate = static_cast<uint>(streamFormat.mSampleRate);
|
||||
}
|
||||
|
||||
/* 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
|
||||
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 }
|
||||
}};
|
||||
|
||||
if(!mDevice->Flags.test(ChannelsRequest))
|
||||
{
|
||||
auto propSize = UInt32{};
|
||||
auto writable = Boolean{};
|
||||
|
||||
err = AudioUnitGetPropertyInfo(mAudioUnit, kAudioUnitProperty_AudioChannelLayout,
|
||||
kAudioUnitScope_Output, OutputElement, &propSize, &writable);
|
||||
if(err == noErr)
|
||||
{
|
||||
auto layout_data = std::make_unique<char[]>(propSize);
|
||||
auto *layout = reinterpret_cast<AudioChannelLayout*>(layout_data.get());
|
||||
|
||||
err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_AudioChannelLayout,
|
||||
kAudioUnitScope_Output, OutputElement, layout, &propSize);
|
||||
if(err == noErr)
|
||||
{
|
||||
auto descs = al::span{std::data(layout->mChannelDescriptions),
|
||||
layout->mNumberChannelDescriptions};
|
||||
auto labels = std::vector<AudioChannelLayoutTag>(descs.size());
|
||||
|
||||
std::transform(descs.begin(), descs.end(), labels.begin(),
|
||||
std::mem_fn(&AudioChannelDescription::mChannelLabel));
|
||||
sort(labels.begin(), labels.end());
|
||||
|
||||
auto check_labels = [&labels](const ChannelMap &chanmap) -> bool
|
||||
{
|
||||
return std::includes(labels.begin(), labels.end(), chanmap.map.begin(),
|
||||
chanmap.map.end());
|
||||
};
|
||||
auto chaniter = std::find_if(chanmaps.cbegin(), chanmaps.cend(), check_labels);
|
||||
if(chaniter != chanmaps.cend())
|
||||
mDevice->FmtChans = chaniter->fmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Also set kAudioUnitProperty_AudioChannelLayout according to the AL
|
||||
* device's channel configuration.
|
||||
*/
|
||||
streamFormat.mChannelsPerFrame = mDevice->channelsFromFmt();
|
||||
|
||||
@@ -548,7 +619,7 @@ bool CoreAudioPlayback::reset()
|
||||
OutputElement, &streamFormat, sizeof(streamFormat));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
|
||||
ERR("AudioUnitSetProperty(StreamFormat) failed: '{}' ({})", FourCCPrinter{err}.c_str(),
|
||||
err);
|
||||
return false;
|
||||
}
|
||||
@@ -566,7 +637,7 @@ bool CoreAudioPlayback::reset()
|
||||
kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty(SetRenderCallback) failed: '%s' (%u)\n",
|
||||
ERR("AudioUnitSetProperty(SetRenderCallback) failed: '{}' ({})",
|
||||
FourCCPrinter{err}.c_str(), err);
|
||||
return false;
|
||||
}
|
||||
@@ -575,7 +646,7 @@ bool CoreAudioPlayback::reset()
|
||||
err = AudioUnitInitialize(mAudioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
ERR("AudioUnitInitialize failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -587,19 +658,19 @@ void CoreAudioPlayback::start()
|
||||
const OSStatus err{AudioOutputUnitStart(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"AudioOutputUnitStart failed: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
}
|
||||
|
||||
void CoreAudioPlayback::stop()
|
||||
{
|
||||
OSStatus err{AudioOutputUnitStop(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
ERR("AudioOutputUnitStop failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
|
||||
}
|
||||
|
||||
|
||||
struct CoreAudioCapture final : public BackendBase {
|
||||
CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
explicit CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~CoreAudioCapture() override;
|
||||
|
||||
OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
@@ -650,7 +721,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
inNumberFrames, &audiobuf.list)};
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitRender capture error: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
ERR("AudioUnitRender capture error: '{}' ({})", FourCCPrinter{err}.c_str(), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -676,7 +747,7 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name);
|
||||
if(devmatch == CaptureList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
"Device name \"{}\" not found", name};
|
||||
|
||||
audioDevice = devmatch->mId;
|
||||
}
|
||||
@@ -684,8 +755,8 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
if(name.empty())
|
||||
name = ca_device;
|
||||
else if(name != ca_device)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found",
|
||||
name};
|
||||
#endif
|
||||
|
||||
AudioComponentDescription desc{};
|
||||
@@ -709,7 +780,7 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"Could not create component instance: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Turn off AudioUnit output
|
||||
UInt32 enableIO{0};
|
||||
@@ -717,7 +788,7 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not disable audio unit output property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||
"Could not disable audio unit output property: '{}' ({})", FourCCPrinter{err}.c_str(),
|
||||
err};
|
||||
|
||||
// Turn on AudioUnit input
|
||||
@@ -726,7 +797,7 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not enable audio unit input property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||
"Could not enable audio unit input property: '{}' ({})", FourCCPrinter{err}.c_str(),
|
||||
err};
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
@@ -745,7 +816,7 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not set capture callback: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"Could not set capture callback: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Disable buffer allocation for capture
|
||||
UInt32 flag{0};
|
||||
@@ -753,14 +824,14 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
kAudioUnitScope_Output, InputElement, &flag, sizeof(flag));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not disable buffer allocation property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||
"Could not disable buffer allocation property: '{}' ({})", FourCCPrinter{err}.c_str(),
|
||||
err};
|
||||
|
||||
// Initialize the device
|
||||
err = AudioUnitInitialize(mAudioUnit);
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"Could not initialize audio unit: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Get the hardware format
|
||||
AudioStreamBasicDescription hardwareFormat{};
|
||||
@@ -769,7 +840,7 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
InputElement, &hardwareFormat, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(hardwareFormat))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not get input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"Could not get input format: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Set up the requested format description
|
||||
AudioStreamBasicDescription requestedFormat{};
|
||||
@@ -822,15 +893,16 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtX714:
|
||||
case DevFmtX7144:
|
||||
case DevFmtX3D71:
|
||||
case DevFmtAmbi3D:
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "%s not supported",
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "{} not supported",
|
||||
DevFmtChannelsString(mDevice->FmtChans)};
|
||||
}
|
||||
|
||||
requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
|
||||
requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
|
||||
requestedFormat.mSampleRate = mDevice->Frequency;
|
||||
requestedFormat.mSampleRate = mDevice->mSampleRate;
|
||||
requestedFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
requestedFormat.mReserved = 0;
|
||||
requestedFormat.mFramesPerPacket = 1;
|
||||
@@ -850,18 +922,18 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
InputElement, &outputFormat, sizeof(outputFormat));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not set input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"Could not set input format: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
/* Calculate the minimum AudioUnit output format frame count for the pre-
|
||||
* conversion ring buffer. Ensure at least 100ms for the total buffer.
|
||||
*/
|
||||
double srateScale{outputFormat.mSampleRate / mDevice->Frequency};
|
||||
auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
|
||||
double srateScale{outputFormat.mSampleRate / mDevice->mSampleRate};
|
||||
auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->mBufferSize*srateScale)),
|
||||
static_cast<UInt32>(outputFormat.mSampleRate)/10_u64);
|
||||
FrameCount64 += MaxResamplerPadding;
|
||||
if(FrameCount64 > std::numeric_limits<int32_t>::max())
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Calculated frame count is too large: %" PRIu64, FrameCount64};
|
||||
"Calculated frame count is too large: {}", FrameCount64};
|
||||
|
||||
UInt32 outputFrameCount{};
|
||||
propertySize = sizeof(outputFrameCount);
|
||||
@@ -869,7 +941,7 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(outputFrameCount))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not get input frame count: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"Could not get input frame count: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
mCaptureData.resize(outputFrameCount * mFrameSize);
|
||||
|
||||
@@ -877,14 +949,14 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
|
||||
|
||||
/* Set up sample converter if needed */
|
||||
if(outputFormat.mSampleRate != mDevice->Frequency)
|
||||
if(outputFormat.mSampleRate != mDevice->mSampleRate)
|
||||
mConverter = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType,
|
||||
mFormat.mChannelsPerFrame, static_cast<uint>(hardwareFormat.mSampleRate),
|
||||
mDevice->Frequency, Resampler::FastBSinc24);
|
||||
mDevice->mSampleRate, Resampler::FastBSinc24);
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(!name.empty())
|
||||
mDevice->DeviceName = name;
|
||||
mDeviceName = name;
|
||||
else
|
||||
{
|
||||
UInt32 propSize{sizeof(audioDevice)};
|
||||
@@ -893,11 +965,11 @@ void CoreAudioCapture::open(std::string_view name)
|
||||
kAudioUnitScope_Global, InputElement, &audioDevice, &propSize);
|
||||
|
||||
std::string devname{GetDeviceName(audioDevice)};
|
||||
if(!devname.empty()) mDevice->DeviceName = std::move(devname);
|
||||
else mDevice->DeviceName = "Unknown Device Name";
|
||||
if(!devname.empty()) mDeviceName = std::move(devname);
|
||||
else mDeviceName = "Unknown Device Name";
|
||||
}
|
||||
#else
|
||||
mDevice->DeviceName = name;
|
||||
mDeviceName = name;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -907,14 +979,14 @@ void CoreAudioCapture::start()
|
||||
OSStatus err{AudioOutputUnitStart(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
"AudioOutputUnitStart failed: '{}' ({})", FourCCPrinter{err}.c_str(), err};
|
||||
}
|
||||
|
||||
void CoreAudioCapture::stop()
|
||||
{
|
||||
OSStatus err{AudioOutputUnitStop(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
ERR("AudioOutputUnitStop failed: '{}' ({})", FourCCPrinter{err}.c_str(), err);
|
||||
}
|
||||
|
||||
void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
@@ -926,16 +998,16 @@ void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
}
|
||||
|
||||
auto rec_vec = mRing->getReadVector();
|
||||
const void *src0{rec_vec.first.buf};
|
||||
auto src0len = static_cast<uint>(rec_vec.first.len);
|
||||
const void *src0{rec_vec[0].buf};
|
||||
auto src0len = static_cast<uint>(rec_vec[0].len);
|
||||
uint got{mConverter->convert(&src0, &src0len, buffer, samples)};
|
||||
size_t total_read{rec_vec.first.len - src0len};
|
||||
if(got < samples && !src0len && rec_vec.second.len > 0)
|
||||
size_t total_read{rec_vec[0].len - src0len};
|
||||
if(got < samples && !src0len && rec_vec[1].len > 0)
|
||||
{
|
||||
const void *src1{rec_vec.second.buf};
|
||||
auto src1len = static_cast<uint>(rec_vec.second.len);
|
||||
const void *src1{rec_vec[1].buf};
|
||||
auto src1len = static_cast<uint>(rec_vec[1].len);
|
||||
got += mConverter->convert(&src1, &src1len, buffer + got*mFrameSize, samples-got);
|
||||
total_read += rec_vec.second.len - src1len;
|
||||
total_read += rec_vec[1].len - src1len;
|
||||
}
|
||||
|
||||
mRing->readAdvance(total_read);
|
||||
@@ -966,23 +1038,23 @@ bool CoreAudioBackendFactory::init()
|
||||
bool CoreAudioBackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
||||
|
||||
std::string CoreAudioBackendFactory::probe(BackendType type)
|
||||
auto CoreAudioBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
std::vector<std::string> outnames;
|
||||
#if CAN_ENUMERATE
|
||||
auto append_name = [&outnames](const DeviceEntry &entry) -> void
|
||||
{
|
||||
/* Includes null char. */
|
||||
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
||||
};
|
||||
{ outnames.emplace_back(entry.mName); };
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
EnumerateDevices(PlaybackList, false);
|
||||
outnames.reserve(PlaybackList.size());
|
||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||
break;
|
||||
case BackendType::Capture:
|
||||
EnumerateDevices(CaptureList, true);
|
||||
outnames.reserve(CaptureList.size());
|
||||
std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
|
||||
break;
|
||||
}
|
||||
@@ -993,8 +1065,7 @@ std::string CoreAudioBackendFactory::probe(BackendType type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
case BackendType::Capture:
|
||||
/* Includes null char. */
|
||||
outnames.append(ca_device, sizeof(ca_device));
|
||||
outnames.emplace_back(ca_device);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user