Replace deprecated macOS APIs with modern equivalents

This commit is contained in:
Martin Natano
2022-06-05 12:04:37 +02:00
parent 5f9f0608c2
commit 3eb239ad3b
16 changed files with 110 additions and 273 deletions
+51 -94
View File
@@ -20,6 +20,7 @@ extern "C" {
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>
#import <AppKit/NSScreen.h>
#import <Foundation/Foundation.h>
static bool IsFatalSignal( int signal )
@@ -146,7 +147,7 @@ void ArchHooks_MacOSX::DumpDebugInfo()
{
// http://stackoverflow.com/a/891336
NSDictionary *version = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
NSString *productVersion = [version objectForKey:@"ProductVersion"];
NSString *productVersion = version[@"ProductVersion"];
SystemVersion = ssprintf("macOS %s", [productVersion cStringUsingEncoding:[NSString defaultCStringEncoding]]);
}
@@ -158,16 +159,9 @@ void ArchHooks_MacOSX::DumpDebugInfo()
{
uint64_t iRam = 0;
GET_PARAM( "hw.memsize", iRam );
if( iRam >= 1073741824 )
{
fRam = float( double(iRam) / 1073741824.0 );
ramPower = 'G';
}
else
{
fRam = float( double(iRam) / 1048576.0 );
ramPower = 'M';
}
fRam = float( double(iRam) / 1073741824.0 );
ramPower = 'G';
}
// Get processor information
@@ -175,7 +169,7 @@ void ArchHooks_MacOSX::DumpDebugInfo()
int iCPUs = 0;
float fFreq;
char freqPower;
RString sModel;
RString sModel("Unknown");
do {
char szModel[128];
uint64_t iFreq;
@@ -184,49 +178,23 @@ void ArchHooks_MacOSX::DumpDebugInfo()
GET_PARAM( "hw.logicalcpu", iCPUs );
GET_PARAM( "hw.cpufrequency", iFreq );
if( iFreq >= 1000000000 )
{
fFreq = float( double(iFreq) / 1000000000.0 );
freqPower = 'G';
}
else
{
fFreq = float( double(iFreq) / 1000000.0 );
freqPower = 'M';
}
fFreq = float( double(iFreq) / 1000000000.0 );
freqPower = 'G';
if( GET_PARAM("hw.model", szModel) )
{
sModel = "Unknown";
if( GET_PARAM("hw.model", szModel) != 0 )
break;
}
sModel = szModel;
CFURLRef urlRef = CFBundleCopyResourceURL( CFBundleGetMainBundle(), CFSTR("Hardware.plist"), nil, nil);
if( urlRef == nil)
NSURL* url = [NSURL fileURLWithPath:@"//System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/en.lproj/SIMachineAttributes.plist"];
NSDictionary* machineAttributes = [NSDictionary dictionaryWithContentsOfURL:url];
if (machineAttributes == nil)
break;
CFDataRef dataRef = nil;
SInt32 error;
CFURLCreateDataAndPropertiesFromResource( nil, urlRef, &dataRef, nil, nil, &error );
CFRelease( urlRef );
if( dataRef == nil)
break;
// This also works with binary property lists for some reason.
CFPropertyListRef plRef = CFPropertyListCreateFromXMLData( nil, dataRef, kCFPropertyListImmutable, nil);
CFRelease( dataRef );
if( plRef == nil)
break;
if( CFGetTypeID(plRef) != CFDictionaryGetTypeID() )
{
CFRelease( plRef );
break;
}
CFStringRef keyRef = CFStringCreateWithCStringNoCopy( nil, szModel, kCFStringEncodingMacRoman, kCFAllocatorNull );
CFStringRef modelRef = (CFStringRef)CFDictionaryGetValue( (CFDictionaryRef)plRef, keyRef );
if( modelRef )
sModel = CFStringGetCStringPtr( modelRef, kCFStringEncodingMacRoman );
CFRelease( keyRef );
CFRelease( plRef );
NSString* key = [NSString stringWithUTF8String:szModel];
NSString* val = machineAttributes[key][@"_LOCALIZABLE_"][@"marketingModel"];
if (val != nil)
sModel = [val UTF8String];
} while( false );
#undef GET_PARAM
@@ -303,16 +271,6 @@ int64_t ArchHooks::GetMicrosecondsSinceStart( bool bAccurate )
#include "RageFileManager.h"
static void PathForFolderType( char dir[PATH_MAX], OSType folderType )
{
FSRef fs;
if( FSFindFolder(kUserDomain, folderType, kDontCreateFolder, &fs) )
FAIL_M( ssprintf("FSFindFolder(%lu) failed.", folderType) );
if( FSRefMakePath(&fs, (UInt8 *)dir, PATH_MAX) )
FAIL_M( "FSRefMakePath() failed." );
}
void ArchHooks::MountInitialFilesystems( const RString &sDirOfExecutable )
{
FILEMAN->Mount("dirro", sDirOfExecutable, "/");
@@ -374,41 +332,48 @@ void ArchHooks::MountInitialFilesystems( const RString &sDirOfExecutable )
}
}
static std::string PathForDirectory( NSSearchPathDirectory directory )
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *url = [fileManager URLForDirectory:directory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
if (url == nil)
FAIL_M( "URLForDirectory() failed." );
return [url fileSystemRepresentation];
}
void ArchHooks::MountUserFilesystems( const RString &sDirOfExecutable )
{
char dir[PATH_MAX];
// /Save -> ~/Library/Preferences/PRODUCT_ID
PathForFolderType( dir, kPreferencesFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID, dir), "/Save" );
std::string libraryDir = PathForDirectory(NSLibraryDirectory);
FILEMAN->Mount( "dir", libraryDir + "/Preferences/" PRODUCT_ID, "/Save" );
// Other stuff -> ~/Library/Application Support/PRODUCT_ID/*
PathForFolderType( dir, kApplicationSupportFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/Announcers", dir), "/Announcers" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/BGAnimations", dir), "/BGAnimations" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/BackgroundEffects", dir), "/BackgroundEffects" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/BackgroundTransitions", dir), "/BackgroundTransitions" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/CDTitles", dir), "/CDTitles" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/Characters", dir), "/Characters" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/Courses", dir), "/Courses" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/Downloads", dir), "/Downloads" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/NoteSkins", dir), "/NoteSkins" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/Packages", dir), "/Packages" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/Songs", dir), "/Songs" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/RandomMovies", dir), "/RandomMovies" );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/Themes", dir), "/Themes" );
std::string appSupportDir = PathForDirectory(NSApplicationSupportDirectory);
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/Announcers", "/Announcers" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/BGAnimations", "/BGAnimations" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/BackgroundEffects", "/BackgroundEffects" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/BackgroundTransitions", "/BackgroundTransitions" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/CDTitles", "/CDTitles" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/Characters", "/Characters" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/Courses", "/Courses" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/Downloads", "/Downloads" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/NoteSkins", "/NoteSkins" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/Packages", "/Packages" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/Songs", "/Songs" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/RandomMovies", "/RandomMovies" );
FILEMAN->Mount( "dir", appSupportDir + "/" PRODUCT_ID "/Themes", "/Themes" );
// /Screenshots -> ~/Pictures/PRODUCT_ID Screenshots
PathForFolderType( dir, kPictureDocumentsFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID " Screenshots", dir), "/Screenshots" );
std::string picturesDir = PathForDirectory(NSCachesDirectory);
FILEMAN->Mount( "dir", picturesDir + "/" PRODUCT_ID " Screenshots", "/Screenshots" );
// /Cache -> ~/Library/Caches/PRODUCT_ID
PathForFolderType( dir, kCachedDataFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID, dir), "/Cache" );
std::string cachesDir = PathForDirectory(NSCachesDirectory);
FILEMAN->Mount( "dir", cachesDir + "/" PRODUCT_ID, "/Cache" );
// /Logs -> ~/Library/Logs/PRODUCT_ID
PathForFolderType( dir, kDomainLibraryFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/Logs/" PRODUCT_ID, dir), "/Logs" );
FILEMAN->Mount( "dir", libraryDir + "/Logs/" PRODUCT_ID, "/Logs" );
}
static inline int GetIntValue( CFTypeRef r )
@@ -423,16 +388,8 @@ static inline int GetIntValue( CFTypeRef r )
float ArchHooks_MacOSX::GetDisplayAspectRatio()
{
io_connect_t displayPort = CGDisplayIOServicePort( CGMainDisplayID() );
CFDictionaryRef dict = IODisplayCreateInfoDictionary( displayPort, 0 );
int width = GetIntValue( CFDictionaryGetValue(dict, CFSTR(kDisplayHorizontalImageSize)) );
int height = GetIntValue( CFDictionaryGetValue(dict, CFSTR(kDisplayVerticalImageSize)) );
CFRelease( dict );
if( width && height )
return float(width)/height;
return 4/3.f;
NSScreen *screen = [NSScreen mainScreen];
return screen.frame.size.width / screen.frame.size.height;
}
/*
@@ -17,14 +17,13 @@ REGISTER_INPUT_HANDLER_CLASS2( HID, MacOSX_HID );
void InputHandler_MacOSX_HID::QueueCallback( void *target, int result, void *refcon, void *sender )
{
// The result seems useless as you can't actually return anything...
// refcon is the Device number
RageTimer now;
InputHandler_MacOSX_HID *This = (InputHandler_MacOSX_HID *)target;
IOHIDQueueInterface **queue = (IOHIDQueueInterface **)sender;
IOHIDEventStruct event;
AbsoluteTime zeroTime = { 0, 0 };
HIDDevice *dev = This->m_vDevices[size_t( refcon )];
HIDDevice *dev = static_cast<HIDDevice*>(refcon);
vector<DeviceInput> vPresses;
while( (result = CALL(queue, getNextEvent, &event, zeroTime, 0)) == kIOReturnSuccess )
@@ -116,11 +115,9 @@ void InputHandler_MacOSX_HID::DeviceChanged( void *refCon, io_service_t service,
// m_LoopRef needs to be set before this is called
void InputHandler_MacOSX_HID::StartDevices()
{
int n = 0;
ASSERT( m_LoopRef );
for (HIDDevice *i : m_vDevices)
i->StartQueue( m_LoopRef, InputHandler_MacOSX_HID::QueueCallback, this, n++ );
i->StartQueue( m_LoopRef, InputHandler_MacOSX_HID::QueueCallback, this );
CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource( m_NotifyPort );
@@ -397,8 +394,10 @@ static wchar_t KeyCodeToChar(CGKeyCode keyCode, unsigned int modifierFlags)
if( status != noErr )
{
fprintf(stderr, "There was an %s error translating from the '%d' key code to a human readable string: %s\n",
GetMacOSStatusErrorString(status), (int)status, GetMacOSStatusCommentString(status));
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
const char* errorDescription = [error.localizedDescription UTF8String];
fprintf(stderr, "There was an error translating from the '%d' key code to a human readable string: %s\n",
keyCode, errorDescription);
}
else if( actualStringLength == 0 )
{
@@ -52,7 +52,7 @@
[m_Text setSelectable:NO];
[m_Text setDrawsBackground:NO];
[m_Text setBackgroundColor:[NSColor lightGrayColor]];
[m_Text setAlignment:NSCenterTextAlignment];
[m_Text setAlignment:NSTextAlignmentCenter];
[m_Text setHorizontallyResizable:NO];
[m_Text setVerticallyResizable:NO];
[m_Text setString:@"Initializing Hardware..."];
@@ -64,17 +64,15 @@
windowRect = NSMakeRect( 0, 0, size.width, size.height + height + progressHeight + padding);
m_Window = [[NSWindow alloc] initWithContentRect:windowRect
styleMask:NSTitledWindowMask
styleMask:NSWindowStyleMaskTitled
backing:NSBackingStoreBuffered
defer:YES];
NSView *view = [m_Window contentView];
// Set some properties.
[m_Window setOneShot:YES];
[m_Window setReleasedWhenClosed:YES];
[m_Window setExcludedFromWindowsMenu:YES];
[m_Window useOptimizedDrawing:YES];
[m_Window setTitle:@PRODUCT_FAMILY];
[m_Window center];
@@ -17,8 +17,8 @@ extern "C" {
}
static const unsigned int g_iStyleMask = NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask | NSResizableWindowMask;
static const unsigned int g_iStyleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable;
static bool g_bResized;
static int g_iWidth;
static int g_iHeight;
@@ -94,7 +94,6 @@ public:
defer:YES];
[m_Window setExcludedFromWindowsMenu:YES];
[m_Window useOptimizedDrawing:YES];
[m_Window setReleasedWhenClosed:NO];
[m_Window setDelegate:static_cast<id<NSWindowDelegate> >(self)];
}
@@ -16,28 +16,10 @@ static const UInt32 kBytesPerPacket = kChannelsPerFrame * kBitsPerChannel / 8;
static const UInt32 kBytesPerFrame = kBytesPerPacket;
static const UInt32 kFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsFloat;
#define WERROR(str, num, extra...) str ": '%s' (%lu).", ## extra, FourCCToString(num).c_str(), (num)
#define ERROR(str, num, extra...) (ssprintf(WERROR(str, (num), ## extra)))
static inline RString FourCCToString( uint32_t num )
static const char *FormatOSError(OSStatus status)
{
RString s( 4, '?' );
char c;
c = (num >> 24) & 0xFF;
if( c >='\x20' && c <= '\x7e' )
s[0] = c;
c = (num >> 16) & 0xFF;
if( c >='\x20' && c <= '\x7e' )
s[1] = c;
c = (num >> 8) & 0xFF;
if( c >='\x20' && c <= '\x7e' )
s[2] = c;
c = num & 0xFF;
if( c >= '\x20' && c <= '\x7e' )
s[3] = c;
return s;
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
return [error.localizedDescription UTF8String];
}
RageSoundDriver_AU::RageSoundDriver_AU() : m_OutputUnit(nullptr), m_iSampleRate(0), m_bDone(false), m_bStarted(false),
@@ -54,7 +36,7 @@ static void SetSampleRate( AudioUnit au, Float64 desiredRate )
if( (error = AudioUnitGetProperty(au, kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, 0, &OutputDevice, &size)) )
{
LOG->Warn( WERROR("No output device", error) );
LOG->Warn("No output device: %s", FormatOSError(error));
return;
}
@@ -68,7 +50,7 @@ static void SetSampleRate( AudioUnit au, Float64 desiredRate )
size = sizeof( Float64 );
if( (error = AudioObjectGetPropertyData(OutputDevice, &RateAddr, 0, NULL, &size, &rate)) )
{
LOG->Warn( WERROR("Couldn't get the device's sample rate", error) );
LOG->Warn("Couldn't get the device's sample rate: %s", FormatOSError(error));
return;
}
if( rate == desiredRate )
@@ -82,7 +64,7 @@ static void SetSampleRate( AudioUnit au, Float64 desiredRate )
if( (error = AudioObjectGetPropertyData(OutputDevice, &AvailableRatesAddr, 0, nullptr, &size, nullptr)) )
{
LOG->Warn( WERROR("Couldn't get available nominal sample rates info", error) );
LOG->Warn("Couldn't get available nominal sample rates info: %s", FormatOSError(error));
return;
}
@@ -91,7 +73,7 @@ static void SetSampleRate( AudioUnit au, Float64 desiredRate )
if( (error = AudioObjectGetPropertyData(OutputDevice, &AvailableRatesAddr, 0, NULL, &size, ranges)) )
{
LOG->Warn( WERROR("Couldn't get available nominal sample rates", error) );
LOG->Warn("Couldn't get available nominal sample rates: %s", FormatOSError(error));
delete[] ranges;
return;
}
@@ -115,7 +97,7 @@ static void SetSampleRate( AudioUnit au, Float64 desiredRate )
if( (error = AudioObjectSetPropertyData(OutputDevice, &RateAddr, 0, nullptr, sizeof(Float64), &bestRate)) )
{
LOG->Warn( WERROR("Couldn't set the device's sample rate", error) );
LOG->Warn("Couldn't set the device's sample rate: %s", FormatOSError(error));
}
}
@@ -138,7 +120,7 @@ RString RageSoundDriver_AU::Init()
OSStatus error = AudioComponentInstanceNew( comp, &m_OutputUnit );
if( error != noErr || m_OutputUnit == nullptr )
return ERROR( "Could not open the default output unit", error );
return ssprintf("Could not open the default output unit: %s", FormatOSError(error));
// Set up a callback function to generate output to the output unit
AURenderCallbackStruct input;
@@ -152,7 +134,7 @@ RString RageSoundDriver_AU::Init()
&input,
sizeof(input) );
if( error != noErr )
return ERROR( "Failed to set render callback", error );
return ssprintf("Failed to set render callback: %s", FormatOSError(error));
AudioStreamBasicDescription streamFormat;
@@ -181,7 +163,7 @@ RString RageSoundDriver_AU::Init()
&streamFormat,
sizeof(AudioStreamBasicDescription) );
if( error != noErr )
return ERROR( "Failed to set AU stream format", error );
return ssprintf("Failed to set AU stream format: %s", FormatOSError(error));
UInt32 renderQuality = kRenderQuality_Max;
error = AudioUnitSetProperty( m_OutputUnit,
@@ -191,16 +173,16 @@ RString RageSoundDriver_AU::Init()
&renderQuality,
sizeof(renderQuality) );
if( error != noErr )
LOG->Warn( WERROR("Failed to set the maximum render quality", error) );
LOG->Warn("Failed to set the maximum render quality: %s", FormatOSError(error));
// Initialize the AU.
if( (error = AudioUnitInitialize(m_OutputUnit)) )
return ERROR( "Could not initialize the AudioUnit", error );
return ssprintf("Could not initialize the AudioUnit: %s", FormatOSError(error));
StartDecodeThread();
if( (error = AudioOutputUnitStart(m_OutputUnit)) )
return ERROR( "Could not start the AudioUnit", error );
return ssprintf("Could not start the AudioUnit: %s", FormatOSError(error));
m_bStarted = true;
return RString();
}
@@ -215,7 +197,7 @@ RageSoundDriver_AU::~RageSoundDriver_AU()
m_Semaphore.Wait();
}
AudioUnitUninitialize( m_OutputUnit );
CloseComponent( m_OutputUnit );
AudioComponentInstanceDispose( m_OutputUnit );
delete m_pIOThread;
delete m_pNotificationThread;
}
@@ -245,7 +227,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
if( (error = AudioUnitGetProperty(m_OutputUnit, kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, 0, &OutputDevice, &size)) )
{
LOG->Warn( WERROR("No output device", error) );
LOG->Warn("No output device: %s", FormatOSError(error));
return 0.0f;
}
@@ -258,7 +240,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
size = sizeof( Float64 );
if( (error = AudioObjectGetPropertyData(OutputDevice, &RateAddr, 0, nullptr, &size, &sampleRate)) )
{
LOG->Warn( WERROR("Couldn't get the device sample rate", error) );
LOG->Warn("Couldn't get the device sample rate: %s", FormatOSError(error));
return 0.0f;
}
@@ -271,7 +253,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
size = sizeof( UInt32 );
if( (error = AudioObjectGetPropertyData(OutputDevice, &BufferAddr, 0, nullptr, &size, &bufferSize)) )
{
LOG->Warn( WERROR("Couldn't determine buffer size", error) );
LOG->Warn("Couldn't determine buffer size: %s", FormatOSError(error));
bufferSize = 0;
}
@@ -286,7 +268,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
size = sizeof( UInt32 );
if( (error = AudioObjectGetPropertyData(OutputDevice, &LatencyAddr, 0, nullptr, &size, &frames)) )
{
LOG->Warn( WERROR( "Couldn't get device latency", error) );
LOG->Warn("Couldn't get device latency: %s", FormatOSError(error));
frames = 0;
}
@@ -300,7 +282,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
size = sizeof( UInt32 );
if( (error = AudioObjectGetPropertyData(OutputDevice, &SafetyAddr, 0, nullptr, &size, &frames)) )
{
LOG->Warn( WERROR("Couldn't get device safety offset", error) );
LOG->Warn("Couldn't get device safety offset: %s", FormatOSError(error));
frames = 0;
}
bufferSize += frames;
@@ -315,7 +297,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
if( (error = AudioObjectGetPropertyData(OutputDevice, &StreamsAddr, 0, nullptr, &size, nullptr)) )
{
LOG->Warn( WERROR("Device has no streams", error) );
LOG->Warn("Device has no streams: %s", FormatOSError(error));
break;
}
int num = size / sizeof( AudioStreamID );
@@ -328,7 +310,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
if( (error = AudioObjectGetPropertyData(OutputDevice, &StreamsAddr, 0, nullptr, &size, streams)) )
{
LOG->Warn( WERROR("Cannot get device's streams", error) );
LOG->Warn("Cannot get device's streams: %s", FormatOSError(error));
delete[] streams;
break;
}
@@ -341,7 +323,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
if( (error = AudioObjectGetPropertyData(streams[0], &LatencyAddr, 0, nullptr, &size, &frames)) )
{
LOG->Warn( WERROR("Stream does not report latency", error) );
LOG->Warn("Stream does not report latency: %s", FormatOSError(error));
frames = 0;
}
delete[] streams;