Fixed input to float conversion and made it mandatory, so I could move volume scaling to the converter node
parent
8aa01894ee
commit
15c545b10d
|
@ -106,7 +106,7 @@
|
||||||
[rgi retain];
|
[rgi retain];
|
||||||
[rgInfo release];
|
[rgInfo release];
|
||||||
rgInfo = rgi;
|
rgInfo = rgi;
|
||||||
[inputNode setRGInfo:rgi];
|
[converterNode setRGInfo:rgi];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)rgInfo
|
- (NSDictionary *)rgInfo
|
||||||
|
|
|
@ -15,26 +15,36 @@
|
||||||
#import "Node.h"
|
#import "Node.h"
|
||||||
|
|
||||||
@interface ConverterNode : Node {
|
@interface ConverterNode : Node {
|
||||||
|
NSDictionary * rgInfo;
|
||||||
|
|
||||||
AudioConverterRef converter;
|
AudioConverterRef converter;
|
||||||
AudioConverterRef converterDownmix;
|
AudioConverterRef converterFloat;
|
||||||
void *callbackBuffer;
|
void *callbackBuffer;
|
||||||
|
|
||||||
void *downmixBuffer;
|
float volumeScale;
|
||||||
int downmixSize, downmixOffset;
|
|
||||||
|
void *floatBuffer;
|
||||||
|
int floatSize, floatOffset;
|
||||||
|
|
||||||
AudioStreamBasicDescription inputFormat;
|
AudioStreamBasicDescription inputFormat;
|
||||||
AudioStreamBasicDescription downmixFormat;
|
AudioStreamBasicDescription floatFormat;
|
||||||
AudioStreamBasicDescription outputFormat;
|
AudioStreamBasicDescription outputFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)registerObservers;
|
||||||
|
|
||||||
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inputFormat outputFormat:(AudioStreamBasicDescription)outputFormat;
|
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inputFormat outputFormat:(AudioStreamBasicDescription)outputFormat;
|
||||||
- (void)cleanUp;
|
- (void)cleanUp;
|
||||||
|
|
||||||
- (void)process;
|
- (void)process;
|
||||||
- (int)convert:(void *)dest amount:(int)amount;
|
- (int)convert:(void *)dest amount:(int)amount;
|
||||||
|
|
||||||
|
- (void)setRGInfo:(NSDictionary *)rgi;
|
||||||
|
|
||||||
- (void)setOutputFormat:(AudioStreamBasicDescription)format;
|
- (void)setOutputFormat:(AudioStreamBasicDescription)format;
|
||||||
|
|
||||||
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format;
|
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format;
|
||||||
|
|
||||||
|
- (void)refreshVolumeScaling;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -75,6 +75,13 @@ static void downmix_to_stereo(float * buffer, int channels, int count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void scale_by_volume(float * buffer, int count, float volume)
|
||||||
|
{
|
||||||
|
if ( volume != 1.0 )
|
||||||
|
for (int i = 0; i < count; ++i )
|
||||||
|
buffer[i] *= volume;
|
||||||
|
}
|
||||||
|
|
||||||
//called from the complexfill when the audio is converted...good clean fun
|
//called from the complexfill when the audio is converted...good clean fun
|
||||||
static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
|
static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
|
||||||
{
|
{
|
||||||
|
@ -114,7 +121,7 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static OSStatus ACDownmixProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
|
static OSStatus ACFloatProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
|
||||||
{
|
{
|
||||||
ConverterNode *converter = (ConverterNode *)inUserData;
|
ConverterNode *converter = (ConverterNode *)inUserData;
|
||||||
OSStatus err = noErr;
|
OSStatus err = noErr;
|
||||||
|
@ -128,17 +135,17 @@ static OSStatus ACDownmixProc(AudioConverterRef inAudioConverter, UInt32* ioNumb
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
amountToWrite = (*ioNumberDataPackets)*(converter->downmixFormat.mBytesPerPacket);
|
amountToWrite = (*ioNumberDataPackets)*(converter->floatFormat.mBytesPerPacket);
|
||||||
|
|
||||||
if ( amountToWrite + converter->downmixOffset > converter->downmixSize )
|
if ( amountToWrite + converter->floatOffset > converter->floatSize )
|
||||||
amountToWrite = converter->downmixSize - converter->downmixOffset;
|
amountToWrite = converter->floatSize - converter->floatOffset;
|
||||||
|
|
||||||
ioData->mBuffers[0].mData = converter->downmixBuffer + converter->downmixOffset;
|
ioData->mBuffers[0].mData = converter->floatBuffer + converter->floatOffset;
|
||||||
ioData->mBuffers[0].mDataByteSize = amountToWrite;
|
ioData->mBuffers[0].mDataByteSize = amountToWrite;
|
||||||
ioData->mBuffers[0].mNumberChannels = (converter->downmixFormat.mChannelsPerFrame);
|
ioData->mBuffers[0].mNumberChannels = (converter->floatFormat.mChannelsPerFrame);
|
||||||
ioData->mNumberBuffers = 1;
|
ioData->mNumberBuffers = 1;
|
||||||
|
|
||||||
converter->downmixOffset += amountToWrite;
|
converter->floatOffset += amountToWrite;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -161,90 +168,135 @@ static OSStatus ACDownmixProc(AudioConverterRef inAudioConverter, UInt32* ioNumb
|
||||||
OSStatus err;
|
OSStatus err;
|
||||||
int amountRead = 0;
|
int amountRead = 0;
|
||||||
|
|
||||||
if ( converterDownmix )
|
if (floatOffset == floatSize) {
|
||||||
{
|
|
||||||
if (downmixOffset == downmixSize) {
|
|
||||||
ioNumberFrames = amount / outputFormat.mBytesPerFrame;
|
|
||||||
|
|
||||||
downmixBuffer = realloc( downmixBuffer, ioNumberFrames * downmixFormat.mBytesPerFrame );
|
|
||||||
ioData.mBuffers[0].mData = downmixBuffer;
|
|
||||||
ioData.mBuffers[0].mDataByteSize = ioNumberFrames * downmixFormat.mBytesPerFrame;
|
|
||||||
ioData.mBuffers[0].mNumberChannels = downmixFormat.mChannelsPerFrame;
|
|
||||||
ioData.mNumberBuffers = 1;
|
|
||||||
|
|
||||||
tryagain:
|
|
||||||
err = AudioConverterFillComplexBuffer(converter, ACInputProc, self, &ioNumberFrames, &ioData, NULL);
|
|
||||||
amountRead += ioData.mBuffers[0].mDataByteSize;
|
|
||||||
if (err == 100)
|
|
||||||
{
|
|
||||||
NSLog(@"INSIZE: %i", amountRead);
|
|
||||||
ioData.mBuffers[0].mData = downmixBuffer + amountRead;
|
|
||||||
ioNumberFrames = ( amount / outputFormat.mBytesPerFrame ) - ( amountRead / downmixFormat.mBytesPerFrame );
|
|
||||||
ioData.mBuffers[0].mDataByteSize = ioNumberFrames * downmixFormat.mBytesPerFrame;
|
|
||||||
goto tryagain;
|
|
||||||
}
|
|
||||||
else if (err != noErr)
|
|
||||||
{
|
|
||||||
NSLog(@"Error: %i", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
downmix_to_stereo( (float*) downmixBuffer, downmixFormat.mChannelsPerFrame, amountRead / downmixFormat.mBytesPerFrame );
|
|
||||||
|
|
||||||
downmixSize = amountRead;
|
|
||||||
downmixOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ioNumberFrames = amount / outputFormat.mBytesPerFrame;
|
ioNumberFrames = amount / outputFormat.mBytesPerFrame;
|
||||||
ioData.mBuffers[0].mData = dest;
|
|
||||||
ioData.mBuffers[0].mDataByteSize = amount;
|
floatBuffer = realloc( floatBuffer, ioNumberFrames * floatFormat.mBytesPerFrame );
|
||||||
ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
|
ioData.mBuffers[0].mData = floatBuffer;
|
||||||
|
ioData.mBuffers[0].mDataByteSize = ioNumberFrames * floatFormat.mBytesPerFrame;
|
||||||
|
ioData.mBuffers[0].mNumberChannels = floatFormat.mChannelsPerFrame;
|
||||||
ioData.mNumberBuffers = 1;
|
ioData.mNumberBuffers = 1;
|
||||||
|
|
||||||
amountRead = 0;
|
tryagain:
|
||||||
|
err = AudioConverterFillComplexBuffer(converterFloat, ACInputProc, self, &ioNumberFrames, &ioData, NULL);
|
||||||
tryagain2:
|
|
||||||
err = AudioConverterFillComplexBuffer(converterDownmix, ACDownmixProc, self, &ioNumberFrames, &ioData, NULL);
|
|
||||||
amountRead += ioData.mBuffers[0].mDataByteSize;
|
amountRead += ioData.mBuffers[0].mDataByteSize;
|
||||||
if (err == 100)
|
if (err == 100)
|
||||||
{
|
{
|
||||||
NSLog(@"INSIZE: %i", amountRead);
|
NSLog(@"INSIZE: %i", amountRead);
|
||||||
ioData.mBuffers[0].mData = dest + amountRead;
|
ioData.mBuffers[0].mData = floatBuffer + amountRead;
|
||||||
ioNumberFrames = ( amount - amountRead ) / outputFormat.mBytesPerFrame;
|
ioNumberFrames = ( amount / outputFormat.mBytesPerFrame ) - ( amountRead / floatFormat.mBytesPerFrame );
|
||||||
ioData.mBuffers[0].mDataByteSize = ioNumberFrames * outputFormat.mBytesPerFrame;
|
ioData.mBuffers[0].mDataByteSize = ioNumberFrames * floatFormat.mBytesPerFrame;
|
||||||
goto tryagain2;
|
goto tryagain;
|
||||||
}
|
}
|
||||||
else if (err != noErr && err != kAudioConverterErr_InvalidInputSize)
|
else if (err != noErr && err != kAudioConverterErr_InvalidInputSize)
|
||||||
{
|
{
|
||||||
NSLog(@"Error: %i", err);
|
NSLog(@"Error: %i", err);
|
||||||
|
return amountRead;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ioNumberFrames = amount/outputFormat.mBytesPerFrame;
|
|
||||||
ioData.mBuffers[0].mData = dest;
|
|
||||||
ioData.mBuffers[0].mDataByteSize = amount;
|
|
||||||
ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
|
|
||||||
ioData.mNumberBuffers = 1;
|
|
||||||
|
|
||||||
tryagain3:
|
if ( inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2 )
|
||||||
err = AudioConverterFillComplexBuffer(converter, ACInputProc, self, &ioNumberFrames, &ioData, NULL);
|
downmix_to_stereo( (float*) floatBuffer, inputFormat.mChannelsPerFrame, amountRead / floatFormat.mBytesPerFrame );
|
||||||
amountRead += ioData.mBuffers[0].mDataByteSize;
|
|
||||||
if (err == 100) //It returns insz at EOS at times...so run it again to make sure all data is converted
|
scale_by_volume( (float*) floatBuffer, amountRead / sizeof(float), volumeScale);
|
||||||
{
|
|
||||||
NSLog(@"INSIZE: %i", amountRead);
|
floatSize = amountRead;
|
||||||
ioData.mBuffers[0].mData = dest + amountRead;
|
floatOffset = 0;
|
||||||
ioNumberFrames = ( amount - amountRead ) / outputFormat.mBytesPerFrame;
|
}
|
||||||
ioData.mBuffers[0].mDataByteSize = ioNumberFrames * outputFormat.mBytesPerFrame;
|
|
||||||
goto tryagain3;
|
ioNumberFrames = amount / outputFormat.mBytesPerFrame;
|
||||||
}
|
ioData.mBuffers[0].mData = dest;
|
||||||
else if (err != noErr) {
|
ioData.mBuffers[0].mDataByteSize = amount;
|
||||||
NSLog(@"Error: %i", err);
|
ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
|
||||||
}
|
ioData.mNumberBuffers = 1;
|
||||||
|
|
||||||
|
amountRead = 0;
|
||||||
|
|
||||||
|
tryagain2:
|
||||||
|
err = AudioConverterFillComplexBuffer(converter, ACFloatProc, self, &ioNumberFrames, &ioData, NULL);
|
||||||
|
amountRead += ioData.mBuffers[0].mDataByteSize;
|
||||||
|
if (err == 100)
|
||||||
|
{
|
||||||
|
NSLog(@"INSIZE: %i", amountRead);
|
||||||
|
ioData.mBuffers[0].mData = dest + amountRead;
|
||||||
|
ioNumberFrames = ( amount - amountRead ) / outputFormat.mBytesPerFrame;
|
||||||
|
ioData.mBuffers[0].mDataByteSize = ioNumberFrames * outputFormat.mBytesPerFrame;
|
||||||
|
goto tryagain2;
|
||||||
|
}
|
||||||
|
else if (err != noErr && err != kAudioConverterErr_InvalidInputSize)
|
||||||
|
{
|
||||||
|
NSLog(@"Error: %i", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
return amountRead;
|
return amountRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)registerObservers
|
||||||
|
{
|
||||||
|
NSLog(@"REGISTERING OBSERVERS");
|
||||||
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||||
|
ofObject:(id)object
|
||||||
|
change:(NSDictionary *)change
|
||||||
|
context:(void *)context
|
||||||
|
{
|
||||||
|
NSLog(@"SOMETHING CHANGED!");
|
||||||
|
if ([keyPath isEqual:@"values.volumeScaling"]) {
|
||||||
|
//User reset the volume scaling option
|
||||||
|
[self refreshVolumeScaling];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static float db_to_scale(float db)
|
||||||
|
{
|
||||||
|
return pow(10.0, db / 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)refreshVolumeScaling
|
||||||
|
{
|
||||||
|
if (rgInfo == nil)
|
||||||
|
{
|
||||||
|
volumeScale = 1.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString * scaling = [[NSUserDefaults standardUserDefaults] stringForKey:@"volumeScaling"];
|
||||||
|
BOOL useAlbum = [scaling hasPrefix:@"albumGain"];
|
||||||
|
BOOL useTrack = useAlbum || [scaling hasPrefix:@"trackGain"];
|
||||||
|
BOOL useVolume = useAlbum || useTrack || [scaling isEqualToString:@"volumeScale"];
|
||||||
|
BOOL usePeak = [scaling hasSuffix:@"WithPeak"];
|
||||||
|
float scale = 1.0;
|
||||||
|
float peak = 0.0;
|
||||||
|
if (useVolume) {
|
||||||
|
id pVolumeScale = [rgInfo objectForKey:@"volume"];
|
||||||
|
if (pVolumeScale != nil)
|
||||||
|
scale = [pVolumeScale floatValue];
|
||||||
|
}
|
||||||
|
if (useTrack) {
|
||||||
|
id trackGain = [rgInfo objectForKey:@"replayGainTrackGain"];
|
||||||
|
id trackPeak = [rgInfo objectForKey:@"replayGainTrackPeak"];
|
||||||
|
if (trackGain != nil)
|
||||||
|
scale = db_to_scale([trackGain floatValue]);
|
||||||
|
if (trackPeak != nil)
|
||||||
|
peak = [trackPeak floatValue];
|
||||||
|
}
|
||||||
|
if (useAlbum) {
|
||||||
|
id albumGain = [rgInfo objectForKey:@"replayGainAlbumGain"];
|
||||||
|
id albumPeak = [rgInfo objectForKey:@"replayGainAlbumPeak"];
|
||||||
|
if (albumGain != nil)
|
||||||
|
scale = db_to_scale([albumGain floatValue]);
|
||||||
|
if (albumPeak != nil)
|
||||||
|
peak = [albumPeak floatValue];
|
||||||
|
}
|
||||||
|
if (usePeak) {
|
||||||
|
if (scale * peak > 1.0)
|
||||||
|
scale = 1.0 / peak;
|
||||||
|
}
|
||||||
|
volumeScale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inf outputFormat:(AudioStreamBasicDescription)outf
|
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inf outputFormat:(AudioStreamBasicDescription)outf
|
||||||
{
|
{
|
||||||
//Make the converter
|
//Make the converter
|
||||||
|
@ -253,43 +305,40 @@ static OSStatus ACDownmixProc(AudioConverterRef inAudioConverter, UInt32* ioNumb
|
||||||
inputFormat = inf;
|
inputFormat = inf;
|
||||||
outputFormat = outf;
|
outputFormat = outf;
|
||||||
|
|
||||||
|
[self registerObservers];
|
||||||
|
|
||||||
|
floatFormat = inputFormat;
|
||||||
|
floatFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
||||||
|
floatFormat.mBitsPerChannel = 32;
|
||||||
|
floatFormat.mBytesPerFrame = (32/8)*floatFormat.mChannelsPerFrame;
|
||||||
|
floatFormat.mBytesPerPacket = floatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket;
|
||||||
|
|
||||||
|
stat = AudioConverterNew( &inputFormat, &floatFormat, &converterFloat );
|
||||||
|
if (stat != noErr)
|
||||||
|
{
|
||||||
|
NSLog(@"Error creating converter %i", stat);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
stat = AudioConverterNew ( &floatFormat, &outputFormat, &converter );
|
||||||
|
if (stat != noErr)
|
||||||
|
{
|
||||||
|
NSLog(@"Error creating converter %i", stat);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
if (inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2)
|
if (inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2)
|
||||||
{
|
{
|
||||||
downmixFormat = inputFormat;
|
|
||||||
downmixFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
|
||||||
downmixFormat.mBitsPerChannel = 32;
|
|
||||||
downmixFormat.mBytesPerFrame = (32/8)*downmixFormat.mChannelsPerFrame;
|
|
||||||
downmixFormat.mBytesPerPacket = downmixFormat.mBytesPerFrame * downmixFormat.mFramesPerPacket;
|
|
||||||
stat = AudioConverterNew( &inputFormat, &downmixFormat, &converter );
|
|
||||||
if (stat != noErr)
|
|
||||||
{
|
|
||||||
NSLog(@"Error creating converter %i", stat);
|
|
||||||
}
|
|
||||||
stat = AudioConverterNew ( &downmixFormat, &outputFormat, &converterDownmix );
|
|
||||||
if (stat != noErr)
|
|
||||||
{
|
|
||||||
NSLog(@"Error creating converter %i", stat);
|
|
||||||
}
|
|
||||||
|
|
||||||
SInt32 channelMap[2] = { 0, 1 };
|
SInt32 channelMap[2] = { 0, 1 };
|
||||||
|
|
||||||
stat = AudioConverterSetProperty(converterDownmix,kAudioConverterChannelMap,sizeof(channelMap),channelMap);
|
stat = AudioConverterSetProperty(converter,kAudioConverterChannelMap,sizeof(channelMap),channelMap);
|
||||||
if (stat != noErr)
|
if (stat != noErr)
|
||||||
{
|
{
|
||||||
NSLog(@"Error mapping channels %i", stat);
|
NSLog(@"Error mapping channels %i", stat);
|
||||||
|
return NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (inputFormat.mChannelsPerFrame == 1)
|
||||||
{
|
|
||||||
|
|
||||||
stat = AudioConverterNew ( &inputFormat, &outputFormat, &converter);
|
|
||||||
if (stat != noErr)
|
|
||||||
{
|
|
||||||
NSLog(@"Error creating converter %i", stat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputFormat.mChannelsPerFrame == 1)
|
|
||||||
{
|
{
|
||||||
SInt32 channelMap[2] = { 0, 0 };
|
SInt32 channelMap[2] = { 0, 0 };
|
||||||
|
|
||||||
|
@ -297,12 +346,15 @@ static OSStatus ACDownmixProc(AudioConverterRef inAudioConverter, UInt32* ioNumb
|
||||||
if (stat != noErr)
|
if (stat != noErr)
|
||||||
{
|
{
|
||||||
NSLog(@"Error mapping channels %i", stat);
|
NSLog(@"Error mapping channels %i", stat);
|
||||||
|
return NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintStreamDesc(&inf);
|
PrintStreamDesc(&inf);
|
||||||
PrintStreamDesc(&outf);
|
PrintStreamDesc(&outf);
|
||||||
|
|
||||||
|
[self refreshVolumeScaling];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,27 +379,40 @@ static OSStatus ACDownmixProc(AudioConverterRef inAudioConverter, UInt32* ioNumb
|
||||||
[self setupWithInputFormat:format outputFormat:outputFormat];
|
[self setupWithInputFormat:format outputFormat:outputFormat];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setRGInfo:(NSDictionary *)rgi
|
||||||
|
{
|
||||||
|
NSLog(@"Setting ReplayGain info");
|
||||||
|
[rgInfo release];
|
||||||
|
[rgi retain];
|
||||||
|
rgInfo = rgi;
|
||||||
|
[self refreshVolumeScaling];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)cleanUp
|
- (void)cleanUp
|
||||||
{
|
{
|
||||||
if (converterDownmix)
|
[rgInfo release];
|
||||||
|
rgInfo = nil;
|
||||||
|
if (converterFloat)
|
||||||
{
|
{
|
||||||
AudioConverterDispose(converterDownmix);
|
AudioConverterDispose(converterFloat);
|
||||||
converterDownmix = NULL;
|
converterFloat = NULL;
|
||||||
}
|
}
|
||||||
if (converter)
|
if (converter)
|
||||||
{
|
{
|
||||||
AudioConverterDispose(converter);
|
AudioConverterDispose(converter);
|
||||||
converter = NULL;
|
converter = NULL;
|
||||||
}
|
}
|
||||||
if (downmixBuffer)
|
if (floatBuffer)
|
||||||
{
|
{
|
||||||
free(downmixBuffer);
|
free(floatBuffer);
|
||||||
downmixBuffer = NULL;
|
floatBuffer = NULL;
|
||||||
}
|
}
|
||||||
if (callbackBuffer) {
|
if (callbackBuffer) {
|
||||||
free(callbackBuffer);
|
free(callbackBuffer);
|
||||||
callbackBuffer = NULL;
|
callbackBuffer = NULL;
|
||||||
}
|
}
|
||||||
|
floatOffset = 0;
|
||||||
|
floatSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -18,11 +18,9 @@
|
||||||
|
|
||||||
@interface InputNode : Node {
|
@interface InputNode : Node {
|
||||||
id<CogDecoder> decoder;
|
id<CogDecoder> decoder;
|
||||||
NSDictionary * rgInfo;
|
|
||||||
|
|
||||||
int bytesPerSample;
|
int bytesPerSample;
|
||||||
int bytesPerFrame;
|
int bytesPerFrame;
|
||||||
int volumeScale;
|
|
||||||
BOOL floatingPoint;
|
BOOL floatingPoint;
|
||||||
BOOL swapEndian;
|
BOOL swapEndian;
|
||||||
|
|
||||||
|
@ -41,10 +39,6 @@
|
||||||
|
|
||||||
- (BOOL)setTrack:(NSURL *)track;
|
- (BOOL)setTrack:(NSURL *)track;
|
||||||
|
|
||||||
- (void)setRGInfo:(NSDictionary *)rgi;
|
|
||||||
|
|
||||||
- (id<CogDecoder>) decoder;
|
- (id<CogDecoder>) decoder;
|
||||||
|
|
||||||
- (void)refreshVolumeScaling;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -12,15 +12,6 @@
|
||||||
#import "CoreAudioUtils.h"
|
#import "CoreAudioUtils.h"
|
||||||
|
|
||||||
|
|
||||||
static BOOL hostIsBigEndian()
|
|
||||||
{
|
|
||||||
#ifdef __BIG_ENDIAN__
|
|
||||||
return YES;
|
|
||||||
#else
|
|
||||||
return NO;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation InputNode
|
@implementation InputNode
|
||||||
|
|
||||||
- (BOOL)openWithSource:(id<CogSource>)source
|
- (BOOL)openWithSource:(id<CogSource>)source
|
||||||
|
@ -43,20 +34,7 @@ static BOOL hostIsBigEndian()
|
||||||
int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue];
|
int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue];
|
||||||
int channels = [[properties objectForKey:@"channels"] intValue];
|
int channels = [[properties objectForKey:@"channels"] intValue];
|
||||||
|
|
||||||
bytesPerSample = bitsPerSample / 8;
|
bytesPerFrame = (bitsPerSample / 8) * channels;
|
||||||
bytesPerFrame = bytesPerSample * channels;
|
|
||||||
|
|
||||||
if (([[properties objectForKey:@"endian"] isEqualToString:@"big"] && !hostIsBigEndian()) ||
|
|
||||||
([[properties objectForKey:@"endian"] isEqualToString:@"little"] && hostIsBigEndian())) {
|
|
||||||
swapEndian = YES;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
swapEndian = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
floatingPoint = [[properties objectForKey:@"floatingPoint"] boolValue];
|
|
||||||
|
|
||||||
[self refreshVolumeScaling];
|
|
||||||
|
|
||||||
shouldContinue = YES;
|
shouldContinue = YES;
|
||||||
shouldSeek = NO;
|
shouldSeek = NO;
|
||||||
|
@ -74,10 +52,7 @@ static BOOL hostIsBigEndian()
|
||||||
int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue];
|
int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue];
|
||||||
int channels = [[properties objectForKey:@"channels"] intValue];
|
int channels = [[properties objectForKey:@"channels"] intValue];
|
||||||
|
|
||||||
bytesPerSample = bitsPerSample / 8;
|
bytesPerFrame = (bitsPerSample / 8) * channels;
|
||||||
bytesPerFrame = bytesPerSample * channels;
|
|
||||||
|
|
||||||
[self refreshVolumeScaling];
|
|
||||||
|
|
||||||
[self registerObservers];
|
[self registerObservers];
|
||||||
|
|
||||||
|
@ -101,8 +76,6 @@ static BOOL hostIsBigEndian()
|
||||||
forKeyPath:@"metadata"
|
forKeyPath:@"metadata"
|
||||||
options:(NSKeyValueObservingOptionNew)
|
options:(NSKeyValueObservingOptionNew)
|
||||||
context:NULL];
|
context:NULL];
|
||||||
|
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||||
|
@ -116,104 +89,10 @@ static BOOL hostIsBigEndian()
|
||||||
//Inform something of properties change
|
//Inform something of properties change
|
||||||
//Disable support until it is properly implimented.
|
//Disable support until it is properly implimented.
|
||||||
//[controller inputFormatDidChange: propertiesToASBD([decoder properties])];
|
//[controller inputFormatDidChange: propertiesToASBD([decoder properties])];
|
||||||
[self refreshVolumeScaling];
|
|
||||||
}
|
}
|
||||||
else if ([keyPath isEqual:@"metadata"]) {
|
else if ([keyPath isEqual:@"metadata"]) {
|
||||||
//Inform something of metadata change
|
//Inform something of metadata change
|
||||||
}
|
}
|
||||||
else if ([keyPath isEqual:@"values.volumeScaling"]) {
|
|
||||||
//User reset the volume scaling option
|
|
||||||
[self refreshVolumeScaling];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static float db_to_scale(float db)
|
|
||||||
{
|
|
||||||
return pow(10.0, db / 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)refreshVolumeScaling
|
|
||||||
{
|
|
||||||
NSDictionary *properties = [decoder properties];
|
|
||||||
if (rgInfo != nil)
|
|
||||||
properties = rgInfo;
|
|
||||||
NSString * scaling = [[NSUserDefaults standardUserDefaults] stringForKey:@"volumeScaling"];
|
|
||||||
BOOL useAlbum = [scaling hasPrefix:@"albumGain"];
|
|
||||||
BOOL useTrack = useAlbum || [scaling hasPrefix:@"trackGain"];
|
|
||||||
BOOL useVolume = useAlbum || useTrack || [scaling isEqualToString:@"volumeScale"];
|
|
||||||
BOOL usePeak = [scaling hasSuffix:@"WithPeak"];
|
|
||||||
float scale = 1.0;
|
|
||||||
float peak = 0.0;
|
|
||||||
if (useVolume) {
|
|
||||||
id pVolumeScale = [properties objectForKey:@"volume"];
|
|
||||||
if (pVolumeScale != nil)
|
|
||||||
scale = [pVolumeScale floatValue];
|
|
||||||
}
|
|
||||||
if (useTrack) {
|
|
||||||
id trackGain = [properties objectForKey:@"replayGainTrackGain"];
|
|
||||||
id trackPeak = [properties objectForKey:@"replayGainTrackPeak"];
|
|
||||||
if (trackGain != nil)
|
|
||||||
scale = db_to_scale([trackGain floatValue]);
|
|
||||||
if (trackPeak != nil)
|
|
||||||
peak = [trackPeak floatValue];
|
|
||||||
}
|
|
||||||
if (useAlbum) {
|
|
||||||
id albumGain = [properties objectForKey:@"replayGainAlbumGain"];
|
|
||||||
id albumPeak = [properties objectForKey:@"replayGainAlbumPeak"];
|
|
||||||
if (albumGain != nil)
|
|
||||||
scale = db_to_scale([albumGain floatValue]);
|
|
||||||
if (albumPeak != nil)
|
|
||||||
peak = [albumPeak floatValue];
|
|
||||||
}
|
|
||||||
if (usePeak) {
|
|
||||||
if (scale * peak > 1.0)
|
|
||||||
scale = 1.0 / peak;
|
|
||||||
}
|
|
||||||
volumeScale = scale * 4096;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int16_t swap_16(uint16_t input)
|
|
||||||
{
|
|
||||||
return (input >> 8) | (input << 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t swap_24(uint32_t input)
|
|
||||||
{
|
|
||||||
int32_t temp = (input << 24) >> 8;
|
|
||||||
return temp | ((input >> 16) & 0xff) | (input & 0xff00);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t swap_32(uint32_t input)
|
|
||||||
{
|
|
||||||
return (input >> 24) | ((input >> 8) & 0xff00) | ((input << 8) & 0xff0000) | (input << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
static float swap_32f(float input)
|
|
||||||
{
|
|
||||||
union {
|
|
||||||
float f;
|
|
||||||
int32_t i;
|
|
||||||
} val;
|
|
||||||
val.f = input;
|
|
||||||
val.i = swap_32(val.i);
|
|
||||||
return val.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int64_t swap_64(uint64_t input)
|
|
||||||
{
|
|
||||||
return (input >> 56) | ((input >> 40) & 0xff00) | ((input >> 24) & 0xff0000) | ((input >> 8) & 0xff000000) |
|
|
||||||
((input << 8) & 0xff00000000) | ((input << 24) & 0xff0000000000) | ((input << 40) & 0xff000000000000) | (input << 56);
|
|
||||||
}
|
|
||||||
|
|
||||||
static double swap_64f(double input)
|
|
||||||
{
|
|
||||||
union {
|
|
||||||
double f;
|
|
||||||
int64_t i;
|
|
||||||
} val;
|
|
||||||
val.f = input;
|
|
||||||
val.i = swap_64(val.i);
|
|
||||||
return val.f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)process
|
- (void)process
|
||||||
|
@ -256,123 +135,6 @@ static double swap_64f(double input)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (volumeScale != 4096) {
|
|
||||||
int totalFrames = amountInBuffer / bytesPerSample;
|
|
||||||
switch (bytesPerSample) {
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
uint8_t * samples = (uint8_t *)inputBuffer;
|
|
||||||
for (int i = 0; i < totalFrames; i++)
|
|
||||||
{
|
|
||||||
int32_t sample = (int8_t)samples[i] - 128;
|
|
||||||
sample = (sample * volumeScale) >> 12;
|
|
||||||
if ((unsigned)(sample + 0x80) & 0xffffff00) sample = (sample >> 31) ^ 0x7f;
|
|
||||||
samples[i] = sample + 128;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
int16_t * samples = (int16_t *)inputBuffer;
|
|
||||||
for (int i = 0; i < totalFrames; i++)
|
|
||||||
{
|
|
||||||
int32_t sample = samples[i];
|
|
||||||
if (swapEndian) sample = swap_16(sample);
|
|
||||||
sample = (sample * volumeScale) >> 12;
|
|
||||||
if ((unsigned)(sample + 0x8000) & 0xffff0000) sample = (sample >> 31) ^ 0x7fff;
|
|
||||||
if (swapEndian) sample = swap_16(sample);
|
|
||||||
samples[i] = sample;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
{
|
|
||||||
uint8_t * samples = (uint8_t *)inputBuffer;
|
|
||||||
for (int i = 0; i < totalFrames; i++)
|
|
||||||
{
|
|
||||||
int32_t sample = (samples[i * 3] << 8) | (samples[i * 3 + 1] << 16) | (samples[i * 3 + 2] << 24);
|
|
||||||
sample >>= 8;
|
|
||||||
if (swapEndian) sample = swap_24(sample);
|
|
||||||
sample = (sample * volumeScale) >> 12;
|
|
||||||
if ((unsigned)(sample + 0x800000) & 0xff000000) sample = (sample >> 31) ^ 0x7fffff;
|
|
||||||
if (swapEndian) sample = swap_24(sample);
|
|
||||||
samples[i * 3] = sample;
|
|
||||||
samples[i * 3 + 1] = sample >> 8;
|
|
||||||
samples[i * 3 + 2] = sample >> 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
if (floatingPoint)
|
|
||||||
{
|
|
||||||
float * samples = (float *)inputBuffer;
|
|
||||||
float scale = (float)volumeScale / 4096;
|
|
||||||
for (int i = 0; i < totalFrames; i++)
|
|
||||||
{
|
|
||||||
float sample = samples[i];
|
|
||||||
if (swapEndian) sample = swap_32f(sample);
|
|
||||||
sample *= scale;
|
|
||||||
if (swapEndian) sample = swap_32f(sample);
|
|
||||||
samples[i] = sample;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int32_t * samples = (int32_t *)inputBuffer;
|
|
||||||
for (int i = 0; i < totalFrames; i++)
|
|
||||||
{
|
|
||||||
int64_t sample = samples[i];
|
|
||||||
if (swapEndian) sample = swap_32(sample);
|
|
||||||
sample = (sample * volumeScale) >> 12;
|
|
||||||
if ((unsigned)(sample + 0x80000000) & 0xffffffff00000000) sample = (sample >> 63) ^ 0x7fffffff;
|
|
||||||
if (swapEndian) sample = swap_32(sample);
|
|
||||||
samples[i] = sample;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8:
|
|
||||||
if (floatingPoint)
|
|
||||||
{
|
|
||||||
double * samples = (double *)inputBuffer;
|
|
||||||
double scale = (double)volumeScale / 4096;
|
|
||||||
for (int i = 0; i < totalFrames; i++)
|
|
||||||
{
|
|
||||||
double sample = samples[i];
|
|
||||||
if (swapEndian) sample = swap_64f(sample);
|
|
||||||
sample *= scale;
|
|
||||||
if (swapEndian) sample = swap_64f(sample);
|
|
||||||
samples[i] = sample;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int64_t * samples = (int64_t *)inputBuffer;
|
|
||||||
for (int i = 0; i < totalFrames; i++)
|
|
||||||
{
|
|
||||||
int64_t sample = samples[i];
|
|
||||||
if (swapEndian) sample = swap_64(sample);
|
|
||||||
int64_t high_part = sample >> (32 + 12);
|
|
||||||
int64_t low_part = (sample & 0xffffffff) | (sample >> 31);
|
|
||||||
high_part *= volumeScale;
|
|
||||||
low_part = (low_part * volumeScale) >> 12;
|
|
||||||
if (((uint64_t)low_part + 0x100000000) & 0xfffffffe00000000)
|
|
||||||
high_part += low_part >> 32;
|
|
||||||
if (((uint64_t)high_part + 0x80000000) & 0xffffffff00000000)
|
|
||||||
sample = high_part >> 63;
|
|
||||||
else
|
|
||||||
sample = (high_part << 32) | (low_part & 0xffffffff);
|
|
||||||
if (swapEndian) sample = swap_64(sample);
|
|
||||||
samples[i] = sample;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[self writeData:inputBuffer amount:amountInBuffer];
|
[self writeData:inputBuffer amount:amountInBuffer];
|
||||||
amountInBuffer = 0;
|
amountInBuffer = 0;
|
||||||
}
|
}
|
||||||
|
@ -402,20 +164,10 @@ static double swap_64f(double input)
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setRGInfo:(NSDictionary *)i
|
|
||||||
{
|
|
||||||
[i retain];
|
|
||||||
[rgInfo release];
|
|
||||||
rgInfo = i;
|
|
||||||
[self refreshVolumeScaling];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
NSLog(@"Input Node dealloc");
|
NSLog(@"Input Node dealloc");
|
||||||
|
|
||||||
[rgInfo release];
|
|
||||||
|
|
||||||
[decoder removeObserver:self forKeyPath:@"properties"];
|
[decoder removeObserver:self forKeyPath:@"properties"];
|
||||||
[decoder removeObserver:self forKeyPath:@"metadata"];
|
[decoder removeObserver:self forKeyPath:@"metadata"];
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue