Skip to content

Commit

Permalink
Revert to previous low latency output system
Browse files Browse the repository at this point in the history
This reverts usage of the AVFoundation output to use
the previous lower latency CoreAudio output, and
paves the way for a change I am cooking up soon.

Fixes several issues with playback and seeking latency.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
  • Loading branch information
kode54 committed Oct 2, 2023
1 parent ca1f938 commit 7ac3228
Show file tree
Hide file tree
Showing 16 changed files with 1,180 additions and 222 deletions.
6 changes: 3 additions & 3 deletions Audio/AudioPlayer.m
Expand Up @@ -91,7 +91,7 @@ - (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)r
bufferChain = [[BufferChain alloc] initWithController:self];
[self notifyStreamChanged:userInfo];

while(![bufferChain open:url withUserInfo:userInfo withRGInfo:rgi]) {
while(![bufferChain open:url withOutputFormat:[output format] withUserInfo:userInfo withRGInfo:rgi]) {
bufferChain = nil;

[self requestNextStream:userInfo];
Expand Down Expand Up @@ -401,7 +401,7 @@ - (BOOL)endOfInputReached:(BufferChain *)sender // Sender is a BufferChain
}

if(pathsEqual || ([[nextStream scheme] isEqualToString:[[lastChain streamURL] scheme]] && (([nextStream host] == nil && [[lastChain streamURL] host] == nil) || [[nextStream host] isEqualToString:[[lastChain streamURL] host]]) && [[nextStream path] isEqualToString:[[lastChain streamURL] path]])) {
if([lastChain setTrack:nextStream] && [newChain openWithInput:[lastChain inputNode] withUserInfo:nextStreamUserInfo withRGInfo:nextStreamRGInfo]) {
if([lastChain setTrack:nextStream] && [newChain openWithInput:[lastChain inputNode] withOutputFormat:[output format] withUserInfo:nextStreamUserInfo withRGInfo:nextStreamRGInfo]) {
[newChain setStreamURL:nextStream];

@synchronized(chainQueue) {
Expand All @@ -420,7 +420,7 @@ - (BOOL)endOfInputReached:(BufferChain *)sender // Sender is a BufferChain

NSURL *url = nextStream;

while(shouldContinue && ![newChain open:url withUserInfo:nextStreamUserInfo withRGInfo:nextStreamRGInfo]) {
while(shouldContinue && ![newChain open:url withOutputFormat:[output format] withUserInfo:nextStreamUserInfo withRGInfo:nextStreamRGInfo]) {
if(nextStream == nil) {
newChain = nil;
if(output)
Expand Down
5 changes: 3 additions & 2 deletions Audio/Chain/BufferChain.h
Expand Up @@ -28,13 +28,14 @@
- (id)initWithController:(id)c;
- (void)buildChain;

- (BOOL)open:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;

// Used when changing tracks to reuse the same decoder
- (BOOL)openWithInput:(InputNode *)i withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;

// Used when resetting the decoder on seek
- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
withOutputFormat:(AudioStreamBasicDescription)outputFormat
withUserInfo:(id)userInfo
withRGInfo:(NSDictionary *)rgi;

Expand Down
23 changes: 18 additions & 5 deletions Audio/Chain/BufferChain.m
Expand Up @@ -40,7 +40,7 @@ - (void)buildChain {
finalNode = converterNode;
}

- (BOOL)open:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
[self setStreamURL:url];
[self setUserInfo:userInfo];

Expand All @@ -66,7 +66,11 @@ - (BOOL)open:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)r
if([properties valueForKey:@"channelConfig"])
inputChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue];

if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]])
outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame;
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;

if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]])
return NO;

[self setRGInfo:rgi];
Expand All @@ -76,7 +80,7 @@ - (BOOL)open:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)r
return YES;
}

- (BOOL)openWithInput:(InputNode *)i withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
DLog(@"New buffer chain!");
[self setUserInfo:userInfo];
[self buildChain];
Expand All @@ -91,8 +95,12 @@ - (BOOL)openWithInput:(InputNode *)i withUserInfo:(id)userInfo withRGInfo:(NSDic
if([properties valueForKey:@"channelConfig"])
inputChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue];

outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame;
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;

DLog(@"Input Properties: %@", properties);
if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
return NO;

[self setRGInfo:rgi];
Expand All @@ -101,6 +109,7 @@ - (BOOL)openWithInput:(InputNode *)i withUserInfo:(id)userInfo withRGInfo:(NSDic
}

- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
withOutputFormat:(AudioStreamBasicDescription)outputFormat
withUserInfo:(id)userInfo
withRGInfo:(NSDictionary *)rgi;
{
Expand All @@ -120,7 +129,11 @@ - (BOOL)openWithDecoder:(id<CogDecoder>)decoder
if([properties valueForKey:@"channelConfig"])
inputChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue];

if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame;
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;

if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
return NO;

[self setRGInfo:rgi];
Expand Down
29 changes: 28 additions & 1 deletion Audio/Chain/ConverterNode.h
Expand Up @@ -12,11 +12,15 @@
#import <AudioUnit/AudioUnit.h>
#import <CoreAudio/AudioHardware.h>

#import <soxr.h>

#import "Node.h"

@interface ConverterNode : Node {
NSDictionary *rgInfo;

soxr_t soxr;

void *inputBuffer;
size_t inputBufferSize;
size_t inpSize, inpOffset;
Expand All @@ -25,12 +29,33 @@
BOOL convertEntered;
BOOL paused;

BOOL skipResampler;

unsigned int PRIME_LEN_;
unsigned int N_samples_to_add_;
unsigned int N_samples_to_drop_;

BOOL is_preextrapolated_;
int is_postextrapolated_;

int latencyEaten;
int latencyEatenPost;

double sampleRatio;

float volumeScale;

void *floatBuffer;
size_t floatBufferSize;

void *extrapolateBuffer;
size_t extrapolateBufferSize;

BOOL rememberedLossless;

AudioStreamBasicDescription inputFormat;
AudioStreamBasicDescription floatFormat;
AudioStreamBasicDescription outputFormat;

uint32_t inputChannelConfig;

Expand All @@ -43,14 +68,16 @@

- (id)initWithController:(id)c previous:(id)p;

- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inputFormat withInputConfig:(uint32_t)inputConfig isLossless:(BOOL)lossless;
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inputFormat withInputConfig:(uint32_t)inputConfig outputFormat:(AudioStreamBasicDescription)outputFormat isLossless:(BOOL)lossless;
- (void)cleanUp;

- (void)process;
- (AudioChunk *)convert;

- (void)setRGInfo:(NSDictionary *)rgi;

- (void)setOutputFormat:(AudioStreamBasicDescription)outputFormat;

- (void)inputFormatDidChange:(AudioStreamBasicDescription)format inputConfig:(uint32_t)inputConfig;

- (void)refreshVolumeScaling;
Expand Down

0 comments on commit 7ac3228

Please sign in to comment.