Cog Audio: Further overhaul output buffering and track queue code

This commit is contained in:
Christopher Snowhill 2022-01-15 02:09:26 -08:00
parent 1a7e7a4b70
commit fbef034903
8 changed files with 93 additions and 35 deletions

View file

@ -95,9 +95,10 @@
//- (BufferChain *)bufferChain; //- (BufferChain *)bufferChain;
- (void)launchOutputThread; - (void)launchOutputThread;
- (void)endOfInputPlayed; - (void)endOfInputPlayed;
- (void)endOfInputPlayedOut;
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj waitUntilDone:(BOOL)wait; - (void)sendDelegateMethod:(SEL)selector withObject:(id)obj waitUntilDone:(BOOL)wait;
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj withObject:(id)obj2 waitUntilDone:(BOOL)wait; - (void)sendDelegateMethod:(SEL)selector withObject:(id)obj withObject:(id)obj2 waitUntilDone:(BOOL)wait;
- (BOOL)chainQueueHasTracks;
@end @end
@protocol AudioPlayerDelegate @protocol AudioPlayerDelegate

View file

@ -261,7 +261,7 @@
- (void)notifyStreamChanged:(id)userInfo - (void)notifyStreamChanged:(id)userInfo
{ {
[self sendDelegateMethod:@selector(audioPlayer:didBeginStream:) withObject:userInfo waitUntilDone:NO]; [self sendDelegateMethod:@selector(audioPlayer:didBeginStream:) withObject:userInfo waitUntilDone:YES];
} }
- (void)addChainToQueue:(BufferChain *)newChain - (void)addChainToQueue:(BufferChain *)newChain
@ -446,12 +446,16 @@
[semaphore signal]; [semaphore signal];
} }
[self notifyStreamChanged:[bufferChain userInfo]];
[output setEndOfStream:NO]; [output setEndOfStream:NO];
} }
- (void)endOfInputPlayedOut - (BOOL)chainQueueHasTracks
{ {
[self notifyStreamChanged:[bufferChain userInfo]]; @synchronized (chainQueue) {
return [chainQueue count] > 0;
}
return NO;
} }
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj waitUntilDone:(BOOL)wait - (void)sendDelegateMethod:(SEL)selector withObject:(id)obj waitUntilDone:(BOOL)wait

View file

@ -8,6 +8,9 @@
#import "ConverterNode.h" #import "ConverterNode.h"
#import "BufferChain.h"
#import "OutputNode.h"
#import "Logging.h" #import "Logging.h"
#import <audio/conversion/s16_to_float.h> #import <audio/conversion/s16_to_float.h>
@ -1243,6 +1246,21 @@ static float db_to_scale(float db)
break; break;
} }
for (;;)
{
void * ptr;
BufferChain * bufferChain = controller;
AudioPlayer * audioPlayer = [bufferChain controller];
VirtualRingBuffer * buffer = [[audioPlayer output] buffer];
dataRead = [buffer lengthAvailableToReadReturningPointer:&ptr];
if (dataRead) {
[refillNode writeData:(float*)ptr floatCount:dataRead / sizeof(float)];
[buffer didReadLength:dataRead];
}
else
break;
}
[self setupWithInputFormat:previousOutputFormat outputFormat:outputFormat]; [self setupWithInputFormat:previousOutputFormat outputFormat:outputFormat];
} }
else else

View file

@ -32,7 +32,8 @@
- (void)resetAmountPlayed; - (void)resetAmountPlayed;
- (void)endOfInputPlayed; - (void)endOfInputPlayed;
- (void)endOfInputPlayedOut;
- (BOOL)chainQueueHasTracks;
- (double)secondsBuffered; - (double)secondsBuffered;

View file

@ -69,9 +69,9 @@
[controller endOfInputPlayed]; [controller endOfInputPlayed];
} }
- (void)endOfInputPlayedOut - (BOOL)chainQueueHasTracks
{ {
[controller endOfInputPlayedOut]; return [controller chainQueueHasTracks];
} }
- (double)secondsBuffered - (double)secondsBuffered
@ -96,7 +96,6 @@
} }
} }
- (double)amountPlayed - (double)amountPlayed
{ {
return amountPlayed; return amountPlayed;

View file

@ -13,13 +13,29 @@
@implementation RefillNode @implementation RefillNode
- (id)initWithController:(id)c previous:(id)p { - (id)initWithController:(id)c previous:(id)p
self = [super initWithController:c previous:p]; {
self = [super init];
if (self)
{
// This special node should be able to handle up to four buffers
buffer = [[VirtualRingBuffer alloc] initWithLength:BUFFER_SIZE * 4];
semaphore = [[Semaphore alloc] init];
readLock = [[NSLock alloc] init];
writeLock = [[NSLock alloc] init];
initialBufferFilled = NO;
controller = c;
endOfStream = NO;
shouldContinue = YES;
[self setPreviousNode:p];
}
return self; return self;
} }
- (void)writeData:(float *)data floatCount:(size_t)count - (void)writeData:(float *)data floatCount:(size_t)count
{ {
[self writeData:data amount:(int)(count * sizeof(float))]; [self writeData:data amount:(int)(count * sizeof(float))];

View file

@ -14,6 +14,8 @@
#import <AudioUnit/AudioUnit.h> #import <AudioUnit/AudioUnit.h>
#import <AVFoundation/AVFoundation.h> #import <AVFoundation/AVFoundation.h>
#import <stdatomic.h>
#import "Semaphore.h" #import "Semaphore.h"
@class OutputNode; @class OutputNode;
@ -30,6 +32,8 @@
BOOL started; BOOL started;
BOOL paused; BOOL paused;
atomic_long bytesRendered;
BOOL listenerapplied; BOOL listenerapplied;
float volume; float volume;

View file

@ -29,6 +29,8 @@ extern void scale_by_volume(float * buffer, size_t count, float volume);
running = NO; running = NO;
started = NO; started = NO;
atomic_init(&bytesRendered, 0);
writeSemaphore = [[Semaphore alloc] init]; writeSemaphore = [[Semaphore alloc] init];
readSemaphore = [[Semaphore alloc] init]; readSemaphore = [[Semaphore alloc] init];
@ -56,14 +58,9 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
} }
- (void)signalEndOfStream - (void)signalEndOfStream
{
[outputController endOfInputPlayed];
}
- (void)signalEndOfStreamBuffered
{ {
[outputController resetAmountPlayed]; [outputController resetAmountPlayed];
[outputController endOfInputPlayedOut]; [outputController endOfInputPlayed];
} }
- (void)threadEntry:(id)arg - (void)threadEntry:(id)arg
@ -71,7 +68,9 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
running = YES; running = YES;
started = NO; started = NO;
size_t eventCount = 0; size_t eventCount = 0;
ssize_t delayedEvent = -1; atomic_store(&bytesRendered, 0);
NSMutableArray *delayedEvents = [[NSMutableArray alloc] init];
BOOL delayedEventsPopped = YES;
while (!stopping) { while (!stopping) {
if (++eventCount == 128) { if (++eventCount == 128) {
[self updateDeviceFormat]; [self updateDeviceFormat];
@ -83,6 +82,20 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
[outputController setShouldReset:NO]; [outputController setShouldReset:NO];
} }
while ([delayedEvents count]) {
size_t localBytesRendered = atomic_load_explicit(&bytesRendered, memory_order_relaxed);
if (localBytesRendered >= [[delayedEvents objectAtIndex:0] longValue]) {
if ([outputController chainQueueHasTracks])
delayedEventsPopped = YES;
[self signalEndOfStream];
[delayedEvents removeObjectAtIndex:0];
}
else break;
}
if (stopping)
break;
void *writePtr; void *writePtr;
int toWrite = [[outputController buffer] lengthAvailableToWriteReturningPointer:&writePtr]; int toWrite = [[outputController buffer] lengthAvailableToWriteReturningPointer:&writePtr];
int bytesRead = 0; int bytesRead = 0;
@ -93,15 +106,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
if (bytesRead) { if (bytesRead) {
[[outputController buffer] didWriteLength:bytesRead]; [[outputController buffer] didWriteLength:bytesRead];
[readSemaphore signal]; [readSemaphore signal];
if (delayedEvent >= 0) {
if (bytesRead >= delayedEvent) {
delayedEvent = -1;
[self signalEndOfStreamBuffered];
}
else {
delayedEvent -= bytesRead;
}
}
continue; continue;
} }
else if ([outputController shouldContinue] == NO) else if ([outputController shouldContinue] == NO)
@ -117,15 +121,19 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
} }
else { else {
// End of input possibly reached // End of input possibly reached
if ([outputController endOfStream] == YES) if (delayedEventsPopped && [outputController endOfStream] == YES)
{ {
[self signalEndOfStream]; long bytesBuffered = [[outputController buffer] bufferedLength];
if (delayedEvent >= 0) { bytesBuffered += atomic_load_explicit(&bytesRendered, memory_order_relaxed);
[self signalEndOfStreamBuffered]; [delayedEvents addObject:[NSNumber numberWithLong:bytesBuffered]];
delayedEvent = -1; delayedEventsPopped = NO;
if (!started) {
started = YES;
if (!paused) {
NSError *err;
[_au startHardwareAndReturnError:&err];
}
} }
else
delayedEvent = [[outputController buffer] bufferedLength];
} }
} }
[readSemaphore signal]; [readSemaphore signal];
@ -411,6 +419,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
__block Semaphore * readSemaphore = self->readSemaphore; __block Semaphore * readSemaphore = self->readSemaphore;
__block OutputNode * outputController = self->outputController; __block OutputNode * outputController = self->outputController;
__block float * volume = &self->volume; __block float * volume = &self->volume;
__block atomic_long * bytesRendered = &self->bytesRendered;
_au.outputProvider = ^AUAudioUnitStatus(AudioUnitRenderActionFlags * actionFlags, const AudioTimeStamp * timestamp, AUAudioFrameCount frameCount, NSInteger inputBusNumber, AudioBufferList * inputData) _au.outputProvider = ^AUAudioUnitStatus(AudioUnitRenderActionFlags * actionFlags, const AudioTimeStamp * timestamp, AUAudioFrameCount frameCount, NSInteger inputBusNumber, AudioBufferList * inputData)
{ {
@ -420,9 +429,12 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
amountToRead = inputData->mBuffers[0].mDataByteSize; amountToRead = inputData->mBuffers[0].mDataByteSize;
if (self->stopping == YES || [outputController shouldContinue] == NO) if (self->stopping == YES || [outputController shouldContinue] == NO ||
([[outputController buffer] isEmpty] && ![outputController chainQueueHasTracks]))
{ {
// Chain is dead, fill out the serial number pointer forever with silence
memset(readPointer, 0, amountToRead); memset(readPointer, 0, amountToRead);
atomic_fetch_add(bytesRendered, amountToRead);
self->stopping = YES; self->stopping = YES;
return 0; return 0;
} }
@ -438,6 +450,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
amountRead = toRead; amountRead = toRead;
[[outputController buffer] didReadLength:toRead]; [[outputController buffer] didReadLength:toRead];
[outputController incrementAmountPlayed:amountRead]; [outputController incrementAmountPlayed:amountRead];
atomic_fetch_add(bytesRendered, amountRead);
[writeSemaphore signal]; [writeSemaphore signal];
} }
@ -450,6 +463,8 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
if (amountRead2 > (amountToRead - amountRead)) if (amountRead2 > (amountToRead - amountRead))
amountRead2 = amountToRead - amountRead; amountRead2 = amountToRead - amountRead;
if (amountRead2) { if (amountRead2) {
atomic_fetch_add(bytesRendered, amountRead2);
memcpy(readPointer + amountRead, readPtr, amountRead2); memcpy(readPointer + amountRead, readPtr, amountRead2);
[[outputController buffer] didReadLength:amountRead2]; [[outputController buffer] didReadLength:amountRead2];