More ReplayGain support, now with metadata handling, but only if the metadata is already loaded

CQTexperiment
Chris Moeller 2013-10-02 02:30:04 -07:00
parent 6ea103b1c3
commit 4c0cf34250
25 changed files with 618 additions and 11 deletions

View File

@ -14,6 +14,7 @@ extern NSString *CogPlaybackDidPauseNotficiation;
extern NSString *CogPlaybackDidResumeNotficiation; extern NSString *CogPlaybackDidResumeNotficiation;
extern NSString *CogPlaybackDidStopNotficiation; extern NSString *CogPlaybackDidStopNotficiation;
extern NSDictionary * makeRGInfo(PlaylistEntry *pe);
@class PlaylistController; @class PlaylistController;
@class PlaylistView; @class PlaylistView;

View File

@ -117,6 +117,22 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
[self playEntryAtIndex:[playlistView selectedRow]]; [self playEntryAtIndex:[playlistView selectedRow]];
} }
NSDictionary * makeRGInfo(PlaylistEntry *pe)
{
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
if ([pe replayGainAlbumGain] != 0)
[dictionary setObject:[NSNumber numberWithFloat:[pe replayGainAlbumGain]] forKey:@"replayGainAlbumGain"];
if ([pe replayGainAlbumPeak] != 0)
[dictionary setObject:[NSNumber numberWithFloat:[pe replayGainAlbumPeak]] forKey:@"replayGainAlbumPeak"];
if ([pe replayGainTrackGain] != 0)
[dictionary setObject:[NSNumber numberWithFloat:[pe replayGainTrackGain]] forKey:@"replayGainTrackGain"];
if ([pe replayGainTrackPeak] != 0)
[dictionary setObject:[NSNumber numberWithFloat:[pe replayGainTrackPeak]] forKey:@"replayGainTrackPeak"];
if ([pe volume] != 1)
[dictionary setObject:[NSNumber numberWithFloat:[pe volume]] forKey:@"volume"];
return dictionary;
}
- (void)playEntry:(PlaylistEntry *)pe - (void)playEntry:(PlaylistEntry *)pe
{ {
if (playbackStatus != kCogStatusStopped) if (playbackStatus != kCogStatusStopped)
@ -130,7 +146,7 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
if (pe == nil) if (pe == nil)
return; return;
[audioPlayer play:[pe URL] withUserInfo:pe]; [audioPlayer play:[pe URL] withUserInfo:pe withRGInfo:makeRGInfo(pe)];
} }
- (IBAction)next:(id)sender - (IBAction)next:(id)sender
@ -430,7 +446,7 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
else else
pe = [playlistController getNextEntry:curEntry]; pe = [playlistController getNextEntry:curEntry];
[player setNextStream:[pe URL] withUserInfo:pe]; [player setNextStream:[pe URL] withUserInfo:pe withRGInfo:makeRGInfo(pe)];
} }
- (void)audioPlayer:(AudioPlayer *)player didBeginStream:(id)userInfo - (void)audioPlayer:(AudioPlayer *)player didBeginStream:(id)userInfo

View File

@ -22,6 +22,7 @@
NSURL *nextStream; NSURL *nextStream;
id nextStreamUserInfo; id nextStreamUserInfo;
NSDictionary *nextStreamRGInfo;
id delegate; id delegate;
@ -35,7 +36,7 @@
- (id)delegate; - (id)delegate;
- (void)play:(NSURL *)url; - (void)play:(NSURL *)url;
- (void)play:(NSURL *)url withUserInfo:(id)userInfo; - (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi;
- (void)stop; - (void)stop;
- (void)pause; - (void)pause;
@ -50,9 +51,11 @@
- (double)amountPlayed; - (double)amountPlayed;
- (void)setNextStream:(NSURL *)url; - (void)setNextStream:(NSURL *)url;
- (void)setNextStream:(NSURL *)url withUserInfo:(id)userInfo; - (void)setNextStream:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi;
- (void)resetNextStreams; - (void)resetNextStreams;
- (void)setRGInfo:(NSDictionary *)rgi;
+ (NSArray *)fileTypes; + (NSArray *)fileTypes;
+ (NSArray *)schemes; + (NSArray *)schemes;
+ (NSArray *)containerTypes; + (NSArray *)containerTypes;

View File

@ -43,10 +43,10 @@
- (void)play:(NSURL *)url - (void)play:(NSURL *)url
{ {
[self play:url withUserInfo:nil]; [self play:url withUserInfo:nil withRGInfo:nil];
} }
- (void)play:(NSURL *)url withUserInfo:(id)userInfo - (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi
{ {
if (output) if (output)
{ {
@ -91,6 +91,7 @@
} }
userInfo = nextStreamUserInfo; userInfo = nextStreamUserInfo;
rgi = nextStreamRGInfo;
[self notifyStreamChanged:userInfo]; [self notifyStreamChanged:userInfo];
@ -98,6 +99,7 @@
} }
[bufferChain setUserInfo:userInfo]; [bufferChain setUserInfo:userInfo];
[bufferChain setRGInfo:rgi];
[self setShouldContinue:YES]; [self setShouldContinue:YES];
@ -152,10 +154,10 @@
//This is called by the delegate DURING a requestNextStream request. //This is called by the delegate DURING a requestNextStream request.
- (void)setNextStream:(NSURL *)url - (void)setNextStream:(NSURL *)url
{ {
[self setNextStream:url withUserInfo:nil]; [self setNextStream:url withUserInfo:nil withRGInfo:nil];
} }
- (void)setNextStream:(NSURL *)url withUserInfo:(id)userInfo - (void)setNextStream:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi
{ {
[url retain]; [url retain];
[nextStream release]; [nextStream release];
@ -165,6 +167,9 @@
[nextStreamUserInfo release]; [nextStreamUserInfo release];
nextStreamUserInfo = userInfo; nextStreamUserInfo = userInfo;
[rgi retain];
[nextStreamRGInfo release];
nextStreamRGInfo = rgi;
} }
// Called when the playlist changed before we actually started playing a requested stream. We will re-request. // Called when the playlist changed before we actually started playing a requested stream. We will re-request.
@ -212,7 +217,7 @@
- (void)notifyStreamChanged:(id)userInfo - (void)notifyStreamChanged:(id)userInfo
{ {
[self sendDelegateMethod:@selector(audioPlayer:didBeginStream:) withObject:userInfo waitUntilDone:NO]; [self sendDelegateMethod:@selector(audioPlayer:didBeginStream:) withObject:userInfo waitUntilDone:YES];
} }
- (void)addChainToQueue:(BufferChain *)newChain - (void)addChainToQueue:(BufferChain *)newChain
@ -233,6 +238,9 @@
nextStreamUserInfo = [sender userInfo]; nextStreamUserInfo = [sender userInfo];
[nextStreamUserInfo retain]; //Retained because when setNextStream is called, it will be released!!! [nextStreamUserInfo retain]; //Retained because when setNextStream is called, it will be released!!!
nextStreamRGInfo = [sender rgInfo];
[nextStreamRGInfo retain];
[self requestNextStream: nextStreamUserInfo]; [self requestNextStream: nextStreamUserInfo];
newChain = [[BufferChain alloc] initWithController:self]; newChain = [[BufferChain alloc] initWithController:self];

View File

@ -18,6 +18,7 @@
NSURL *streamURL; NSURL *streamURL;
id userInfo; id userInfo;
NSDictionary *rgInfo;
id finalNode; //Final buffer in the chain. id finalNode; //Final buffer in the chain.
@ -43,6 +44,9 @@
- (id)userInfo; - (id)userInfo;
- (void)setUserInfo:(id)i; - (void)setUserInfo:(id)i;
- (NSDictionary*)rgInfo;
- (void)setRGInfo:(NSDictionary *)rgi;
- (NSURL *)streamURL; - (NSURL *)streamURL;
- (void)setStreamURL:(NSURL *)url; - (void)setStreamURL:(NSURL *)url;

View File

@ -21,6 +21,7 @@
controller = c; controller = c;
streamURL = nil; streamURL = nil;
userInfo = nil; userInfo = nil;
rgInfo = nil;
inputNode = nil; inputNode = nil;
converterNode = nil; converterNode = nil;
@ -100,8 +101,22 @@
return userInfo; return userInfo;
} }
- (void)setRGInfo:(NSDictionary *)rgi
{
[rgi retain];
[rgInfo release];
rgInfo = rgi;
[inputNode setRGInfo:rgi];
}
- (NSDictionary *)rgInfo
{
return rgInfo;
}
- (void)dealloc - (void)dealloc
{ {
[rgInfo release];
[userInfo release]; [userInfo release];
[streamURL release]; [streamURL release];

View File

@ -18,6 +18,7 @@
@interface InputNode : Node { @interface InputNode : Node {
id<CogDecoder> decoder; id<CogDecoder> decoder;
NSDictionary * rgInfo;
int bytesPerSample; int bytesPerSample;
int bytesPerFrame; int bytesPerFrame;
@ -39,6 +40,8 @@
- (BOOL)setTrack:(NSURL *)track; - (BOOL)setTrack:(NSURL *)track;
- (void)setRGInfo:(NSDictionary *)rgi;
- (id<CogDecoder>) decoder; - (id<CogDecoder>) decoder;
- (void)refreshVolumeScaling; - (void)refreshVolumeScaling;

View File

@ -133,11 +133,13 @@ static float db_to_scale(float db)
- (void)refreshVolumeScaling - (void)refreshVolumeScaling
{ {
NSDictionary *properties = [decoder properties]; NSDictionary *properties = [decoder properties];
if (rgInfo != nil)
properties = rgInfo;
NSString * scaling = [[NSUserDefaults standardUserDefaults] stringForKey:@"volumeScaling"]; NSString * scaling = [[NSUserDefaults standardUserDefaults] stringForKey:@"volumeScaling"];
BOOL useAlbum = [scaling hasPrefix:@"albumGain"]; BOOL useAlbum = [scaling hasPrefix:@"albumGain"];
BOOL useTrack = useAlbum || [scaling hasPrefix:@"trackGain"]; BOOL useTrack = useAlbum || [scaling hasPrefix:@"trackGain"];
BOOL useVolume = useAlbum || useTrack || [scaling isEqualToString:@"volumeScale"]; BOOL useVolume = useAlbum || useTrack || [scaling isEqualToString:@"volumeScale"];
bool usePeak = [scaling hasSuffix:@"WithPeak"]; BOOL usePeak = [scaling hasSuffix:@"WithPeak"];
float scale = 1.0; float scale = 1.0;
float peak = 0.0; float peak = 0.0;
if (useVolume) { if (useVolume) {
@ -319,10 +321,20 @@ static int32_t swap_32(uint32_t 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"];

View File

@ -133,6 +133,34 @@ TagLib::uint APE::Tag::track() const
return d->itemListMap["TRACK"].toString().toInt(); return d->itemListMap["TRACK"].toString().toInt();
} }
float APE::Tag::rgAlbumGain() const
{
if (d->itemListMap["REPLAYGAIN_ALBUM_GAIN"].isEmpty())
return 0;
return d->itemListMap["REPLAYGAIN_ALBUM_GAIN"].toString().toFloat();
}
float APE::Tag::rgAlbumPeak() const
{
if (d->itemListMap["REPLAYGAIN_ALBUM_PEAK"].isEmpty())
return 0;
return d->itemListMap["REPLAYGAIN_ALBUM_PEAK"].toString().toFloat();
}
float APE::Tag::rgTrackGain() const
{
if (d->itemListMap["REPLAYGAIN_TRACK_GAIN"].isEmpty())
return 0;
return d->itemListMap["REPLAYGAIN_TRACK_GAIN"].toString().toFloat();
}
float APE::Tag::rgTrackPeak() const
{
if (d->itemListMap["REPLAYGAIN_TRACK_PEAK"].isEmpty())
return 0;
return d->itemListMap["REPLAYGAIN_TRACK_PEAK"].toString().toFloat();
}
void APE::Tag::setTitle(const String &s) void APE::Tag::setTitle(const String &s)
{ {
addValue("TITLE", s, true); addValue("TITLE", s, true);
@ -174,6 +202,38 @@ void APE::Tag::setTrack(uint i)
addValue("TRACK", String::number(i), true); addValue("TRACK", String::number(i), true);
} }
void APE::Tag::setRGAlbumGain(float f)
{
if (f == 0)
removeItem("REPLAYGAIN_ALBUM_GAIN");
else
addValue("REPLAYGAIN_ALBUM_GAIN", String::number(f) + " dB", true);
}
void APE::Tag::setRGAlbumPeak(float f)
{
if (f == 0)
removeItem("REPLAYGAIN_ALBUM_PEAK");
else
addValue("REPLAYGAIN_ALBUM_PEAK", String::number(f), true);
}
void APE::Tag::setRGTrackGain(float f)
{
if (f == 0)
removeItem("REPLAYGAIN_TRACK_GAIN");
else
addValue("REPLAYGAIN_TRACK_GAIN", String::number(f) + " dB", true);
}
void APE::Tag::setRGTrackPeak(float f)
{
if (f == 0)
removeItem("REPLAYGAIN_TRACK_PEAK");
else
addValue("REPLAYGAIN_TRACK_PEAK", String::number(f), true);
}
APE::Footer *APE::Tag::footer() const APE::Footer *APE::Tag::footer() const
{ {
return &d->footer; return &d->footer;

View File

@ -94,6 +94,10 @@ namespace TagLib {
virtual String genre() const; virtual String genre() const;
virtual uint year() const; virtual uint year() const;
virtual uint track() const; virtual uint track() const;
virtual float rgAlbumGain() const;
virtual float rgAlbumPeak() const;
virtual float rgTrackGain() const;
virtual float rgTrackPeak() const;
virtual void setTitle(const String &s); virtual void setTitle(const String &s);
virtual void setArtist(const String &s); virtual void setArtist(const String &s);
@ -102,6 +106,10 @@ namespace TagLib {
virtual void setGenre(const String &s); virtual void setGenre(const String &s);
virtual void setYear(uint i); virtual void setYear(uint i);
virtual void setTrack(uint i); virtual void setTrack(uint i);
virtual void setRGAlbumGain(float f);
virtual void setRGAlbumPeak(float f);
virtual void setRGTrackGain(float f);
virtual void setRGTrackPeak(float f);
/*! /*!
* Returns a pointer to the tag's footer. * Returns a pointer to the tag's footer.

View File

@ -19,6 +19,10 @@ public:
TagLib::uint disk; TagLib::uint disk;
TagLib::uint numDisks; TagLib::uint numDisks;
TagLib::uint bpm; TagLib::uint bpm;
float rgAlbumGain;
float rgAlbumPeak;
float rgTrackGain;
float rgTrackPeak;
bool isEmpty; bool isEmpty;
TagLib::ByteVector cover; TagLib::ByteVector cover;
}; };
@ -76,6 +80,26 @@ TagLib::uint MP4::Tag::track() const
return d->track; return d->track;
} }
float MP4::Tag::rgAlbumGain() const
{
return d->rgAlbumGain;
}
float MP4::Tag::rgAlbumPeak() const
{
return d->rgAlbumPeak;
}
float MP4::Tag::rgTrackGain() const
{
return d->rgTrackGain;
}
float MP4::Tag::rgTrackPeak() const
{
return d->rgTrackPeak;
}
TagLib::uint MP4::Tag::numTracks() const TagLib::uint MP4::Tag::numTracks() const
{ {
return d->numTracks; return d->numTracks;
@ -153,6 +177,30 @@ void MP4::Tag::setTrack(TagLib::uint i)
d->isEmpty = false; d->isEmpty = false;
} }
void MP4::Tag::setRGAlbumGain(float f)
{
d->rgAlbumGain = f;
d->isEmpty = false;
}
void MP4::Tag::setRGAlbumPeak(float f)
{
d->rgAlbumPeak = f;
d->isEmpty = false;
}
void MP4::Tag::setRGTrackGain(float f)
{
d->rgTrackGain = f;
d->isEmpty = false;
}
void MP4::Tag::setRGTrackPeak(float f)
{
d->rgTrackPeak = f;
d->isEmpty = false;
}
void MP4::Tag::setNumTracks(TagLib::uint i) void MP4::Tag::setNumTracks(TagLib::uint i)
{ {
d->numTracks = i; d->numTracks = i;

View File

@ -33,6 +33,10 @@ namespace TagLib
virtual String genre() const; virtual String genre() const;
virtual uint year() const; virtual uint year() const;
virtual uint track() const; virtual uint track() const;
virtual float rgAlbumGain() const;
virtual float rgAlbumPeak() const;
virtual float rgTrackGain() const;
virtual float rgTrackPeak() const;
virtual void setTitle(const String &s); virtual void setTitle(const String &s);
virtual void setArtist(const String &s); virtual void setArtist(const String &s);
@ -41,6 +45,10 @@ namespace TagLib
virtual void setGenre(const String &s); virtual void setGenre(const String &s);
virtual void setYear(uint i); virtual void setYear(uint i);
virtual void setTrack(uint i); virtual void setTrack(uint i);
virtual void setRGAlbumGain(float f);
virtual void setRGAlbumPeak(float f);
virtual void setRGTrackGain(float f);
virtual void setRGTrackPeak(float f);
// MP4 specific fields // MP4 specific fields

View File

@ -152,6 +152,26 @@ TagLib::uint ID3v1::Tag::track() const
return d->track; return d->track;
} }
float ID3v1::Tag::rgAlbumGain() const
{
return 0;
}
float ID3v1::Tag::rgAlbumPeak() const
{
return 0;
}
float ID3v1::Tag::rgTrackGain() const
{
return 0;
}
float ID3v1::Tag::rgTrackPeak() const
{
return 0;
}
void ID3v1::Tag::setTitle(const String &s) void ID3v1::Tag::setTitle(const String &s)
{ {
d->title = s; d->title = s;
@ -187,6 +207,22 @@ void ID3v1::Tag::setTrack(uint i)
d->track = i < 256 ? i : 0; d->track = i < 256 ? i : 0;
} }
void ID3v1::Tag::setRGAlbumGain(float)
{
}
void ID3v1::Tag::setRGAlbumPeak(float)
{
}
void ID3v1::Tag::setRGTrackGain(float)
{
}
void ID3v1::Tag::setRGTrackPeak(float)
{
}
void ID3v1::Tag::setStringHandler(const StringHandler *handler) void ID3v1::Tag::setStringHandler(const StringHandler *handler)
{ {
delete TagPrivate::stringHandler; delete TagPrivate::stringHandler;

View File

@ -140,6 +140,10 @@ namespace TagLib {
virtual String genre() const; virtual String genre() const;
virtual uint year() const; virtual uint year() const;
virtual uint track() const; virtual uint track() const;
virtual float rgAlbumGain() const;
virtual float rgAlbumPeak() const;
virtual float rgTrackGain() const;
virtual float rgTrackPeak() const;
virtual void setTitle(const String &s); virtual void setTitle(const String &s);
virtual void setArtist(const String &s); virtual void setArtist(const String &s);
@ -148,6 +152,10 @@ namespace TagLib {
virtual void setGenre(const String &s); virtual void setGenre(const String &s);
virtual void setYear(uint i); virtual void setYear(uint i);
virtual void setTrack(uint i); virtual void setTrack(uint i);
virtual void setRGAlbumGain(float f);
virtual void setRGAlbumPeak(float f);
virtual void setRGTrackGain(float f);
virtual void setRGTrackPeak(float f);
/*! /*!
* Sets the string handler that decides how the ID3v1 data will be * Sets the string handler that decides how the ID3v1 data will be

View File

@ -195,6 +195,39 @@ TagLib::uint ID3v2::Tag::track() const
return 0; return 0;
} }
float ID3v2::Tag::rg(const String &type) const
{
const FrameList &list = d->frameListMap["TXXX"];
if (!list.isEmpty()) {
for (FrameList::ConstIterator it = list.begin(); it != list.end(); ++it) {
if (static_cast<UserTextIdentificationFrame *>(*it)->description() == type) {
return static_cast<UserTextIdentificationFrame *>(*it)->toString().toFloat();
}
}
}
return 0;
}
float ID3v2::Tag::rgAlbumGain() const
{
return rg("replaygain_album_gain");
}
float ID3v2::Tag::rgAlbumPeak() const
{
return rg("replaygain_album_peak");
}
float ID3v2::Tag::rgTrackGain() const
{
return rg("replaygain_track_gain");
}
float ID3v2::Tag::rgTrackPeak() const
{
return rg("replaygain_track_peak");
}
void ID3v2::Tag::setTitle(const String &s) void ID3v2::Tag::setTitle(const String &s)
{ {
setTextFrame("TIT2", s); setTextFrame("TIT2", s);
@ -270,6 +303,52 @@ void ID3v2::Tag::setTrack(uint i)
setTextFrame("TRCK", String::number(i)); setTextFrame("TRCK", String::number(i));
} }
void ID3v2::Tag::setRG(const String &type, float f, bool peak)
{
bool createdFrame = false;
UserTextIdentificationFrame * frame = NULL;
FrameList &list = d->frameListMap["TXXX"];
for (FrameList::Iterator it = list.begin(); it != list.end(); ++it) {
if (static_cast<UserTextIdentificationFrame *>(*it)->description() == type) {
frame = static_cast<UserTextIdentificationFrame*>(*it);
break;
}
}
if (f == 0) {
if (frame)
removeFrame(frame);
return;
}
if (frame == NULL) {
frame = new UserTextIdentificationFrame;
frame->setDescription(type);
createdFrame = true;
}
frame->setText(String::number(f) + (peak ? "" : " dB"));
if (createdFrame)
addFrame(frame);
}
void ID3v2::Tag::setRGAlbumGain(float f)
{
setRG("replaygain_album_gain", f, false);
}
void ID3v2::Tag::setRGAlbumPeak(float f)
{
setRG("replaygain_album_peak", f, true);
}
void ID3v2::Tag::setRGTrackGain(float f)
{
setRG("replaygain_track_gain", f, false);
}
void ID3v2::Tag::setRGTrackPeak(float f)
{
setRG("replaygain_track_peak", f, true);
}
bool ID3v2::Tag::isEmpty() const bool ID3v2::Tag::isEmpty() const
{ {
return d->frameList.isEmpty(); return d->frameList.isEmpty();

View File

@ -142,6 +142,12 @@ namespace TagLib {
virtual uint year() const; virtual uint year() const;
virtual uint track() const; virtual uint track() const;
float rg(const String &type) const;
virtual float rgAlbumGain() const;
virtual float rgAlbumPeak() const;
virtual float rgTrackGain() const;
virtual float rgTrackPeak() const;
virtual void setTitle(const String &s); virtual void setTitle(const String &s);
virtual void setArtist(const String &s); virtual void setArtist(const String &s);
virtual void setAlbum(const String &s); virtual void setAlbum(const String &s);
@ -150,6 +156,12 @@ namespace TagLib {
virtual void setYear(uint i); virtual void setYear(uint i);
virtual void setTrack(uint i); virtual void setTrack(uint i);
void setRG(const String &type, float f, bool peak);
virtual void setRGAlbumGain(float f);
virtual void setRGAlbumPeak(float f);
virtual void setRGTrackGain(float f);
virtual void setRGTrackPeak(float f);
virtual bool isEmpty() const; virtual bool isEmpty() const;
/*! /*!

View File

@ -115,6 +115,34 @@ TagLib::uint Ogg::XiphComment::track() const
return d->fieldListMap["TRACKNUMBER"].front().toInt(); return d->fieldListMap["TRACKNUMBER"].front().toInt();
} }
float Ogg::XiphComment::rgAlbumGain() const
{
if(d->fieldListMap["REPLAYGAIN_ALBUM_GAIN"].isEmpty())
return 0;
return d->fieldListMap["REPLAYGAIN_ALBUM_GAIN"].front().toFloat();
}
float Ogg::XiphComment::rgAlbumPeak() const
{
if(d->fieldListMap["REPLAYGAIN_ALBUM_PEAK"].isEmpty())
return 0;
return d->fieldListMap["REPLAYGAIN_ALBUM_PEAK"].front().toFloat();
}
float Ogg::XiphComment::rgTrackGain() const
{
if(d->fieldListMap["REPLAYGAIN_TRACK_GAIN"].isEmpty())
return 0;
return d->fieldListMap["REPLAYGAIN_TRACK_GAIN"].front().toFloat();
}
float Ogg::XiphComment::rgTrackPeak() const
{
if(d->fieldListMap["REPLAYGAIN_TRACK_PEAK"].isEmpty())
return 0;
return d->fieldListMap["REPLAYGAIN_TRACK_PEAK"].front().toFloat();
}
void Ogg::XiphComment::setTitle(const String &s) void Ogg::XiphComment::setTitle(const String &s)
{ {
addField("TITLE", s); addField("TITLE", s);
@ -156,6 +184,38 @@ void Ogg::XiphComment::setTrack(uint i)
addField("TRACKNUMBER", String::number(i)); addField("TRACKNUMBER", String::number(i));
} }
void Ogg::XiphComment::setRGAlbumGain(float f)
{
if (f == 0)
removeField("REPLAYGAIN_ALBUM_GAIN");
else
addField("REPLAYGAIN_ALBUM_GAIN", String::number(f) + " dB");
}
void Ogg::XiphComment::setRGAlbumPeak(float f)
{
if (f == 0)
removeField("REPLAYGAIN_ALBUM_PEAK");
else
addField("REPLAYGAIN_ALBUM_PEAK", String::number(f));
}
void Ogg::XiphComment::setRGTrackGain(float f)
{
if (f == 0)
removeField("REPLAYGAIN_TRACK_GAIN");
else
addField("REPLAYGAIN_TRACK_GAIN", String::number(f) + " dB");
}
void Ogg::XiphComment::setRGTrackPeak(float f)
{
if (f == 0)
removeField("REPLAYGAIN_TRACK_PEAK");
else
addField("REPLAYGAIN_TRACK_PEAK", String::number(f));
}
bool Ogg::XiphComment::isEmpty() const bool Ogg::XiphComment::isEmpty() const
{ {
FieldListMap::ConstIterator it = d->fieldListMap.begin(); FieldListMap::ConstIterator it = d->fieldListMap.begin();

View File

@ -86,6 +86,10 @@ namespace TagLib {
virtual String genre() const; virtual String genre() const;
virtual uint year() const; virtual uint year() const;
virtual uint track() const; virtual uint track() const;
virtual float rgAlbumGain() const;
virtual float rgAlbumPeak() const;
virtual float rgTrackGain() const;
virtual float rgTrackPeak() const;
virtual void setTitle(const String &s); virtual void setTitle(const String &s);
virtual void setArtist(const String &s); virtual void setArtist(const String &s);
@ -94,6 +98,10 @@ namespace TagLib {
virtual void setGenre(const String &s); virtual void setGenre(const String &s);
virtual void setYear(uint i); virtual void setYear(uint i);
virtual void setTrack(uint i); virtual void setTrack(uint i);
virtual void setRGAlbumGain(float f);
virtual void setRGAlbumPeak(float f);
virtual void setRGTrackGain(float f);
virtual void setRGTrackPeak(float f);
virtual bool isEmpty() const; virtual bool isEmpty() const;

View File

@ -91,6 +91,30 @@ namespace TagLib {
*/ */
virtual uint track() const = 0; virtual uint track() const = 0;
/*!
* Returns the ReplayGain album gain; if there is no gain level set, this
* will return 0.
*/
virtual float rgAlbumGain() const = 0;
/*!
* Returns the ReplayGain album peak; if there is no gain level set, this
* will return 0.
*/
virtual float rgAlbumPeak() const = 0;
/*!
* Returns the ReplayGain track gain; if there is no gain level set, this
* will return 0.
*/
virtual float rgTrackGain() const = 0;
/*!
* Returns the ReplayGain track peak; if there is no gain level set, this
* will return 0.
*/
virtual float rgTrackPeak() const = 0;
/*! /*!
* Sets the title to \a s. If \a s is String::null then this value will be * Sets the title to \a s. If \a s is String::null then this value will be
* cleared. * cleared.
@ -134,6 +158,30 @@ namespace TagLib {
*/ */
virtual void setTrack(uint i) = 0; virtual void setTrack(uint i) = 0;
/*!
* Sets the ReplayGain album gain to \a f. If \a f is 0 then this value will
* be cleared.
*/
virtual void setRGAlbumGain(float i) = 0;
/*!
* Sets the ReplayGain album peak to \a f. If \a f is 0 then this value will
* be cleared.
*/
virtual void setRGAlbumPeak(float i) = 0;
/*!
* Sets the ReplayGain track gain to \a f. If \a f is 0 then this value will
* be cleared.
*/
virtual void setRGTrackGain(float i) = 0;
/*!
* Sets the ReplayGain track peak to \a f. If \a f is 0 then this value will
* be cleared.
*/
virtual void setRGTrackPeak(float i) = 0;
/*! /*!
* Returns true if the tag does not contain any data. This should be * Returns true if the tag does not contain any data. This should be
* reimplemented in subclasses that provide more than the basic tagging * reimplemented in subclasses that provide more than the basic tagging

View File

@ -45,6 +45,15 @@ using namespace TagLib;
return tag(2)->method(); \ return tag(2)->method(); \
return 0 return 0
#define floatUnion(method) \
if(tag(0) && tag(0)->method() != 0) \
return tag(0)->method(); \
if(tag(1) && tag(1)->method() != 0) \
return tag(1)->method(); \
if(tag(2) && tag(2)->method() != 0) \
return tag(2)->method(); \
return 0
#define setUnion(method, value) \ #define setUnion(method, value) \
if(tag(0)) \ if(tag(0)) \
tag(0)->set##method(value); \ tag(0)->set##method(value); \
@ -136,6 +145,26 @@ TagLib::uint TagUnion::track() const
numberUnion(track); numberUnion(track);
} }
float TagUnion::rgAlbumGain() const
{
floatUnion(rgAlbumGain);
}
float TagUnion::rgAlbumPeak() const
{
floatUnion(rgAlbumPeak);
}
float TagUnion::rgTrackGain() const
{
floatUnion(rgTrackGain);
}
float TagUnion::rgTrackPeak() const
{
floatUnion(rgTrackPeak);
}
void TagUnion::setTitle(const String &s) void TagUnion::setTitle(const String &s)
{ {
setUnion(Title, s); setUnion(Title, s);
@ -171,6 +200,26 @@ void TagUnion::setTrack(uint i)
setUnion(Track, i); setUnion(Track, i);
} }
void TagUnion::setRGAlbumGain(float f)
{
setUnion(RGAlbumGain, f);
}
void TagUnion::setRGAlbumPeak(float f)
{
setUnion(RGAlbumPeak, f);
}
void TagUnion::setRGTrackGain(float f)
{
setUnion(RGTrackGain, f);
}
void TagUnion::setRGTrackPeak(float f)
{
setUnion(RGTrackPeak, f);
}
bool TagUnion::isEmpty() const bool TagUnion::isEmpty() const
{ {
if(d->tags[0] && !d->tags[0]->isEmpty()) if(d->tags[0] && !d->tags[0]->isEmpty())

View File

@ -63,6 +63,10 @@ namespace TagLib {
virtual String genre() const; virtual String genre() const;
virtual uint year() const; virtual uint year() const;
virtual uint track() const; virtual uint track() const;
virtual float rgAlbumGain() const;
virtual float rgAlbumPeak() const;
virtual float rgTrackGain() const;
virtual float rgTrackPeak() const;
virtual void setTitle(const String &s); virtual void setTitle(const String &s);
virtual void setArtist(const String &s); virtual void setArtist(const String &s);
@ -71,6 +75,10 @@ namespace TagLib {
virtual void setGenre(const String &s); virtual void setGenre(const String &s);
virtual void setYear(uint i); virtual void setYear(uint i);
virtual void setTrack(uint i); virtual void setTrack(uint i);
virtual void setRGAlbumGain(float f);
virtual void setRGAlbumPeak(float f);
virtual void setRGTrackGain(float f);
virtual void setRGTrackPeak(float f);
virtual bool isEmpty() const; virtual bool isEmpty() const;
template <class T> T *access(int index, bool create) template <class T> T *access(int index, bool create)

View File

@ -31,6 +31,8 @@
#include <string.h> #include <string.h>
#include <math.h>
namespace TagLib { namespace TagLib {
inline unsigned short byteSwap(unsigned short x) inline unsigned short byteSwap(unsigned short x)
@ -435,6 +437,31 @@ int String::toInt() const
return value; return value;
} }
float String::toFloat() const
{
float value = 0;
float decimal_value = 1;
bool negative = d->data[0] == '-';
uint i = negative ? 1 : 0;
for(; i < d->data.size() && d->data[i] >= '0' && d->data[i] <= '9'; i++)
value = value * 10 + (d->data[i] - '0');
if (i < d->data.size() && d->data[i] == '.')
for(++i; i < d->data.size() && d->data[i] >= '0' && d->data[i] <= '9'; i++) {
value = value * 10 + (d->data[i] - '0');
decimal_value *= 0.1;
}
if(negative)
value = value * -1;
value *= decimal_value;
return value;
}
String String::stripWhiteSpace() const String String::stripWhiteSpace() const
{ {
wstring::const_iterator begin = d->data.begin(); wstring::const_iterator begin = d->data.begin();
@ -479,6 +506,11 @@ bool String::isAscii() const
return true; return true;
} }
String String::number(uint n)
{
return number((int)n);
}
String String::number(int n) // static String String::number(int n) // static
{ {
if(n == 0) if(n == 0)
@ -508,6 +540,48 @@ String String::number(int n) // static
return s; return s;
} }
String String::number(float n) // static
{
if(n == 0)
return String("0");
float decimal = fmod(n, 1);
n -= decimal;
String charStack;
bool negative = n < 0;
if(negative)
n = n * -1;
while(n > 0) {
float remainder = fmod(n, 10);
charStack += char(remainder + '0');
n = (n - remainder) / 10;
}
String s;
if(negative)
s += '-';
for(int i = charStack.d->data.size() - 1; i >= 0; i--)
s += charStack.d->data[i];
if (decimal > 0) {
s += '.';
while (decimal > 0) {
decimal *= 10;
float remainder = fmod(decimal, 1);
s += char(decimal + '0');
decimal = remainder;
}
}
return s;
}
TagLib::wchar &String::operator[](int i) TagLib::wchar &String::operator[](int i)
{ {
return d->data[i]; return d->data[i];

View File

@ -287,6 +287,11 @@ namespace TagLib {
*/ */
int toInt() const; int toInt() const;
/*!
* Convert the string to a float.
*/
float toFloat() const;
/*! /*!
* Returns a string with the leading and trailing whitespace stripped. * Returns a string with the leading and trailing whitespace stripped.
*/ */
@ -305,8 +310,14 @@ namespace TagLib {
/*! /*!
* Converts the base-10 integer \a n to a string. * Converts the base-10 integer \a n to a string.
*/ */
static String number(uint n);
static String number(int n); static String number(int n);
/*!
* Converts the base-10 float \a n to a string.
*/
static String number(float n);
/*! /*!
* Returns a reference to the character at position \a i. * Returns a reference to the character at position \a i.
*/ */

View File

@ -39,6 +39,12 @@
@synthesize bitsPerSample; @synthesize bitsPerSample;
@synthesize sampleRate; @synthesize sampleRate;
@synthesize replayGainAlbumGain;
@synthesize replayGainAlbumPeak;
@synthesize replayGainTrackGain;
@synthesize replayGainTrackPeak;
@synthesize volume;
@synthesize endian; @synthesize endian;
@synthesize seekable; @synthesize seekable;
@ -82,6 +88,18 @@
return [NSString stringWithFormat:@"PlaylistEntry %i:(%@)", self.index, self.URL]; return [NSString stringWithFormat:@"PlaylistEntry %i:(%@)", self.index, self.URL];
} }
- (id)init
{
if (self = [super init]) {
self.replayGainAlbumGain = 0;
self.replayGainAlbumPeak = 0;
self.replayGainTrackGain = 0;
self.replayGainTrackPeak = 0;
self.volume = 1;
}
return self;
}
- (void)dealloc - (void)dealloc
{ {
self.errorMessage = nil; self.errorMessage = nil;

View File

@ -32,6 +32,7 @@
{ {
TagLib::String artist, title, album, genre, comment; TagLib::String artist, title, album, genre, comment;
int year, track; int year, track;
float rgAlbumGain, rgAlbumPeak, rgTrackGain, rgTrackPeak;
artist = tag->artist(); artist = tag->artist();
title = tag->title();; title = tag->title();;
@ -45,6 +46,15 @@
track = tag->track(); track = tag->track();
[dict setObject:[NSNumber numberWithInt:track] forKey:@"track"]; [dict setObject:[NSNumber numberWithInt:track] forKey:@"track"];
rgAlbumGain = tag->rgAlbumGain();
rgAlbumPeak = tag->rgAlbumPeak();
rgTrackGain = tag->rgTrackGain();
rgTrackPeak = tag->rgTrackPeak();
[dict setObject:[NSNumber numberWithFloat:rgAlbumGain] forKey:@"replayGainAlbumGain"];
[dict setObject:[NSNumber numberWithFloat:rgAlbumPeak] forKey:@"replayGainAlbumPeak"];
[dict setObject:[NSNumber numberWithFloat:rgTrackGain] forKey:@"replayGainTrackGain"];
[dict setObject:[NSNumber numberWithFloat:rgTrackPeak] forKey:@"replayGainTrackPeak"];
if (!artist.isNull()) if (!artist.isNull())
[dict setObject:[NSString stringWithUTF8String:artist.toCString(true)] forKey:@"artist"]; [dict setObject:[NSString stringWithUTF8String:artist.toCString(true)] forKey:@"artist"];