Cog/Audio/Chain/OutputNode.m
Christopher Snowhill dccb7f8b47 Replace Core Audio output with Core Media runtime
The output now uses AVSampleBufferAudioRenderer to play all formats, and
uses that to resample. It also supports Spatial Audio on macOS 12.0 or
newer. Note that there are some outstanding bugs with Spatial Audio
support. Namely that it appears to be limited to only 192 kHz at mono or
stereo, or 352800 Hz at surround configurations. This breaks DSD64
playback at stereo formats, as well as possibly other things. This is
entirely an Apple bug. I have reported it to Apple with reference code
FB10441301 for reference, in case anyone else wants to complain that it
isn't fixed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-23 23:23:13 -07:00

189 lines
3.6 KiB
Objective-C

//
// OutputNode.m
// Cog
//
// Created by Vincent Spader on 8/2/05.
// Copyright 2005 Vincent Spader. All rights reserved.
//
#import "OutputNode.h"
#import "AudioPlayer.h"
#import "BufferChain.h"
#import "OutputAVFoundation.h"
#import "Logging.h"
@implementation OutputNode
- (void)setup {
amountPlayed = 0.0;
amountPlayedInterval = 0.0;
paused = YES;
started = NO;
intervalReported = NO;
output = [[OutputAVFoundation alloc] initWithController:self];
[output setup];
}
- (void)seek:(double)time {
// [output pause];
[self resetBuffer];
amountPlayed = time;
}
- (void)process {
paused = NO;
[output start];
}
- (void)pause {
paused = YES;
[output pause];
}
- (void)resume {
paused = NO;
[output resume];
}
- (void)incrementAmountPlayed:(double)seconds {
amountPlayed += seconds;
amountPlayedInterval += seconds;
if(!intervalReported && amountPlayedInterval >= 60.0) {
intervalReported = YES;
[controller reportPlayCount];
}
}
- (void)resetAmountPlayed {
amountPlayed = 0;
}
- (void)resetAmountPlayedInterval {
amountPlayedInterval = 0;
intervalReported = NO;
}
- (BOOL)selectNextBuffer {
return [controller selectNextBuffer];
}
- (void)endOfInputPlayed {
if(!intervalReported) {
intervalReported = YES;
[controller reportPlayCount];
}
[controller endOfInputPlayed];
[self resetAmountPlayedInterval];
}
- (BOOL)chainQueueHasTracks {
return [controller chainQueueHasTracks];
}
- (double)secondsBuffered {
return [buffer listDuration];
}
- (AudioChunk *)readChunk:(size_t)amount {
@autoreleasepool {
[self setPreviousNode:[[controller bufferChain] finalNode]];
AudioChunk *ret = [super readChunk:amount];
/* if (n == 0) {
DLog(@"Output Buffer dry!");
}
*/
return ret;
}
}
- (double)amountPlayed {
return amountPlayed;
}
- (double)amountPlayedInterval {
return amountPlayedInterval;
}
- (AudioStreamBasicDescription)format {
return format;
}
- (uint32_t)config {
return config;
}
- (void)setFormat:(AudioStreamBasicDescription *)f channelConfig:(uint32_t)channelConfig {
format = *f;
config = channelConfig;
// Calculate a ratio and add to double(seconds) instead, as format may change
// double oldSampleRatio = sampleRatio;
BufferChain *bufferChain = [controller bufferChain];
if(bufferChain) {
ConverterNode *converter = [bufferChain converter];
if(converter) {
// This clears the resampler buffer, but not the input buffer
// We also have to jump the play position ahead accounting for
// the data we are flushing
amountPlayed += [[converter buffer] listDuration];
AudioStreamBasicDescription inf = [bufferChain inputFormat];
uint32_t config = [bufferChain inputConfig];
format.mChannelsPerFrame = inf.mChannelsPerFrame;
format.mBytesPerFrame = ((inf.mBitsPerChannel + 7) / 8) * format.mChannelsPerFrame;
format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket;
channelConfig = config;
[converter inputFormatDidChange:[bufferChain inputFormat] inputConfig:[bufferChain inputConfig]];
}
}
}
- (void)close {
[output stop];
output = nil;
}
- (void)setVolume:(double)v {
[output setVolume:v];
}
- (void)setShouldContinue:(BOOL)s {
[super setShouldContinue:s];
// if (s == NO)
// [output stop];
}
- (BOOL)isPaused {
return paused;
}
- (void)beginEqualizer:(AudioUnit)eq {
[controller beginEqualizer:eq];
}
- (void)refreshEqualizer:(AudioUnit)eq {
[controller refreshEqualizer:eq];
}
- (void)endEqualizer:(AudioUnit)eq {
[controller endEqualizer:eq];
}
- (void)sustainHDCD {
[output sustainHDCD];
}
- (void)restartPlaybackAtCurrentPosition {
[controller restartPlaybackAtCurrentPosition];
}
@end