Implemented ReplayGain support, so far only in the HighlyComplete component.

CQTexperiment
Chris Moeller 2013-10-01 23:00:16 -07:00
parent 5d141f1d70
commit 6ea103b1c3
8 changed files with 634 additions and 2567 deletions

View File

@ -359,6 +359,8 @@ increase/decrease as long as the user holds the left/right, plus/minus button */
[userDefaultsValuesDict setObject:@"clearAndPlay" forKey:@"openingFilesBehavior"];
[userDefaultsValuesDict setObject:@"enqueue" forKey:@"openingFilesAlteredBehavior"];
[userDefaultsValuesDict setObject:@"albumGainWithPeak" forKey:@"volumeScaling"];
//Register and sync defaults
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];

View File

@ -19,7 +19,10 @@
@interface InputNode : Node {
id<CogDecoder> decoder;
int bytesPerSample;
int bytesPerFrame;
int volumeScale;
BOOL swapEndian;
BOOL shouldSeek;
long seekFrame;
@ -38,4 +41,6 @@
- (id<CogDecoder>) decoder;
- (void)refreshVolumeScaling;
@end

View File

@ -11,6 +11,16 @@
#import "Plugin.h"
#import "CoreAudioUtils.h"
static BOOL hostIsBigEndian()
{
#ifdef __BIG_ENDIAN__
return YES;
#else
return NO;
#endif
}
@implementation InputNode
- (BOOL)openWithSource:(id<CogSource>)source
@ -33,7 +43,18 @@
int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue];
int channels = [[properties objectForKey:@"channels"] intValue];
bytesPerFrame = (bitsPerSample/8) * channels;
bytesPerSample = bitsPerSample / 8;
bytesPerFrame = bytesPerSample * channels;
if (([[properties objectForKey:@"endian"] isEqualToString:@"big"] && !hostIsBigEndian()) ||
([[properties objectForKey:@"endian"] isEqualToString:@"little"] && hostIsBigEndian())) {
swapEndian = YES;
}
else {
swapEndian = NO;
}
[self refreshVolumeScaling];
shouldContinue = YES;
shouldSeek = NO;
@ -51,7 +72,10 @@
int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue];
int channels = [[properties objectForKey:@"channels"] intValue];
bytesPerFrame = (bitsPerSample/8) * channels;
bytesPerSample = bitsPerSample / 8;
bytesPerFrame = bytesPerSample * channels;
[self refreshVolumeScaling];
[self registerObservers];
@ -75,6 +99,8 @@
forKeyPath:@"metadata"
options:(NSKeyValueObservingOptionNew)
context:NULL];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
@ -88,10 +114,74 @@
//Inform something of properties change
//Disable support until it is properly implimented.
//[controller inputFormatDidChange: propertiesToASBD([decoder properties])];
[self refreshVolumeScaling];
}
else if ([keyPath isEqual:@"metadata"]) {
//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];
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);
}
- (void)process
@ -133,6 +223,72 @@
NSLog(@"closing? is %i", shouldClose);
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:
{
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;
}
}
[self writeData:inputBuffer amount:amountInBuffer];
amountInBuffer = 0;

View File

@ -32,6 +32,12 @@
NSNumber *year;
NSNumber *track;
NSImage *albumArt;
float replayGainAlbumGain;
float replayGainAlbumPeak;
float replayGainTrackGain;
float replayGainTrackPeak;
float volume;
long long totalFrames;
int bitrate;
@ -91,6 +97,12 @@
@property int bitsPerSample;
@property float sampleRate;
@property float replayGainAlbumGain;
@property float replayGainAlbumPeak;
@property float replayGainTrackGain;
@property float replayGainTrackPeak;
@property float volume;
@property(retain) NSString *endian;
@property BOOL seekable;

View File

@ -20,7 +20,11 @@
int tagLengthMs;
int tagFadeMs;
int volumeScale;
float replayGainAlbumGain;
float replayGainAlbumPeak;
float replayGainTrackGain;
float replayGainTrackPeak;
float volume;
int type;

View File

@ -182,7 +182,11 @@ struct psf_info_meta_state
int tag_length_ms;
int tag_fade_ms;
int volume_scale;
float albumGain;
float albumPeak;
float trackGain;
float trackPeak;
float volume;
};
static int parse_time_crap(NSString * value)
@ -205,16 +209,6 @@ static int parse_time_crap(NSString * value)
return totalSeconds;
}
static int db_to_int(NSString * value)
{
return pow(10.0, [value floatValue] / 20) * 4096;
}
static int scale_to_int(NSString * value)
{
return [value floatValue] * 4096;
}
static int psf_info_meta(void * context, const char * name, const char * value)
{
struct psf_info_meta_state * state = ( struct psf_info_meta_state * ) context;
@ -237,19 +231,24 @@ static int psf_info_meta(void * context, const char * name, const char * value)
if ([taglc hasPrefix:@"replaygain_"])
{
if ([taglc isEqualToString:@"replaygain_album_gain"])
if ([taglc hasPrefix:@"replaygain_album_"])
{
state->volume_scale = db_to_int(svalue);
if ([taglc hasSuffix:@"gain"])
state->albumGain = [svalue floatValue];
else if ([taglc hasSuffix:@"peak"])
state->albumPeak = [svalue floatValue];
}
else if (!state->volume_scale && [taglc isEqualToString:@"replaygain_track_gain"])
else if ([taglc hasPrefix:@"replaygain_track_"])
{
state->volume_scale = db_to_int(svalue);
if ([taglc hasSuffix:@"gain"])
state->trackGain = [svalue floatValue];
else if ([taglc hasSuffix:@"peak"])
state->trackPeak = [svalue floatValue];
}
}
else if ([taglc isEqualToString:@"volume"])
{
if (!state->volume_scale)
state->volume_scale = scale_to_int(svalue);
state->volume = [svalue floatValue];
}
else if ([taglc isEqualToString:@"length"])
{
@ -763,8 +762,13 @@ struct gsf_sound_out : public GBASoundOut
info.utf8 = false;
info.tag_length_ms = 0;
info.tag_fade_ms = 0;
info.volume_scale = 0;
info.albumGain = 0;
info.albumPeak = 0;
info.trackGain = 0;
info.trackPeak = 0;
info.volume = 1;
currentUrl = [[[[source url] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] retain];
[[psf_file_container instance] add_hint:currentUrl source:currentSource];
@ -784,7 +788,12 @@ struct gsf_sound_out : public GBASoundOut
tagLengthMs = info.tag_length_ms;
tagFadeMs = info.tag_fade_ms;
volumeScale = info.volume_scale;
replayGainAlbumGain = info.albumGain;
replayGainAlbumPeak = info.albumPeak;
replayGainTrackGain = info.trackGain;
replayGainTrackPeak = info.trackPeak;
volume = info.volume;
metadataList = info.info;
@ -848,18 +857,6 @@ struct gsf_sound_out : public GBASoundOut
qsound_execute( emulatorCore, 0x7fffffff, ( int16_t * ) buf, &howmany);
frames = howmany;
}
if ( volumeScale )
{
int16_t * samples = ( int16_t * ) buf;
for ( UInt32 i = 0, j = frames * 2; i < j; ++i )
{
int sample = ( samples[ i ] * volumeScale ) >> 12;
if ( (uint32_t)(sample + 0x8000) & 0xffff0000 ) sample = ( sample >> 31 ) ^ 0x7fff;
samples[ i ] = ( int16_t ) sample;
}
}
framesRead += frames;
@ -968,6 +965,11 @@ struct gsf_sound_out : public GBASoundOut
[NSNumber numberWithInteger:totalFrames], @"totalFrames",
[NSNumber numberWithInt:0], @"bitrate",
[NSNumber numberWithBool:YES], @"seekable",
[NSNumber numberWithFloat:replayGainAlbumGain], @"replayGainAlbumGain",
[NSNumber numberWithFloat:replayGainAlbumPeak], @"replayGainAlbumPeak",
[NSNumber numberWithFloat:replayGainTrackGain], @"replayGainTrackGain",
[NSNumber numberWithFloat:replayGainTrackPeak], @"replayGainTrackPeak",
[NSNumber numberWithFloat:volume], @"volume",
@"host", @"endian",
nil];
}
@ -980,7 +982,6 @@ struct gsf_sound_out : public GBASoundOut
info.utf8 = false;
info.tag_length_ms = 0;
info.tag_fade_ms = 0;
info.volume_scale = 0;
NSString * decodedUrl = [[url absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
17E41DB80C130AA500AC744D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 17E41DB70C130AA500AC744D /* Localizable.strings */; };
17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E78A7D0D68BE3C005C5A59 /* file_tree.png */; };
17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17E78B680D68C1E3005C5A59 /* Preferences.xib */; };
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */; };
8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; };
8E07AA880AAC8EA200A4B32F /* HotKeyPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E07AA810AAC8EA200A4B32F /* HotKeyPane.m */; };
8E07AA890AAC8EA200A4B32F /* GeneralPreferencePane.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E07AA830AAC8EA200A4B32F /* GeneralPreferencePane.m */; };
@ -53,6 +54,8 @@
17E78A7D0D68BE3C005C5A59 /* file_tree.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = file_tree.png; path = Icons/file_tree.png; sourceTree = "<group>"; };
17E78B690D68C1E3005C5A59 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/Preferences.xib; sourceTree = "<group>"; };
32DBCF630370AF2F00C91783 /* General_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = General_Prefix.pch; sourceTree = "<group>"; };
83EF495D17FBC96A00642E3C /* VolumeBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VolumeBehaviorArrayController.h; sourceTree = "<group>"; };
83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VolumeBehaviorArrayController.m; sourceTree = "<group>"; };
8D5B49B6048680CD000E48DA /* General.preferencePane */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = General.preferencePane; sourceTree = BUILT_PRODUCTS_DIR; };
8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8E07AA800AAC8EA200A4B32F /* HotKeyPane.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = HotKeyPane.h; sourceTree = "<group>"; };
@ -180,6 +183,8 @@
17C643360B8A77CC00C53518 /* OutputsArrayController.m */,
99F1813D0DE01D7A00FD5FFB /* PlaylistBehaviorArrayController.h */,
99F1813E0DE01D7A00FD5FFB /* PlaylistBehaviorArrayController.m */,
83EF495D17FBC96A00642E3C /* VolumeBehaviorArrayController.h */,
83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */,
);
name = Custom;
sourceTree = "<group>";
@ -296,6 +301,7 @@
8E6C12160AACAE4100819171 /* NDHotKeyControl.m in Sources */,
8E6C12170AACAE4100819171 /* NDHotKeyEvent.m in Sources */,
8E6C13A00AACBAB500819171 /* HotKeyControl.m in Sources */,
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */,
17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */,
17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */,
170744AD0BFF3938002475C9 /* AppcastArrayController.m in Sources */,