[Audio Threads] Join output device workgroup

On Big Sur or newer, it is possible to join the audio threads to the
same OS workgroup as the audio output device, improving response.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
swiftingly
Christopher Snowhill 2022-06-09 00:27:55 -07:00
parent cdcbabd72f
commit 6179b304d0
6 changed files with 88 additions and 0 deletions

View File

@ -424,6 +424,8 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
// when the end of stream is reached. Convert function instead processes what it can,
// and returns 0 samples when it has nothing more to process at the end of stream.
while([self shouldContinue] == YES) {
[self followWorkgroup];
int amountConverted;
while(paused) {
usleep(500);
@ -435,6 +437,7 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
if(paused) {
continue;
} else if(streamFormatChanged) {
[self leaveWorkgroup];
[self cleanUp];
[self setupWithInputFormat:newInputFormat withInputConfig:newInputChannelConfig outputFormat:outputFormat outputConfig:outputChannelConfig isLossless:rememberedLossless];
continue;

View File

@ -148,6 +148,7 @@
while([self shouldContinue] == YES && [self endOfStream] == NO) {
if(shouldSeek == YES) {
[self leaveWorkgroup];
BufferChain *bufferChain = [[controller controller] bufferChain];
ConverterNode *converter = [bufferChain converter];
DLog(@"SEEKING! Resetting Buffer");
@ -173,6 +174,8 @@
}
if(amountInBuffer < CHUNK_SIZE) {
[self followWorkgroup];
int framesToRead = CHUNK_SIZE - amountInBuffer;
int framesRead;
@autoreleasepool {

View File

@ -10,6 +10,8 @@
#import "Semaphore.h"
#import <Cocoa/Cocoa.h>
#import <os/workgroup.h>
#define BUFFER_SIZE 1024 * 1024
#define CHUNK_SIZE 16 * 1024
@ -31,6 +33,9 @@
AudioStreamBasicDescription nodeFormat;
uint32_t nodeChannelConfig;
BOOL nodeLossless;
os_workgroup_t wg;
os_workgroup_join_token_s wgToken;
}
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p;
@ -42,6 +47,9 @@
- (void)process; // Should be overwriten by subclass
- (void)threadEntry:(id _Nullable)arg;
- (void)followWorkgroup;
- (void)leaveWorkgroup;
- (void)launchThread;
- (void)setShouldReset:(BOOL)s;

View File

@ -11,6 +11,8 @@
#import "BufferChain.h"
#import "Logging.h"
#import "OutputCoreAudio.h"
@implementation Node
- (id)initWithController:(id)c previous:(id)p {
@ -91,7 +93,38 @@
- (void)threadEntry:(id)arg {
@autoreleasepool {
[self followWorkgroup];
[self process];
[self leaveWorkgroup];
}
}
- (void)followWorkgroup {
if(@available(macOS 11, *)) {
if(currentWorkgroup != wg) {
if(wg) {
os_workgroup_leave(wg, &wgToken);
}
wg = currentWorkgroup;
if(wg) {
int result = os_workgroup_join(wg, &wgToken);
if(result == 0) return;
if(result == EALREADY) {
DLog(@"Thread already in workgroup");
} else {
DLog(@"Cannot join workgroup, error %d", result);
}
}
}
}
}
- (void)leaveWorkgroup {
if(@available(macOS 11, *)) {
if(wg) {
os_workgroup_leave(wg, &wgToken);
wg = nil;
}
}
}

View File

@ -33,6 +33,10 @@ using std::atomic_long;
#import <stdio.h>
#endif
#import <os/workgroup.h>
extern volatile os_workgroup_t currentWorkgroup;
@class OutputNode;
@interface OutputCoreAudio : NSObject {
@ -84,6 +88,9 @@ using std::atomic_long;
VisualizationController *visController;
os_workgroup_t wg;
os_workgroup_join_token_s wgToken;
#ifdef OUTPUT_LOG
FILE *_logFile;
#endif

View File

@ -17,6 +17,8 @@
#import <Accelerate/Accelerate.h>
volatile os_workgroup_t currentWorkgroup;
extern void scale_by_volume(float *buffer, size_t count, float volume);
static NSString *CogPlaybackDidBeginNotficiation = @"CogPlaybackDidBeginNotficiation";
@ -260,6 +262,22 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
atomic_store(&bytesRendered, 0);
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];
@ -344,6 +362,14 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
[readSemaphore signal];
[writeSemaphore timedWait:5000];
}
if(@available(macOS 11, *)) {
if(wg) {
os_workgroup_leave(wg, &wgToken);
wg = nil;
}
}
stopped = YES;
if(!stopInvoked)
[self stop];
@ -780,6 +806,10 @@ 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];
@ -809,6 +839,10 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
}
- (void)stop {
if(@available(macOS 11, *)) {
currentWorkgroup = nil;
}
stopInvoked = YES;
if(observersapplied) {
observersapplied = NO;