Cog Audio: Further overhaul output buffering and track queue code
parent
1a7e7a4b70
commit
fbef034903
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
- (void)resetAmountPlayed;
|
||||
|
||||
- (void)endOfInputPlayed;
|
||||
- (void)endOfInputPlayedOut;
|
||||
|
||||
- (BOOL)chainQueueHasTracks;
|
||||
|
||||
- (double)secondsBuffered;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
Loading…
Reference in New Issue