[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 <kode54@gmail.com>
This commit is contained in:
parent
fef8821cf6
commit
91898e9e77
6 changed files with 63 additions and 42 deletions
|
@ -430,9 +430,11 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
||||||
while(paused) {
|
while(paused) {
|
||||||
usleep(500);
|
usleep(500);
|
||||||
}
|
}
|
||||||
|
[self startWorkslice];
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
amountConverted = [self convert:writeBuf amount:CHUNK_SIZE];
|
amountConverted = [self convert:writeBuf amount:CHUNK_SIZE];
|
||||||
}
|
}
|
||||||
|
[self endWorkslice];
|
||||||
if(!amountConverted) {
|
if(!amountConverted) {
|
||||||
if(paused) {
|
if(paused) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -178,9 +178,11 @@
|
||||||
|
|
||||||
int framesToRead = CHUNK_SIZE - amountInBuffer;
|
int framesToRead = CHUNK_SIZE - amountInBuffer;
|
||||||
int framesRead;
|
int framesRead;
|
||||||
|
[self startWorkslice];
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
framesRead = [decoder readAudio:((char *)inputBuffer) + bytesInBuffer frames:framesToRead];
|
framesRead = [decoder readAudio:((char *)inputBuffer) + bytesInBuffer frames:framesToRead];
|
||||||
}
|
}
|
||||||
|
[self endWorkslice];
|
||||||
|
|
||||||
if(framesRead > 0 && !seekError) {
|
if(framesRead > 0 && !seekError) {
|
||||||
amountInBuffer += framesRead;
|
amountInBuffer += framesRead;
|
||||||
|
|
|
@ -34,7 +34,8 @@
|
||||||
uint32_t nodeChannelConfig;
|
uint32_t nodeChannelConfig;
|
||||||
BOOL nodeLossless;
|
BOOL nodeLossless;
|
||||||
|
|
||||||
os_workgroup_t wg;
|
int64_t intervalMachLength;
|
||||||
|
os_workgroup_interval_t workgroup, wg;
|
||||||
os_workgroup_join_token_s wgToken;
|
os_workgroup_join_token_s wgToken;
|
||||||
}
|
}
|
||||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p;
|
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p;
|
||||||
|
@ -49,6 +50,8 @@
|
||||||
|
|
||||||
- (void)followWorkgroup;
|
- (void)followWorkgroup;
|
||||||
- (void)leaveWorkgroup;
|
- (void)leaveWorkgroup;
|
||||||
|
- (void)startWorkslice;
|
||||||
|
- (void)endWorkslice;
|
||||||
|
|
||||||
- (void)launchThread;
|
- (void)launchThread;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,20 @@
|
||||||
|
|
||||||
#import "OutputCoreAudio.h"
|
#import "OutputCoreAudio.h"
|
||||||
|
|
||||||
|
#import <mach/mach_time.h>
|
||||||
|
|
||||||
|
// 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
|
@implementation Node
|
||||||
|
|
||||||
- (id)initWithController:(id)c previous:(id)p {
|
- (id)initWithController:(id)c previous:(id)p {
|
||||||
|
@ -32,6 +46,21 @@
|
||||||
nodeChannelConfig = 0;
|
nodeChannelConfig = 0;
|
||||||
nodeLossless = NO;
|
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];
|
[self setPreviousNode:p];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,11 +130,8 @@
|
||||||
|
|
||||||
- (void)followWorkgroup {
|
- (void)followWorkgroup {
|
||||||
if(@available(macOS 11, *)) {
|
if(@available(macOS 11, *)) {
|
||||||
if(currentWorkgroup != wg) {
|
if(!wg) {
|
||||||
if(wg) {
|
wg = workgroup;
|
||||||
os_workgroup_leave(wg, &wgToken);
|
|
||||||
}
|
|
||||||
wg = currentWorkgroup;
|
|
||||||
if(wg) {
|
if(wg) {
|
||||||
int result = os_workgroup_join(wg, &wgToken);
|
int result = os_workgroup_join(wg, &wgToken);
|
||||||
if(result == 0) return;
|
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 {
|
- (BOOL)peekFormat:(nonnull AudioStreamBasicDescription *)format channelConfig:(nonnull uint32_t *)config {
|
||||||
[accessLock lock];
|
[accessLock lock];
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,6 @@ using std::atomic_long;
|
||||||
#import <stdio.h>
|
#import <stdio.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import <os/workgroup.h>
|
|
||||||
|
|
||||||
extern volatile os_workgroup_t currentWorkgroup;
|
|
||||||
|
|
||||||
@class OutputNode;
|
@class OutputNode;
|
||||||
|
|
||||||
@interface OutputCoreAudio : NSObject {
|
@interface OutputCoreAudio : NSObject {
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
#import <Accelerate/Accelerate.h>
|
#import <Accelerate/Accelerate.h>
|
||||||
|
|
||||||
volatile os_workgroup_t currentWorkgroup;
|
|
||||||
|
|
||||||
extern void scale_by_volume(float *buffer, size_t count, float volume);
|
extern void scale_by_volume(float *buffer, size_t count, float volume);
|
||||||
|
|
||||||
static NSString *CogPlaybackDidBeginNotficiation = @"CogPlaybackDidBeginNotficiation";
|
static NSString *CogPlaybackDidBeginNotficiation = @"CogPlaybackDidBeginNotficiation";
|
||||||
|
@ -263,21 +261,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
||||||
NSMutableArray *delayedEvents = [[NSMutableArray alloc] init];
|
NSMutableArray *delayedEvents = [[NSMutableArray alloc] init];
|
||||||
BOOL delayedEventsPopped = YES;
|
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) {
|
while(!stopping) {
|
||||||
if(++eventCount == 48) {
|
if(++eventCount == 48) {
|
||||||
[self resetIfOutputChanged];
|
[self resetIfOutputChanged];
|
||||||
|
@ -363,13 +346,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
||||||
[writeSemaphore timedWait:5000];
|
[writeSemaphore timedWait:5000];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(@available(macOS 11, *)) {
|
|
||||||
if(wg) {
|
|
||||||
os_workgroup_leave(wg, &wgToken);
|
|
||||||
wg = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stopped = YES;
|
stopped = YES;
|
||||||
if(!stopInvoked)
|
if(!stopInvoked)
|
||||||
[self stop];
|
[self stop];
|
||||||
|
@ -806,10 +782,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
||||||
|
|
||||||
visController = [VisualizationController sharedController];
|
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.outputDevice" options:0 context:NULL];
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.GraphicEQenable" 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];
|
[[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 {
|
- (void)stop {
|
||||||
if(@available(macOS 11, *)) {
|
|
||||||
currentWorkgroup = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
stopInvoked = YES;
|
stopInvoked = YES;
|
||||||
if(observersapplied) {
|
if(observersapplied) {
|
||||||
observersapplied = NO;
|
observersapplied = NO;
|
||||||
|
|
Loading…
Reference in a new issue