Revert "Core Audio output: Rewrote major portions"

This reverts commit 637ea4efe1.
CQTexperiment
Christopher Snowhill 2022-02-05 04:14:03 -08:00
parent 4cdca2f5f8
commit 0131f7c925
17 changed files with 317 additions and 655 deletions

View File

@ -93,7 +93,7 @@
bufferChain = [[BufferChain alloc] initWithController:self];
[self notifyStreamChanged:userInfo];
while (![bufferChain open:url withOutputFormatHint:[output format] withRGInfo:rgi])
while (![bufferChain open:url withOutputFormat:[output format] withRGInfo:rgi])
{
bufferChain = nil;
@ -401,7 +401,7 @@
&& [[nextStream path] isEqualToString:[[lastChain streamURL] path]]))
{
if ([lastChain setTrack:nextStream]
&& [newChain openWithInput:[lastChain inputNode] withOutputFormatHint:[output format] withRGInfo:nextStreamRGInfo])
&& [newChain openWithInput:[lastChain inputNode] withOutputFormat:[output format] withRGInfo:nextStreamRGInfo])
{
[newChain setStreamURL:nextStream];
[newChain setUserInfo:nextStreamUserInfo];
@ -418,7 +418,7 @@
lastChain = nil;
while (shouldContinue && ![newChain open:nextStream withOutputFormatHint:[output format] withRGInfo:nextStreamRGInfo])
while (shouldContinue && ![newChain open:nextStream withOutputFormat:[output format] withRGInfo:nextStreamRGInfo])
{
if (nextStream == nil)
{

View File

@ -30,14 +30,14 @@
- (id)initWithController:(id)c;
- (void)buildChain;
- (BOOL)open:(NSURL *)url withOutputFormatHint:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi;
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi;
//Used when changing tracks to reuse the same decoder
- (BOOL)openWithInput:(InputNode *)i withOutputFormatHint:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi;
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi;
//Used when resetting the decoder on seek
- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
withOutputFormatHint:(AudioStreamBasicDescription)outputFormat
withOutputFormat:(AudioStreamBasicDescription)outputFormat
withRGInfo:(NSDictionary*)rgi;
- (void)seek:(double)time;

View File

@ -43,7 +43,7 @@
finalNode = converterNode;
}
- (BOOL)open:(NSURL *)url withOutputFormatHint:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi
{
[self setStreamURL:url];
@ -64,14 +64,8 @@
return NO;
NSDictionary * properties = [inputNode properties];
inputFormat = [inputNode nodeFormat];
outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame;
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
if (![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]])
if (![converterNode setupWithInputFormat:(inputFormat = propertiesToASBD(properties)) outputFormat:outputFormat isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]])
return NO;
[self setRGInfo:rgi];
@ -81,7 +75,7 @@
return YES;
}
- (BOOL)openWithInput:(InputNode *)i withOutputFormatHint:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi
{
DLog(@"New buffer chain!");
[self buildChain];
@ -92,14 +86,7 @@
NSDictionary * properties = [inputNode properties];
DLog(@"Input Properties: %@", properties);
inputFormat = [inputNode nodeFormat];
outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame;
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
if (![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
if (![converterNode setupWithInputFormat:(inputFormat = propertiesToASBD(properties)) outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
return NO;
[self setRGInfo:rgi];
@ -108,7 +95,7 @@
}
- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
withOutputFormatHint:(AudioStreamBasicDescription)outputFormat
withOutputFormat:(AudioStreamBasicDescription)outputFormat
withRGInfo:(NSDictionary*)rgi;
{
DLog(@"New buffer chain!");
@ -120,14 +107,7 @@
NSDictionary * properties = [inputNode properties];
DLog(@"Input Properties: %@", properties);
inputFormat = [inputNode nodeFormat];
outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame;
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
if (![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
if (![converterNode setupWithInputFormat:(inputFormat = propertiesToASBD(properties)) outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
return NO;
[self setRGInfo:rgi];

View File

@ -74,6 +74,8 @@
id __weak originalPreviousNode;
void *hdcd_decoder;
HeadphoneFilter *hFilter;
}
@property AudioStreamBasicDescription inputFormat;

View File

@ -72,11 +72,189 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
hdcd_decoder = NULL;
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.headphoneVirtualization" options:0 context:nil];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.hrirPath" options:0 context:nil];
}
return self;
}
static const float STEREO_DOWNMIX[8-2][8][2]={
/*3.0*/
{
{0.5858F,0.0F},{0.0F,0.5858F},{0.4142F,0.4142F}
},
/*quadrophonic*/
{
{0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
},
/*5.0*/
{
{0.651F,0.0F},{0.0F,0.651F},{0.46F,0.46F},{0.5636F,0.3254F},
{0.3254F,0.5636F}
},
/*5.1*/
{
{0.529F,0.0F},{0.0F,0.529F},{0.3741F,0.3741F},{0.3741F,0.3741F},{0.4582F,0.2645F},
{0.2645F,0.4582F}
},
/*6.1*/
{
{0.4553F,0.0F},{0.0F,0.4553F},{0.322F,0.322F},{0.322F,0.322F},{0.2788F,0.2788F},
{0.3943F,0.2277F},{0.2277F,0.3943F}
},
/*7.1*/
{
{0.3886F,0.0F},{0.0F,0.3886F},{0.2748F,0.2748F},{0.2748F,0.2748F},{0.3366F,0.1943F},
{0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F}
}
};
static void downmix_to_stereo(float * buffer, int channels, size_t count)
{
if (channels >= 3 && channels <= 8)
for (size_t i = 0; i < count; ++i)
{
float left = 0, right = 0;
for (int j = 0; j < channels; ++j)
{
left += buffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][0];
right += buffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][1];
}
buffer[i * 2 + 0] = left;
buffer[i * 2 + 1] = right;
}
}
static void downmix_to_mono(float * buffer, int channels, size_t count)
{
if (channels >= 3 && channels <= 8)
{
downmix_to_stereo(buffer, channels, count);
channels = 2;
}
float invchannels = 1.0 / (float)channels;
for (size_t i = 0; i < count; ++i)
{
float sample = 0;
for (int j = 0; j < channels; ++j)
{
sample += buffer[i * channels + j];
}
buffer[i] = sample * invchannels;
}
}
static void upmix(float * buffer, int inchannels, int outchannels, size_t count)
{
for (ssize_t i = count - 1; i >= 0; --i)
{
if (inchannels == 1 && outchannels == 2)
{
// upmix mono to stereo
float sample = buffer[i];
buffer[i * 2 + 0] = sample;
buffer[i * 2 + 1] = sample;
}
else if (inchannels == 1 && outchannels == 4)
{
// upmix mono to quad
float sample = buffer[i];
buffer[i * 4 + 0] = sample;
buffer[i * 4 + 1] = sample;
buffer[i * 4 + 2] = 0;
buffer[i * 4 + 3] = 0;
}
else if (inchannels == 1 && (outchannels == 3 || outchannels >= 5))
{
// upmix mono to center channel
float sample = buffer[i];
buffer[i * outchannels + 2] = sample;
for (int j = 0; j < 2; ++j)
{
buffer[i * outchannels + j] = 0;
}
for (int j = 3; j < outchannels; ++j)
{
buffer[i * outchannels + j] = 0;
}
}
else if (inchannels == 4 && outchannels >= 5)
{
float fl = buffer[i * 4 + 0];
float fr = buffer[i * 4 + 1];
float bl = buffer[i * 4 + 2];
float br = buffer[i * 4 + 3];
const int skipclfe = (outchannels == 5) ? 1 : 2;
buffer[i * outchannels + 0] = fl;
buffer[i * outchannels + 1] = fr;
buffer[i * outchannels + skipclfe + 2] = bl;
buffer[i * outchannels + skipclfe + 3] = br;
for (int j = 0; j < skipclfe; ++j)
{
buffer[i * outchannels + 2 + j] = 0;
}
for (int j = 4 + skipclfe; j < outchannels; ++j)
{
buffer[i * outchannels + j] = 0;
}
}
else if (inchannels == 5 && outchannels >= 6)
{
float fl = buffer[i * 5 + 0];
float fr = buffer[i * 5 + 1];
float c = buffer[i * 5 + 2];
float bl = buffer[i * 5 + 3];
float br = buffer[i * 5 + 4];
buffer[i * outchannels + 0] = fl;
buffer[i * outchannels + 1] = fr;
buffer[i * outchannels + 2] = c;
buffer[i * outchannels + 3] = 0;
buffer[i * outchannels + 4] = bl;
buffer[i * outchannels + 5] = br;
for (int j = 6; j < outchannels; ++j)
{
buffer[i * outchannels + j] = 0;
}
}
else if (inchannels == 7 && outchannels == 8)
{
float fl = buffer[i * 7 + 0];
float fr = buffer[i * 7 + 1];
float c = buffer[i * 7 + 2];
float lfe = buffer[i * 7 + 3];
float sl = buffer[i * 7 + 4];
float sr = buffer[i * 7 + 5];
float bc = buffer[i * 7 + 6];
buffer[i * 8 + 0] = fl;
buffer[i * 8 + 1] = fr;
buffer[i * 8 + 2] = c;
buffer[i * 8 + 3] = lfe;
buffer[i * 8 + 4] = bc;
buffer[i * 8 + 5] = bc;
buffer[i * 8 + 6] = sl;
buffer[i * 8 + 7] = sr;
}
else
{
// upmix N channels to N channels plus silence the empty channels
float samples[inchannels];
for (int j = 0; j < inchannels; ++j)
{
samples[j] = buffer[i * inchannels + j];
}
for (int j = 0; j < inchannels; ++j)
{
buffer[i * outchannels + j] = samples[j];
}
for (int j = inchannels; j < outchannels; ++j)
{
buffer[i * outchannels + j] = 0;
}
}
}
}
void scale_by_volume(float * buffer, size_t count, float volume)
{
if ( volume != 1.0 )
@ -842,6 +1020,31 @@ tryagain:
scale_by_volume( (float*) floatBuffer, amountReadFromFC / sizeof(float), volumeScale);
if ( hFilter ) {
int samples = amountReadFromFC / floatFormat.mBytesPerFrame;
[hFilter process:floatBuffer sampleCount:samples toBuffer:floatBuffer + amountReadFromFC];
memmove(floatBuffer, floatBuffer + amountReadFromFC, samples * sizeof(float) * 2);
amountReadFromFC = samples * sizeof(float) * 2;
}
else if ( inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2 )
{
int samples = amountReadFromFC / floatFormat.mBytesPerFrame;
downmix_to_stereo( (float*) floatBuffer, inputFormat.mChannelsPerFrame, samples );
amountReadFromFC = samples * sizeof(float) * 2;
}
else if ( inputFormat.mChannelsPerFrame > 1 && outputFormat.mChannelsPerFrame == 1 )
{
int samples = amountReadFromFC / floatFormat.mBytesPerFrame;
downmix_to_mono( (float*) floatBuffer, inputFormat.mChannelsPerFrame, samples );
amountReadFromFC = samples * sizeof(float);
}
else if ( inputFormat.mChannelsPerFrame < outputFormat.mChannelsPerFrame )
{
int samples = amountReadFromFC / floatFormat.mBytesPerFrame;
upmix( (float*) floatBuffer, inputFormat.mChannelsPerFrame, outputFormat.mChannelsPerFrame, samples );
amountReadFromFC = samples * sizeof(float) * outputFormat.mChannelsPerFrame;
}
floatSize = amountReadFromFC;
floatOffset = 0;
}
@ -872,6 +1075,15 @@ tryagain:
//User reset the volume scaling option
[self refreshVolumeScaling];
}
else if ([keyPath isEqualToString:@"values.headphoneVirtualization"] ||
[keyPath isEqualToString:@"values.hrirPath"]) {
// Reset the converter, without rebuffering
if (outputFormat.mChannelsPerFrame == 2 &&
inputFormat.mChannelsPerFrame >= 1 &&
inputFormat.mChannelsPerFrame <= 8) {
[self inputFormatDidChange:inputFormat];
}
}
}
static float db_to_scale(float db)
@ -929,8 +1141,6 @@ static float db_to_scale(float db)
inputFormat = inf;
outputFormat = outf;
nodeFormat = outputFormat;
rememberedLossless = lossless;
// These are the only sample formats we support translating
@ -982,6 +1192,33 @@ static float db_to_scale(float db)
dmFloatFormat.mBytesPerFrame = (32/8)*dmFloatFormat.mChannelsPerFrame;
dmFloatFormat.mBytesPerPacket = dmFloatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket;
BOOL hVirt = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"headphoneVirtualization"];
if (hVirt &&
outputFormat.mChannelsPerFrame == 2 &&
inputFormat.mChannelsPerFrame >= 1 &&
inputFormat.mChannelsPerFrame <= 8) {
NSString * userPreset = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"hrirPath"];
NSURL * presetUrl = nil;
if (userPreset && ![userPreset isEqualToString:@""]) {
presetUrl = [NSURL fileURLWithPath:userPreset];
if (![HeadphoneFilter validateImpulseFile:presetUrl])
presetUrl = nil;
}
if (!presetUrl) {
presetUrl = [[NSBundle mainBundle] URLForResource:@"gsx" withExtension:@"wv"];
if (![HeadphoneFilter validateImpulseFile:presetUrl])
presetUrl = nil;
}
if (presetUrl) {
hFilter = [[HeadphoneFilter alloc] initWithImpulseFile:presetUrl forSampleRate:outputFormat.mSampleRate withInputChannels:inputFormat.mChannelsPerFrame];
}
}
skipResampler = outputFormat.mSampleRate == floatFormat.mSampleRate;
sampleRatio = (double)outputFormat.mSampleRate / (double)floatFormat.mSampleRate;
@ -1034,6 +1271,8 @@ static float db_to_scale(float db)
DLog(@"Decoder dealloc");
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling"];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.headphoneVirtualization"];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.hrirPath"];
paused = NO;
[self cleanUp];
@ -1098,6 +1337,9 @@ static float db_to_scale(float db)
{
usleep(500);
}
if (hFilter) {
hFilter = nil;
}
if (hdcd_decoder)
{
free(hdcd_decoder);

View File

@ -1,26 +0,0 @@
//
// Downmix.h
// Cog
//
// Created by Christopher Snowhill on 2/05/22.
// Copyright 2022 __LoSnoCo__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
#import "HeadphoneFilter.h"
@interface DownmixProcessor : NSObject {
HeadphoneFilter *hFilter;
AudioStreamBasicDescription inputFormat;
AudioStreamBasicDescription outputFormat;
}
- (id) initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf;
- (void) process:(const void*)inBuffer frameCount:(size_t)frames output:(void *)outBuffer;
@end

View File

@ -1,302 +0,0 @@
//
// Downmix.m
// Cog
//
// Created by Christopher Snowhill on 2/05/22.
// Copyright 2022 __LoSnoCo__. All rights reserved.
//
#import "Downmix.h"
#import "Logging.h"
static const float STEREO_DOWNMIX[8-2][8][2]={
/*3.0*/
{
{0.5858F,0.0F},{0.0F,0.5858F},{0.4142F,0.4142F}
},
/*quadrophonic*/
{
{0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
},
/*5.0*/
{
{0.651F,0.0F},{0.0F,0.651F},{0.46F,0.46F},{0.5636F,0.3254F},
{0.3254F,0.5636F}
},
/*5.1*/
{
{0.529F,0.0F},{0.0F,0.529F},{0.3741F,0.3741F},{0.3741F,0.3741F},{0.4582F,0.2645F},
{0.2645F,0.4582F}
},
/*6.1*/
{
{0.4553F,0.0F},{0.0F,0.4553F},{0.322F,0.322F},{0.322F,0.322F},{0.2788F,0.2788F},
{0.3943F,0.2277F},{0.2277F,0.3943F}
},
/*7.1*/
{
{0.3886F,0.0F},{0.0F,0.3886F},{0.2748F,0.2748F},{0.2748F,0.2748F},{0.3366F,0.1943F},
{0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F}
}
};
static void downmix_to_stereo(const float * inBuffer, int channels, float * outBuffer, size_t count)
{
if (channels >= 3 && channels <= 8)
for (size_t i = 0; i < count; ++i)
{
float left = 0, right = 0;
for (int j = 0; j < channels; ++j)
{
left += inBuffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][0];
right += inBuffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][1];
}
outBuffer[i * 2 + 0] = left;
outBuffer[i * 2 + 1] = right;
}
}
static void downmix_to_mono(const float * inBuffer, int channels, float * outBuffer, size_t count)
{
float tempBuffer[count * 2];
if (channels >= 3 && channels <= 8)
{
downmix_to_stereo(inBuffer, channels, tempBuffer, count);
inBuffer = tempBuffer;
channels = 2;
}
float invchannels = 1.0 / (float)channels;
for (size_t i = 0; i < count; ++i)
{
float sample = 0;
for (int j = 0; j < channels; ++j)
{
sample += inBuffer[i * channels + j];
}
outBuffer[i] = sample * invchannels;
}
}
static void upmix(const float * inBuffer, int inchannels, float * outBuffer, int outchannels, size_t count)
{
for (ssize_t i = 0; i < count; ++i)
{
if (inchannels == 1 && outchannels == 2)
{
// upmix mono to stereo
float sample = inBuffer[i];
outBuffer[i * 2 + 0] = sample;
outBuffer[i * 2 + 1] = sample;
}
else if (inchannels == 1 && outchannels == 4)
{
// upmix mono to quad
float sample = inBuffer[i];
outBuffer[i * 4 + 0] = sample;
outBuffer[i * 4 + 1] = sample;
outBuffer[i * 4 + 2] = 0;
outBuffer[i * 4 + 3] = 0;
}
else if (inchannels == 1 && (outchannels == 3 || outchannels >= 5))
{
// upmix mono to center channel
float sample = inBuffer[i];
outBuffer[i * outchannels + 2] = sample;
for (int j = 0; j < 2; ++j)
{
outBuffer[i * outchannels + j] = 0;
}
for (int j = 3; j < outchannels; ++j)
{
outBuffer[i * outchannels + j] = 0;
}
}
else if (inchannels == 4 && outchannels >= 5)
{
float fl = inBuffer[i * 4 + 0];
float fr = inBuffer[i * 4 + 1];
float bl = inBuffer[i * 4 + 2];
float br = inBuffer[i * 4 + 3];
const int skipclfe = (outchannels == 5) ? 1 : 2;
outBuffer[i * outchannels + 0] = fl;
outBuffer[i * outchannels + 1] = fr;
outBuffer[i * outchannels + skipclfe + 2] = bl;
outBuffer[i * outchannels + skipclfe + 3] = br;
for (int j = 0; j < skipclfe; ++j)
{
outBuffer[i * outchannels + 2 + j] = 0;
}
for (int j = 4 + skipclfe; j < outchannels; ++j)
{
outBuffer[i * outchannels + j] = 0;
}
}
else if (inchannels == 5 && outchannels >= 6)
{
float fl = inBuffer[i * 5 + 0];
float fr = inBuffer[i * 5 + 1];
float c = inBuffer[i * 5 + 2];
float bl = inBuffer[i * 5 + 3];
float br = inBuffer[i * 5 + 4];
outBuffer[i * outchannels + 0] = fl;
outBuffer[i * outchannels + 1] = fr;
outBuffer[i * outchannels + 2] = c;
outBuffer[i * outchannels + 3] = 0;
outBuffer[i * outchannels + 4] = bl;
outBuffer[i * outchannels + 5] = br;
for (int j = 6; j < outchannels; ++j)
{
outBuffer[i * outchannels + j] = 0;
}
}
else if (inchannels == 7 && outchannels == 8)
{
float fl = inBuffer[i * 7 + 0];
float fr = inBuffer[i * 7 + 1];
float c = inBuffer[i * 7 + 2];
float lfe = inBuffer[i * 7 + 3];
float sl = inBuffer[i * 7 + 4];
float sr = inBuffer[i * 7 + 5];
float bc = inBuffer[i * 7 + 6];
outBuffer[i * 8 + 0] = fl;
outBuffer[i * 8 + 1] = fr;
outBuffer[i * 8 + 2] = c;
outBuffer[i * 8 + 3] = lfe;
outBuffer[i * 8 + 4] = bc;
outBuffer[i * 8 + 5] = bc;
outBuffer[i * 8 + 6] = sl;
outBuffer[i * 8 + 7] = sr;
}
else
{
// upmix N channels to N channels plus silence the empty channels
float samples[inchannels];
for (int j = 0; j < inchannels; ++j)
{
samples[j] = inBuffer[i * inchannels + j];
}
for (int j = 0; j < inchannels; ++j)
{
outBuffer[i * outchannels + j] = samples[j];
}
for (int j = inchannels; j < outchannels; ++j)
{
outBuffer[i * outchannels + j] = 0;
}
}
}
}
@implementation DownmixProcessor
- (id) initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf {
self = [super init];
if (self) {
if (inf.mFormatID != kAudioFormatLinearPCM ||
(inf.mFormatFlags & kAudioFormatFlagsNativeFloatPacked) != kAudioFormatFlagsNativeFloatPacked ||
inf.mBitsPerChannel != 32 ||
inf.mBytesPerFrame != (4 * inf.mChannelsPerFrame) ||
inf.mBytesPerPacket != inf.mFramesPerPacket * inf.mBytesPerFrame)
return nil;
if (outf.mFormatID != kAudioFormatLinearPCM ||
(outf.mFormatFlags & kAudioFormatFlagsNativeFloatPacked) != kAudioFormatFlagsNativeFloatPacked ||
outf.mBitsPerChannel != 32 ||
outf.mBytesPerFrame != (4 * outf.mChannelsPerFrame) ||
outf.mBytesPerPacket != outf.mFramesPerPacket * outf.mBytesPerFrame)
return nil;
inputFormat = inf;
outputFormat = outf;
[self setupVirt];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.headphoneVirtualization" options:0 context:nil];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.hrirPath" options:0 context:nil];
}
return self;
}
- (void) dealloc {
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.headphoneVirtualization"];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.hrirPath"];
}
- (void) setupVirt {
@synchronized(hFilter) {
hFilter = nil;
}
BOOL hVirt = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"headphoneVirtualization"];
if (hVirt &&
outputFormat.mChannelsPerFrame == 2 &&
inputFormat.mChannelsPerFrame >= 1 &&
inputFormat.mChannelsPerFrame <= 8) {
NSString * userPreset = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"hrirPath"];
NSURL * presetUrl = nil;
if (userPreset && ![userPreset isEqualToString:@""]) {
presetUrl = [NSURL fileURLWithPath:userPreset];
if (![HeadphoneFilter validateImpulseFile:presetUrl])
presetUrl = nil;
}
if (!presetUrl) {
presetUrl = [[NSBundle mainBundle] URLForResource:@"gsx" withExtension:@"wv"];
if (![HeadphoneFilter validateImpulseFile:presetUrl])
presetUrl = nil;
}
if (presetUrl) {
@synchronized(hFilter) {
hFilter = [[HeadphoneFilter alloc] initWithImpulseFile:presetUrl forSampleRate:outputFormat.mSampleRate withInputChannels:inputFormat.mChannelsPerFrame];
}
}
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
DLog(@"SOMETHING CHANGED!");
if ([keyPath isEqualToString:@"values.headphoneVirtualization"] ||
[keyPath isEqualToString:@"values.hrirPath"]) {
// Reset the converter, without rebuffering
[self setupVirt];
}
}
- (void) process:(const void *)inBuffer frameCount:(size_t)frames output:(void *)outBuffer {
@synchronized (hFilter) {
if ( hFilter ) {
[hFilter process:(const float *) inBuffer sampleCount:frames toBuffer:(float *) outBuffer];
return;
}
}
if ( inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2 )
{
downmix_to_stereo( (const float*) inBuffer, inputFormat.mChannelsPerFrame, (float*) outBuffer, frames );
}
else if ( inputFormat.mChannelsPerFrame > 1 && outputFormat.mChannelsPerFrame == 1 )
{
downmix_to_mono( (const float*) inBuffer, inputFormat.mChannelsPerFrame, (float*) outBuffer, frames );
}
else if ( inputFormat.mChannelsPerFrame < outputFormat.mChannelsPerFrame )
{
upmix( (const float*) inBuffer, inputFormat.mChannelsPerFrame, (float*) outBuffer, outputFormat.mChannelsPerFrame, frames );
}
else if ( inputFormat.mChannelsPerFrame == outputFormat.mChannelsPerFrame )
{
memcpy(outBuffer, inBuffer, frames * outputFormat.mBytesPerPacket);
}
}
@end

View File

@ -51,8 +51,6 @@
bytesPerFrame = ((bitsPerSample + 7) / 8) * channels;
nodeFormat = propertiesToASBD(properties);
shouldContinue = YES;
shouldSeek = NO;
@ -70,8 +68,6 @@
bytesPerFrame = ((bitsPerSample + 7) / 8) * channels;
nodeFormat = propertiesToASBD(properties);
[self registerObservers];
shouldContinue = YES;

View File

@ -7,7 +7,6 @@
//
#import <Cocoa/Cocoa.h>
#import <CoreAudio/CoreAudio.h>
#import "VirtualRingBuffer.h"
#import "Semaphore.h"
@ -26,12 +25,7 @@
BOOL shouldContinue;
BOOL endOfStream; //All data is now in buffer
BOOL initialBufferFilled;
AudioStreamBasicDescription nodeFormat;
}
@property (readonly) AudioStreamBasicDescription nodeFormat;
- (id)initWithController:(id)c previous:(id)p;
- (int)writeData:(void *)ptr amount:(int)a;

View File

@ -13,8 +13,6 @@
@implementation Node
@synthesize nodeFormat;
- (id)initWithController:(id)c previous:(id)p
{
self = [super init];
@ -43,8 +41,7 @@
while (shouldContinue == YES && amountLeft > 0)
{
BOOL wrapped;
availOutput = [buffer lengthAvailableToWriteReturningPointer:&writePtr bufferWrapped:&wrapped];
availOutput = [buffer lengthAvailableToWriteReturningPointer:&writePtr];
if (availOutput == 0) {
if (initialBufferFilled == NO) {
initialBufferFilled = YES;

View File

@ -24,9 +24,6 @@
BOOL paused;
BOOL started;
BOOL formatSetup;
BOOL formatChanged;
}
- (void)beginEqualizer:(AudioUnit)eq;
@ -65,6 +62,4 @@
- (void)sustainHDCD;
- (BOOL)formatChanged;
@end

View File

@ -22,9 +22,6 @@
paused = YES;
started = NO;
formatSetup = NO;
formatChanged = NO;
output = [[OutputCoreAudio alloc] initWithController:self];
@ -87,7 +84,7 @@
@autoreleasepool {
int n;
[self setPreviousNode:[[controller bufferChain] finalNode]];
n = [super readData:ptr amount:amount];
/* if (n == 0) {
@ -131,14 +128,6 @@
if (oldSampleRatio)
amountPlayed += oldSampleRatio * [[converter buffer] bufferedLength];
#endif
AudioStreamBasicDescription inf = [bufferChain inputFormat];
format.mChannelsPerFrame = inf.mChannelsPerFrame;
format.mBytesPerFrame = ((format.mBitsPerChannel + 7) / 8) * format.mChannelsPerFrame;
format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket;
sampleRatio = 1.0 / (format.mSampleRate * format.mBytesPerPacket);
[converter setOutputFormat:format];
[converter inputFormatDidChange:[bufferChain inputFormat]];
}
@ -189,21 +178,4 @@
[output sustainHDCD];
}
- (BOOL)formatChanged
{
[self setPreviousNode:[[controller bufferChain] finalNode]];
AudioStreamBasicDescription inf = [[self previousNode] nodeFormat];
if (!formatSetup || memcmp(&nodeFormat, &inf, sizeof(nodeFormat)) != 0) {
nodeFormat = inf;
formatSetup = YES;
formatChanged = YES;
}
BOOL copyFormatChanged = formatChanged;
formatChanged = NO;
return copyFormatChanged;
}
@end

View File

@ -59,8 +59,6 @@
83725A8E27AA0DE60003F694 /* libsoxr.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83725A8A27AA0DBF0003F694 /* libsoxr.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83725A9027AA16C90003F694 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83725A7B27AA0D8A0003F694 /* Accelerate.framework */; };
83725A9127AA16D50003F694 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83725A7C27AA0D8E0003F694 /* AVFoundation.framework */; };
8380F2D927AE6053009183C1 /* Downmix.m in Sources */ = {isa = PBXBuildFile; fileRef = 8380F2D727AE6053009183C1 /* Downmix.m */; };
8380F2DA27AE6053009183C1 /* Downmix.h in Headers */ = {isa = PBXBuildFile; fileRef = 8380F2D827AE6053009183C1 /* Downmix.h */; };
8384912718080FF100E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384912618080FF100E7332D /* Logging.h */; };
839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; };
839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; };
@ -153,8 +151,6 @@
83725A7C27AA0D8E0003F694 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
83725A8827AA0DBF0003F694 /* soxr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = soxr.h; sourceTree = "<group>"; };
83725A8A27AA0DBF0003F694 /* libsoxr.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libsoxr.0.dylib; sourceTree = "<group>"; };
8380F2D727AE6053009183C1 /* Downmix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Downmix.m; sourceTree = "<group>"; };
8380F2D827AE6053009183C1 /* Downmix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Downmix.h; sourceTree = "<group>"; };
8384912618080FF100E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
839366651815923C006DD712 /* CogPluginMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogPluginMulti.h; sourceTree = "<group>"; };
839366661815923C006DD712 /* CogPluginMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogPluginMulti.m; sourceTree = "<group>"; };
@ -288,8 +284,6 @@
17D21C750B8BE4BA00D1EBDE /* Chain */ = {
isa = PBXGroup;
children = (
8380F2D827AE6053009183C1 /* Downmix.h */,
8380F2D727AE6053009183C1 /* Downmix.m */,
83A44A00279119B50049B6E2 /* RefillNode.h */,
83A449FF279119B50049B6E2 /* RefillNode.m */,
17D21C760B8BE4BA00D1EBDE /* BufferChain.h */,
@ -452,7 +446,6 @@
8347C7412796C58800FA8A7D /* NSFileHandle+CreateFile.h in Headers */,
17C940230B900909008627D6 /* AudioMetadataReader.h in Headers */,
83725A8B27AA0DBF0003F694 /* soxr.h in Headers */,
8380F2DA27AE6053009183C1 /* Downmix.h in Headers */,
17B619300B909BC300BC003F /* AudioPropertiesReader.h in Headers */,
835EDD7D279FE307001EDCCE /* HeadphoneFilter.h in Headers */,
839366671815923C006DD712 /* CogPluginMulti.h in Headers */,
@ -553,7 +546,6 @@
8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */,
17D21DC80B8BE79700D1EBDE /* CoreAudioUtils.m in Sources */,
839366681815923C006DD712 /* CogPluginMulti.m in Sources */,
8380F2D927AE6053009183C1 /* Downmix.m in Sources */,
835C88AA2797D4D400E28EAE /* lpc.c in Sources */,
17D21EBE0B8BF44000D1EBDE /* AudioPlayer.m in Sources */,
17F94DD60B8D0F7000A34E87 /* PluginController.m in Sources */,

View File

@ -19,8 +19,6 @@
#import "Semaphore.h"
#import "Downmix.h"
//#define OUTPUT_LOG
#ifdef OUTPUT_LOG
#import <stdio.h>
@ -44,9 +42,6 @@
BOOL eqEnabled;
BOOL streamFormatSetup;
atomic_long bytesBuffered;
atomic_long bytesRendered;
atomic_long bytesHdcdSustained;
@ -59,19 +54,12 @@
AudioDeviceID outputDeviceID;
AudioStreamBasicDescription deviceFormat; // info about the default device
AudioStreamBasicDescription outerStreamFormat; // this is set when the buffer changes
AudioStreamBasicDescription streamFormat; // this is set when the output callback gets it
AUAudioUnit *_au;
size_t _bufferSize;
AudioUnit _eq;
DownmixProcessor *downmixer;
void * savedBuffer;
size_t savedSize;
size_t savedMaxSize;
#ifdef OUTPUT_LOG
FILE *_logFile;
#endif

View File

@ -83,132 +83,45 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
return 0;
}
if (_self->savedSize) {
int readBytes = (int) _self->savedSize;
const int streamchannels = _self->streamFormat.mChannelsPerFrame;
const int streamBytesPerPacket = streamchannels * sizeof(float);
int samplesToRead = readBytes / streamBytesPerPacket;
if (samplesToRead > (amountToRead / bytesPerPacket))
samplesToRead = amountToRead / bytesPerPacket;
readBytes = samplesToRead * streamBytesPerPacket;
atomic_fetch_sub(&_self->bytesBuffered, readBytes);
float downmixBuffer[samplesToRead * channels];
[_self->downmixer process:_self->savedBuffer frameCount:samplesToRead output:downmixBuffer];
fillBuffers(ioData, downmixBuffer, samplesToRead, 0);
amountRead += samplesToRead * bytesPerPacket;
[_self->outputController incrementAmountPlayed:samplesToRead * bytesPerPacket];
atomic_fetch_add(&_self->bytesRendered, samplesToRead * bytesPerPacket);
[_self->writeSemaphore signal];
if (_self->savedSize > readBytes) {
_self->savedSize -= readBytes;
memmove(_self->savedBuffer, _self->savedBuffer + readBytes, _self->savedSize);
}
else {
_self->savedSize = 0;
}
}
void * readPtr;
int toRead = [[_self->outputController buffer] lengthAvailableToReadReturningPointer:&readPtr];
while (amountRead < amountToRead && [_self->outputController shouldContinue])
if (toRead > amountToRead)
toRead = amountToRead;
if (toRead) {
fillBuffers(ioData, (float*)readPtr, toRead / bytesPerPacket, 0);
amountRead = toRead;
[[_self->outputController buffer] didReadLength:toRead];
[_self->outputController incrementAmountPlayed:amountRead];
atomic_fetch_add(&_self->bytesRendered, amountRead);
[_self->writeSemaphore signal];
}
else
[[_self->outputController buffer] didReadLength:0];
// Try repeatedly! Buffer wraps can cause a slight data shortage, as can
// unexpected track changes.
while ((amountRead < amountToRead) && [_self->outputController shouldContinue] == YES)
{
void * readPtr;
int toRead = 0;
do {
toRead = [[_self->outputController buffer] lengthAvailableToReadReturningPointer:&readPtr];
if (toRead && *((uint8_t*)readPtr) == 0xFF) {
size_t toSkip = 0;
while (toRead && *((uint8_t*)readPtr) == 0xFF) {
toSkip++;
readPtr++;
toRead--;
}
[[_self->outputController buffer] didReadLength:(int)toSkip];
toRead = 0;
}
}
while (!toRead);
int amountRead2; //Use this since return type of readdata isnt known...may want to fix then can do a simple += to readdata
amountRead2 = [[_self->outputController buffer] lengthAvailableToReadReturningPointer:&readPtr];
if (amountRead2 > (amountToRead - amountRead))
amountRead2 = amountToRead - amountRead;
if (amountRead2) {
atomic_fetch_add(&_self->bytesRendered, amountRead2);
fillBuffers(ioData, (float*)readPtr, amountRead2 / bytesPerPacket, amountRead / bytesPerPacket);
[[_self->outputController buffer] didReadLength:amountRead2];
int bytesRead = 0;
int32_t chunkId = -1;
if (toRead >= 4) {
memcpy(&chunkId, readPtr, 4);
readPtr += 4;
toRead -= 4;
bytesRead += 4;
}
if (chunkId == 1 && toRead >= sizeof(AudioStreamBasicDescription)) {
AudioStreamBasicDescription inf;
memcpy(&inf, readPtr, sizeof(inf));
readPtr += sizeof(inf);
toRead -= sizeof(inf);
bytesRead += sizeof(inf);
[_self->outputController incrementAmountPlayed:amountRead2];
if (!_self->streamFormatSetup || memcmp(&inf, &_self->streamFormat, sizeof(inf)) != 0) {
_self->streamFormatSetup = YES;
_self->streamFormat = inf;
_self->downmixer = [[DownmixProcessor alloc] initWithInputFormat:inf andOutputFormat:_self->deviceFormat];
}
if (toRead >= 4) {
memcpy(&chunkId, readPtr, 4);
readPtr += 4;
toRead -= 4;
bytesRead += 4;
}
else chunkId = -1;
}
const int streamchannels = _self->streamFormat.mChannelsPerFrame;
const int streamBytesPerPacket = streamchannels * sizeof(float);
if (chunkId == 0 && toRead >= 4) {
memcpy(&chunkId, readPtr, 4);
readPtr += 4;
bytesRead += 4;
toRead = chunkId;
}
if (toRead) {
size_t samplesToRead = toRead / streamBytesPerPacket;
size_t saveBytes = 0;
if (samplesToRead * bytesPerPacket > (amountToRead - amountRead)) {
size_t shortToRead = (amountToRead - amountRead) / bytesPerPacket;
saveBytes = (samplesToRead - shortToRead) * streamBytesPerPacket;
samplesToRead = shortToRead;
}
float downmixBuffer[samplesToRead * channels];
[_self->downmixer process:readPtr frameCount:samplesToRead output:downmixBuffer];
fillBuffers(ioData, downmixBuffer, samplesToRead, amountRead / bytesPerPacket);
amountRead += samplesToRead * bytesPerPacket;
bytesRead += toRead;
if (saveBytes) {
if (!_self->savedBuffer || _self->savedMaxSize < saveBytes) {
_self->savedBuffer = realloc(_self->savedBuffer, _self->savedMaxSize = saveBytes * 3);
}
_self->savedSize = saveBytes;
memcpy(_self->savedBuffer, readPtr + toRead - saveBytes, saveBytes);
}
atomic_fetch_sub(&_self->bytesBuffered, toRead - saveBytes);
[[_self->outputController buffer] didReadLength:bytesRead];
[_self->outputController incrementAmountPlayed:samplesToRead * bytesPerPacket];
atomic_fetch_add(&_self->bytesRendered, samplesToRead * bytesPerPacket);
amountRead += amountRead2;
[_self->writeSemaphore signal];
}
else
[[_self->outputController buffer] didReadLength:bytesRead];
else {
[[_self->outputController buffer] didReadLength:0];
[_self->readSemaphore timedWait:500];
}
}
float volumeScale = 1.0;
@ -225,6 +138,14 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
scaleBuffersByVolume(ioData, _self->volume * volumeScale);
if (amountRead < amountToRead)
{
// Either underrun, or no data at all. Caller output tends to just
// buffer loop if it doesn't get anything, so always produce a full
// buffer, and silence anything we couldn't supply.
clearBuffers(ioData, (amountToRead - amountRead) / bytesPerPacket, amountRead / bytesPerPacket);
}
return 0;
};
@ -244,15 +165,6 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
started = NO;
stopNext = NO;
streamFormatSetup = NO;
downmixer = nil;
savedBuffer = NULL;
savedSize = 0;
savedMaxSize = 0;
atomic_init(&bytesBuffered, 0);
atomic_init(&bytesRendered, 0);
atomic_init(&bytesHdcdSustained, 0);
@ -312,7 +224,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
if ([outputController shouldReset]) {
[[outputController buffer] empty];
atomic_store(&bytesBuffered, 0);
[outputController setShouldReset:NO];
[delayedEvents removeAllObjects];
delayedEventsPopped = YES;
@ -333,73 +244,13 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
break;
void *writePtr;
BOOL wrapped = NO;
int toWrite = [[outputController buffer] lengthAvailableToWriteReturningPointer:&writePtr bufferWrapped:&wrapped];
int bytesWritten = 0;
if (toWrite >= 4 + sizeof(AudioStreamBasicDescription)) {
if ([outputController formatChanged]) {
int32_t chunkId = 1; // ASBD
memcpy(writePtr, &chunkId, 4);
writePtr += 4;
toWrite -= 4;
bytesWritten += 4;
AudioStreamBasicDescription inf = [outputController nodeFormat];
outerStreamFormat = inf;
memcpy(writePtr, &inf, sizeof(inf));
writePtr += sizeof(inf);
toWrite -= sizeof(inf);
bytesWritten += sizeof(inf);
}
}
[[outputController buffer] didWriteLength:bytesWritten];
toWrite = [[outputController buffer] lengthAvailableToWriteReturningPointer:&writePtr bufferWrapped:&wrapped];
int toWrite = [[outputController buffer] lengthAvailableToWriteReturningPointer:&writePtr];
int bytesRead = 0;
bytesWritten = 0;
if (toWrite >= 4 + 4 + 512 * outerStreamFormat.mBytesPerPacket) {
uint8_t buffer[512 * outerStreamFormat.mBytesPerPacket];
bytesRead = [outputController readData:buffer amount:(int)sizeof(buffer)];
while (bytesRead < sizeof(buffer) && ![outputController endOfStream]) {
int bytesRead2 = [outputController readData:buffer + bytesRead amount:(int)(sizeof(buffer) - bytesRead)];
bytesRead += bytesRead2;
}
int32_t chunkId = 0; // audio data
memcpy(writePtr, &chunkId, 4);
writePtr += 4;
toWrite -= 4;
bytesWritten += 4;
chunkId = bytesRead;
memcpy(writePtr, &chunkId, 4);
writePtr += 4;
toWrite -= 4;
bytesWritten += 4;
memcpy(writePtr, buffer, bytesRead);
writePtr += bytesRead;
toWrite -= bytesRead;
bytesWritten += bytesRead;
atomic_fetch_add(&bytesBuffered, bytesRead);
[[outputController buffer] didWriteLength:bytesWritten];
}
else if (wrapped && toWrite > 0) {
memset(writePtr, 0xFF, toWrite);
[[outputController buffer] didWriteLength:toWrite];
}
else if (toWrite) {
[[outputController buffer] didWriteLength:0];
toWrite = 0;
}
if (toWrite > CHUNK_SIZE)
toWrite = CHUNK_SIZE;
if (toWrite)
bytesRead = [outputController readData:writePtr amount:toWrite];
[[outputController buffer] didWriteLength:bytesRead];
if (bytesRead) {
[readSemaphore signal];
continue;
@ -419,20 +270,20 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
// End of input possibly reached
if (delayedEventsPopped && [outputController endOfStream] == YES)
{
long _bytesBuffered = atomic_load_explicit(&bytesBuffered, memory_order_relaxed) * deviceFormat.mBytesPerPacket / outerStreamFormat.mBytesPerPacket;
_bytesBuffered += atomic_load_explicit(&bytesRendered, memory_order_relaxed);
long bytesBuffered = [[outputController buffer] bufferedLength];
bytesBuffered += atomic_load_explicit(&bytesRendered, memory_order_relaxed);
if ([outputController chainQueueHasTracks])
{
if (_bytesBuffered < CHUNK_SIZE / 2)
_bytesBuffered = 0;
if (bytesBuffered < CHUNK_SIZE / 2)
bytesBuffered = 0;
else
_bytesBuffered -= CHUNK_SIZE / 2;
bytesBuffered -= CHUNK_SIZE / 2;
}
else {
stopNext = YES;
break;
}
[delayedEvents addObject:[NSNumber numberWithLong:_bytesBuffered]];
[delayedEvents addObject:[NSNumber numberWithLong:bytesBuffered]];
delayedEventsPopped = NO;
if (!started) {
started = YES;
@ -626,9 +477,8 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
NSError *err;
AVAudioFormat *renderFormat;
[outputController incrementAmountPlayed:atomic_load_explicit(&bytesBuffered, memory_order_relaxed)];
[outputController incrementAmountPlayed:[[outputController buffer] bufferedLength]];
[[outputController buffer] empty];
atomic_store(&bytesBuffered, 0);
_deviceFormat = format;
deviceFormat = *(format.streamDescription);
@ -712,12 +562,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
paused = NO;
stopNext = NO;
outputDeviceID = -1;
streamFormatSetup = NO;
savedBuffer = NULL;
savedSize = 0;
savedMaxSize = 0;
AudioComponentDescription desc;
NSError *err;
@ -824,8 +668,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
return 0;
};
[_au setMaximumFramesToRender:512];
UInt32 value;
UInt32 size = sizeof(value);
@ -946,14 +788,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
_logFile = NULL;
}
#endif
if (savedBuffer)
{
free(savedBuffer);
savedBuffer = NULL;
savedSize = 0;
savedMaxSize = 0;
}
outputController = nil;
}

View File

@ -83,7 +83,7 @@
// Write operations:
// The writing thread must call this method first.
- (UInt32)lengthAvailableToWriteReturningPointer:(void **)returnedWritePointer bufferWrapped:(BOOL*)wrapped;
- (UInt32)lengthAvailableToWriteReturningPointer:(void **)returnedWritePointer;
// Iff a value > 0 is returned, the writing thread may then write that much data into the returned pointer.
// Afterwards, the writing thread must call didWriteLength:.
- (void)didWriteLength:(UInt32)length;

View File

@ -140,7 +140,7 @@ static void deallocateVirtualBuffer(void *buffer, UInt32 bufferLength);
// Write operations
//
- (UInt32)lengthAvailableToWriteReturningPointer:(void **)returnedWritePointer bufferWrapped:(BOOL *)wrapped
- (UInt32)lengthAvailableToWriteReturningPointer:(void **)returnedWritePointer
{
// Assumptions:
// returnedWritePointer != NULL
@ -152,10 +152,8 @@ static void deallocateVirtualBuffer(void *buffer, UInt32 bufferLength);
int localBufferFilled = atomic_load_explicit(&bufferFilled, memory_order_relaxed);
length = bufferLength - localBufferFilled;
if (length >= bufferLength - localWritePointer) {
*wrapped = YES;
if (length > bufferLength - localWritePointer)
length = bufferLength - localWritePointer;
}
*returnedWritePointer = buffer + localWritePointer;