From 91898e9e77d84e0d67dd262e448f548d48394151 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Thu, 9 Jun 2022 01:38:30 -0700 Subject: [PATCH] [Audio Threads] Change workgroup system again Now it allocates audio workgroups per thread, using work slices like the Apple documentation describes for asynchronous threads. Signed-off-by: Christopher Snowhill --- Audio/Chain/ConverterNode.mm | 2 ++ Audio/Chain/InputNode.m | 2 ++ Audio/Chain/Node.h | 5 ++- Audio/Chain/Node.m | 60 +++++++++++++++++++++++++++++++--- Audio/Output/OutputCoreAudio.h | 4 --- Audio/Output/OutputCoreAudio.m | 32 ------------------ 6 files changed, 63 insertions(+), 42 deletions(-) diff --git a/Audio/Chain/ConverterNode.mm b/Audio/Chain/ConverterNode.mm index bc3a59dd4..697c9c963 100644 --- a/Audio/Chain/ConverterNode.mm +++ b/Audio/Chain/ConverterNode.mm @@ -430,9 +430,11 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes while(paused) { usleep(500); } + [self startWorkslice]; @autoreleasepool { amountConverted = [self convert:writeBuf amount:CHUNK_SIZE]; } + [self endWorkslice]; if(!amountConverted) { if(paused) { continue; diff --git a/Audio/Chain/InputNode.m b/Audio/Chain/InputNode.m index 792c2361a..f8f5705b2 100644 --- a/Audio/Chain/InputNode.m +++ b/Audio/Chain/InputNode.m @@ -178,9 +178,11 @@ int framesToRead = CHUNK_SIZE - amountInBuffer; int framesRead; + [self startWorkslice]; @autoreleasepool { framesRead = [decoder readAudio:((char *)inputBuffer) + bytesInBuffer frames:framesToRead]; } + [self endWorkslice]; if(framesRead > 0 && !seekError) { amountInBuffer += framesRead; diff --git a/Audio/Chain/Node.h b/Audio/Chain/Node.h index b4dcdeea1..723801db0 100644 --- a/Audio/Chain/Node.h +++ b/Audio/Chain/Node.h @@ -34,7 +34,8 @@ uint32_t nodeChannelConfig; BOOL nodeLossless; - os_workgroup_t wg; + int64_t intervalMachLength; + os_workgroup_interval_t workgroup, wg; os_workgroup_join_token_s wgToken; } - (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p; @@ -49,6 +50,8 @@ - (void)followWorkgroup; - (void)leaveWorkgroup; +- (void)startWorkslice; +- (void)endWorkslice; - (void)launchThread; diff --git a/Audio/Chain/Node.m b/Audio/Chain/Node.m index 7150c02f2..ec87734b3 100644 --- a/Audio/Chain/Node.m +++ b/Audio/Chain/Node.m @@ -13,6 +13,20 @@ #import "OutputCoreAudio.h" +#import + +// This workgroup attribute isn't currently used. Set it to NULL. +static os_workgroup_attr_t _Nullable attr = nil; + +// One nanosecond in seconds. +static const double kOneNanosecond = 1.0e9; + +// The I/O interval time in seconds. +static const double kIOIntervalTime = 0.020; + +// The clock identifier that specifies interval timestamps. +static const os_clockid_t clockId = OS_CLOCK_MACH_ABSOLUTE_TIME; + @implementation Node - (id)initWithController:(id)c previous:(id)p { @@ -32,6 +46,21 @@ nodeChannelConfig = 0; nodeLossless = NO; + if(@available(macOS 11, *)) { + workgroup = AudioWorkIntervalCreate("Node Work Interval", clockId, attr); + + // Get the mach time info. + struct mach_timebase_info timeBaseInfo; + mach_timebase_info(&timeBaseInfo); + + // The frequency of the clock is: (timeBaseInfo.denom / timeBaseInfo.numer) * kOneNanosecond + const double nanoSecFrequency = (double)(timeBaseInfo.denom) / (double)(timeBaseInfo.numer); + const double frequency = nanoSecFrequency * kOneNanosecond; + + // Convert the interval time in seconds to mach time length. + intervalMachLength = (int64_t)(kIOIntervalTime * frequency); + } + [self setPreviousNode:p]; } @@ -101,11 +130,8 @@ - (void)followWorkgroup { if(@available(macOS 11, *)) { - if(currentWorkgroup != wg) { - if(wg) { - os_workgroup_leave(wg, &wgToken); - } - wg = currentWorkgroup; + if(!wg) { + wg = workgroup; if(wg) { int result = os_workgroup_join(wg, &wgToken); if(result == 0) return; @@ -128,6 +154,30 @@ } } +- (void)startWorkslice { + if(@available(macOS 11, *)) { + if(wg) { + const uint64_t currentTime = mach_absolute_time(); + const uint64_t deadline = currentTime + intervalMachLength; + int result = os_workgroup_interval_start(wg, currentTime, deadline, nil); + if(result != 0) { + DLog(@"Deadline error = %d", result); + } + } + } +} + +- (void)endWorkslice { + if(@available(macOS 11, *)) { + if(wg) { + int result = os_workgroup_interval_finish(wg, nil); + if(result != 0) { + DLog(@"Deadline end error = %d", result); + } + } + } +} + - (BOOL)peekFormat:(nonnull AudioStreamBasicDescription *)format channelConfig:(nonnull uint32_t *)config { [accessLock lock]; diff --git a/Audio/Output/OutputCoreAudio.h b/Audio/Output/OutputCoreAudio.h index a2d45838a..5aef23443 100644 --- a/Audio/Output/OutputCoreAudio.h +++ b/Audio/Output/OutputCoreAudio.h @@ -33,10 +33,6 @@ using std::atomic_long; #import #endif -#import - -extern volatile os_workgroup_t currentWorkgroup; - @class OutputNode; @interface OutputCoreAudio : NSObject { diff --git a/Audio/Output/OutputCoreAudio.m b/Audio/Output/OutputCoreAudio.m index 4a828911f..909e38957 100644 --- a/Audio/Output/OutputCoreAudio.m +++ b/Audio/Output/OutputCoreAudio.m @@ -17,8 +17,6 @@ #import -volatile os_workgroup_t currentWorkgroup; - extern void scale_by_volume(float *buffer, size_t count, float volume); static NSString *CogPlaybackDidBeginNotficiation = @"CogPlaybackDidBeginNotficiation"; @@ -263,21 +261,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const NSMutableArray *delayedEvents = [[NSMutableArray alloc] init]; BOOL delayedEventsPopped = YES; - if(@available(macOS 11, *)) { - if(currentWorkgroup) { - wg = currentWorkgroup; - int result = os_workgroup_join(wg, &wgToken); - if(result != 0) { - if(result == EALREADY) { - DLog(@"Output thread already in workgroup"); - } else { - DLog(@"Output thread could not be added to workgroup, error = %d", result); - } - wg = nil; - } - } - } - while(!stopping) { if(++eventCount == 48) { [self resetIfOutputChanged]; @@ -363,13 +346,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const [writeSemaphore timedWait:5000]; } - if(@available(macOS 11, *)) { - if(wg) { - os_workgroup_leave(wg, &wgToken); - wg = nil; - } - } - stopped = YES; if(!stopInvoked) [self stop]; @@ -806,10 +782,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const visController = [VisualizationController sharedController]; - if(@available(macOS 11, *)) { - currentWorkgroup = _au.osWorkgroup; - } - [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputDevice" options:0 context:NULL]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.GraphicEQenable" options:0 context:NULL]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.eqPreamp" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:NULL]; @@ -839,10 +811,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const } - (void)stop { - if(@available(macOS 11, *)) { - currentWorkgroup = nil; - } - stopInvoked = YES; if(observersapplied) { observersapplied = NO;