Cog Audio: Further overhaul output buffering and track queue code

CQTexperiment
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;
- (void)launchOutputThread;
- (void)endOfInputPlayed;
- (void)endOfInputPlayedOut;
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj waitUntilDone:(BOOL)wait;
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj withObject:(id)obj2 waitUntilDone:(BOOL)wait;
- (BOOL)chainQueueHasTracks;
@end
@protocol AudioPlayerDelegate

View File

@ -261,7 +261,7 @@
- (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
@ -446,12 +446,16 @@
[semaphore signal];
}
[self notifyStreamChanged:[bufferChain userInfo]];
[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

View File

@ -8,6 +8,9 @@
#import "ConverterNode.h"
#import "BufferChain.h"
#import "OutputNode.h"
#import "Logging.h"
#import <audio/conversion/s16_to_float.h>
@ -1243,6 +1246,21 @@ static float db_to_scale(float db)
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];
}
else

View File

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

View File

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

View File

@ -13,13 +13,29 @@
@implementation RefillNode
- (id)initWithController:(id)c previous:(id)p {
self = [super initWithController:c previous:p];
- (id)initWithController:(id)c previous:(id)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;
}
- (void)writeData:(float *)data floatCount:(size_t)count
{
[self writeData:data amount:(int)(count * sizeof(float))];

View File

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

View File

@ -29,6 +29,8 @@ extern void scale_by_volume(float * buffer, size_t count, float volume);
running = NO;
started = NO;
atomic_init(&bytesRendered, 0);
writeSemaphore = [[Semaphore alloc] init];
readSemaphore = [[Semaphore alloc] init];
@ -56,14 +58,9 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
}
- (void)signalEndOfStream
{
[outputController endOfInputPlayed];
}
- (void)signalEndOfStreamBuffered
{
[outputController resetAmountPlayed];
[outputController endOfInputPlayedOut];
[outputController endOfInputPlayed];
}
- (void)threadEntry:(id)arg
@ -71,7 +68,9 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
running = YES;
started = NO;
size_t eventCount = 0;
ssize_t delayedEvent = -1;
atomic_store(&bytesRendered, 0);
NSMutableArray *delayedEvents = [[NSMutableArray alloc] init];
BOOL delayedEventsPopped = YES;
while (!stopping) {
if (++eventCount == 128) {
[self updateDeviceFormat];
@ -83,6 +82,20 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
[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;
int toWrite = [[outputController buffer] lengthAvailableToWriteReturningPointer:&writePtr];
int bytesRead = 0;
@ -93,15 +106,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
if (bytesRead) {
[[outputController buffer] didWriteLength:bytesRead];
[readSemaphore signal];
if (delayedEvent >= 0) {
if (bytesRead >= delayedEvent) {
delayedEvent = -1;
[self signalEndOfStreamBuffered];
}
else {
delayedEvent -= bytesRead;
}
}
continue;
}
else if ([outputController shouldContinue] == NO)
@ -117,15 +121,19 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
}
else {
// End of input possibly reached
if ([outputController endOfStream] == YES)
if (delayedEventsPopped && [outputController endOfStream] == YES)
{
[self signalEndOfStream];
if (delayedEvent >= 0) {
[self signalEndOfStreamBuffered];
delayedEvent = -1;
long bytesBuffered = [[outputController buffer] bufferedLength];
bytesBuffered += atomic_load_explicit(&bytesRendered, memory_order_relaxed);
[delayedEvents addObject:[NSNumber numberWithLong:bytesBuffered]];
delayedEventsPopped = NO;
if (!started) {
started = YES;
if (!paused) {
NSError *err;
[_au startHardwareAndReturnError:&err];
}
}
else
delayedEvent = [[outputController buffer] bufferedLength];
}
}
[readSemaphore signal];
@ -411,6 +419,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
__block Semaphore * readSemaphore = self->readSemaphore;
__block OutputNode * outputController = self->outputController;
__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)
{
@ -420,9 +429,12 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
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);
atomic_fetch_add(bytesRendered, amountToRead);
self->stopping = YES;
return 0;
}
@ -438,6 +450,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
amountRead = toRead;
[[outputController buffer] didReadLength:toRead];
[outputController incrementAmountPlayed:amountRead];
atomic_fetch_add(bytesRendered, amountRead);
[writeSemaphore signal];
}
@ -450,6 +463,8 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
if (amountRead2 > (amountToRead - amountRead))
amountRead2 = amountToRead - amountRead;
if (amountRead2) {
atomic_fetch_add(bytesRendered, amountRead2);
memcpy(readPointer + amountRead, readPtr, amountRead2);
[[outputController buffer] didReadLength:amountRead2];