Implemented ReplayGain support, so far only in the HighlyComplete component.
parent
5d141f1d70
commit
6ea103b1c3
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -20,7 +20,11 @@
|
|||
int tagLengthMs;
|
||||
int tagFadeMs;
|
||||
|
||||
int volumeScale;
|
||||
float replayGainAlbumGain;
|
||||
float replayGainAlbumPeak;
|
||||
float replayGainTrackGain;
|
||||
float replayGainTrackPeak;
|
||||
float volume;
|
||||
|
||||
int type;
|
||||
|
||||
|
|
|
@ -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
|
@ -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 */,
|
||||
|
|
Loading…
Reference in New Issue