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 *CogPlaybackDidStopNotficiation;
extern NSDictionary * makeRGInfo(PlaylistEntry *pe);
@class PlaylistController;
@class PlaylistView;

View File

@ -117,6 +117,22 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
[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
{
if (playbackStatus != kCogStatusStopped)
@ -130,7 +146,7 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
if (pe == nil)
return;
[audioPlayer play:[pe URL] withUserInfo:pe];
[audioPlayer play:[pe URL] withUserInfo:pe withRGInfo:makeRGInfo(pe)];
}
- (IBAction)next:(id)sender
@ -430,7 +446,7 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
else
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

View File

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

View File

@ -43,10 +43,10 @@
- (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)
{
@ -91,6 +91,7 @@
}
userInfo = nextStreamUserInfo;
rgi = nextStreamRGInfo;
[self notifyStreamChanged:userInfo];
@ -98,6 +99,7 @@
}
[bufferChain setUserInfo:userInfo];
[bufferChain setRGInfo:rgi];
[self setShouldContinue:YES];
@ -152,10 +154,10 @@
//This is called by the delegate DURING a requestNextStream request.
- (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];
[nextStream release];
@ -164,7 +166,10 @@
[userInfo retain];
[nextStreamUserInfo release];
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.
@ -212,7 +217,7 @@
- (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
@ -233,6 +238,9 @@
nextStreamUserInfo = [sender userInfo];
[nextStreamUserInfo retain]; //Retained because when setNextStream is called, it will be released!!!
nextStreamRGInfo = [sender rgInfo];
[nextStreamRGInfo retain];
[self requestNextStream: nextStreamUserInfo];
newChain = [[BufferChain alloc] initWithController:self];

View File

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

View File

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

View File

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

View File

@ -133,11 +133,13 @@ static float db_to_scale(float db)
- (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"];
BOOL usePeak = [scaling hasSuffix:@"WithPeak"];
float scale = 1.0;
float peak = 0.0;
if (useVolume) {
@ -319,9 +321,19 @@ static int32_t swap_32(uint32_t input)
return NO;
}
- (void)setRGInfo:(NSDictionary *)i
{
[i retain];
[rgInfo release];
rgInfo = i;
[self refreshVolumeScaling];
}
- (void)dealloc
{
NSLog(@"Input Node dealloc");
[rgInfo release];
[decoder removeObserver:self forKeyPath:@"properties"];
[decoder removeObserver:self forKeyPath:@"metadata"];

View File

@ -133,6 +133,34 @@ TagLib::uint APE::Tag::track() const
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)
{
addValue("TITLE", s, true);
@ -174,6 +202,38 @@ void APE::Tag::setTrack(uint i)
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
{
return &d->footer;

View File

@ -94,6 +94,10 @@ namespace TagLib {
virtual String genre() const;
virtual uint year() 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 setArtist(const String &s);
@ -102,6 +106,10 @@ namespace TagLib {
virtual void setGenre(const String &s);
virtual void setYear(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.

View File

@ -19,6 +19,10 @@ public:
TagLib::uint disk;
TagLib::uint numDisks;
TagLib::uint bpm;
float rgAlbumGain;
float rgAlbumPeak;
float rgTrackGain;
float rgTrackPeak;
bool isEmpty;
TagLib::ByteVector cover;
};
@ -76,6 +80,26 @@ TagLib::uint MP4::Tag::track() const
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
{
return d->numTracks;
@ -153,6 +177,30 @@ void MP4::Tag::setTrack(TagLib::uint i)
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)
{
d->numTracks = i;

View File

@ -33,6 +33,10 @@ namespace TagLib
virtual String genre() const;
virtual uint year() 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 setArtist(const String &s);
@ -41,6 +45,10 @@ namespace TagLib
virtual void setGenre(const String &s);
virtual void setYear(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

View File

@ -152,6 +152,26 @@ TagLib::uint ID3v1::Tag::track() const
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)
{
d->title = s;
@ -187,6 +207,22 @@ void ID3v1::Tag::setTrack(uint i)
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)
{
delete TagPrivate::stringHandler;

View File

@ -140,6 +140,10 @@ namespace TagLib {
virtual String genre() const;
virtual uint year() 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 setArtist(const String &s);
@ -148,6 +152,10 @@ namespace TagLib {
virtual void setGenre(const String &s);
virtual void setYear(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

View File

@ -195,6 +195,39 @@ TagLib::uint ID3v2::Tag::track() const
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)
{
setTextFrame("TIT2", s);
@ -270,6 +303,52 @@ void ID3v2::Tag::setTrack(uint 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
{
return d->frameList.isEmpty();

View File

@ -142,6 +142,12 @@ namespace TagLib {
virtual uint year() 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 setArtist(const String &s);
virtual void setAlbum(const String &s);
@ -150,6 +156,12 @@ namespace TagLib {
virtual void setYear(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;
/*!

View File

@ -115,6 +115,34 @@ TagLib::uint Ogg::XiphComment::track() const
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)
{
addField("TITLE", s);
@ -156,6 +184,38 @@ void Ogg::XiphComment::setTrack(uint 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
{
FieldListMap::ConstIterator it = d->fieldListMap.begin();

View File

@ -86,6 +86,10 @@ namespace TagLib {
virtual String genre() const;
virtual uint year() 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 setArtist(const String &s);
@ -94,6 +98,10 @@ namespace TagLib {
virtual void setGenre(const String &s);
virtual void setYear(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;

View File

@ -90,7 +90,31 @@ namespace TagLib {
* return 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
* cleared.
@ -133,7 +157,31 @@ namespace TagLib {
* Sets the track to \a i. If \a s is 0 then this value will be cleared.
*/
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
* reimplemented in subclasses that provide more than the basic tagging

View File

@ -45,6 +45,15 @@ using namespace TagLib;
return tag(2)->method(); \
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) \
if(tag(0)) \
tag(0)->set##method(value); \
@ -136,6 +145,26 @@ TagLib::uint TagUnion::track() const
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)
{
setUnion(Title, s);
@ -171,6 +200,26 @@ void TagUnion::setTrack(uint 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
{
if(d->tags[0] && !d->tags[0]->isEmpty())

View File

@ -63,6 +63,10 @@ namespace TagLib {
virtual String genre() const;
virtual uint year() 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 setArtist(const String &s);
@ -71,6 +75,10 @@ namespace TagLib {
virtual void setGenre(const String &s);
virtual void setYear(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;
template <class T> T *access(int index, bool create)

View File

@ -31,6 +31,8 @@
#include <string.h>
#include <math.h>
namespace TagLib {
inline unsigned short byteSwap(unsigned short x)
@ -435,6 +437,31 @@ int String::toInt() const
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
{
wstring::const_iterator begin = d->data.begin();
@ -479,6 +506,11 @@ bool String::isAscii() const
return true;
}
String String::number(uint n)
{
return number((int)n);
}
String String::number(int n) // static
{
if(n == 0)
@ -508,6 +540,48 @@ String String::number(int n) // static
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)
{
return d->data[i];

View File

@ -286,6 +286,11 @@ namespace TagLib {
* Convert the string to an integer.
*/
int toInt() const;
/*!
* Convert the string to a float.
*/
float toFloat() const;
/*!
* Returns a string with the leading and trailing whitespace stripped.
@ -305,7 +310,13 @@ namespace TagLib {
/*!
* Converts the base-10 integer \a n to a string.
*/
static String number(uint 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.

View File

@ -39,6 +39,12 @@
@synthesize bitsPerSample;
@synthesize sampleRate;
@synthesize replayGainAlbumGain;
@synthesize replayGainAlbumPeak;
@synthesize replayGainTrackGain;
@synthesize replayGainTrackPeak;
@synthesize volume;
@synthesize endian;
@synthesize seekable;
@ -82,6 +88,18 @@
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
{
self.errorMessage = nil;

View File

@ -32,6 +32,7 @@
{
TagLib::String artist, title, album, genre, comment;
int year, track;
float rgAlbumGain, rgAlbumPeak, rgTrackGain, rgTrackPeak;
artist = tag->artist();
title = tag->title();;
@ -44,6 +45,15 @@
track = tag->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())
[dict setObject:[NSString stringWithUTF8String:artist.toCString(true)] forKey:@"artist"];