diff --git a/mpeg/id3v2/frames/relativevolumeframe.cpp b/mpeg/id3v2/frames/relativevolumeframe.cpp index 3042a2da..3b8caea9 100644 --- a/mpeg/id3v2/frames/relativevolumeframe.cpp +++ b/mpeg/id3v2/frames/relativevolumeframe.cpp @@ -20,21 +20,32 @@ ***************************************************************************/ #include +#include #include "relativevolumeframe.h" using namespace TagLib; using namespace ID3v2; +static inline int bitsToBytes(int i) +{ + return i % 8 == 0 ? i / 8 : (i - i % 8) / 8 + 1; +} + +struct ChannelData +{ + ChannelData() : channelType(RelativeVolumeFrame::Other), volumeAdjustment(0) {} + + RelativeVolumeFrame::ChannelType channelType; + short volumeAdjustment; + RelativeVolumeFrame::PeakVolume peakVolume; +}; + class RelativeVolumeFrame::RelativeVolumeFramePrivate { public: - RelativeVolumeFramePrivate() : channelType(Other), volumeAdjustment(0) {} - String identification; - ChannelType channelType; - short volumeAdjustment; - PeakVolume peakVolume; + Map channels; }; //////////////////////////////////////////////////////////////////////////////// @@ -57,44 +68,89 @@ String RelativeVolumeFrame::toString() const return d->identification; } -RelativeVolumeFrame::ChannelType RelativeVolumeFrame::channelType() const +List RelativeVolumeFrame::channels() const { - return d->channelType; + List l; + + Map::ConstIterator it = d->channels.begin(); + for(; it != d->channels.end(); ++it) + l.append((*it).first); + + return l; } -void RelativeVolumeFrame::setChannelType(ChannelType t) +// deprecated + +RelativeVolumeFrame::ChannelType RelativeVolumeFrame::channelType() const { - d->channelType = t; + return MasterVolume; +} + +// deprecated + +void RelativeVolumeFrame::setChannelType(ChannelType) +{ + +} + +short RelativeVolumeFrame::volumeAdjustmentIndex(ChannelType type) const +{ + return d->channels.contains(type) ? d->channels[type].volumeAdjustment : 0; } short RelativeVolumeFrame::volumeAdjustmentIndex() const { - return d->volumeAdjustment; + return volumeAdjustmentIndex(MasterVolume); +} + +void RelativeVolumeFrame::setVolumeAdjustmentIndex(short index, ChannelType type) +{ + d->channels[type].volumeAdjustment = index; } void RelativeVolumeFrame::setVolumeAdjustmentIndex(short index) { - d->volumeAdjustment = index; + setVolumeAdjustmentIndex(index, MasterVolume); +} + +float RelativeVolumeFrame::volumeAdjustment(ChannelType type) const +{ + return d->channels.contains(type) ? float(d->channels[type].volumeAdjustment) / float(512) : 0; } float RelativeVolumeFrame::volumeAdjustment() const { - return float(d->volumeAdjustment) / float(512); + return volumeAdjustment(MasterVolume); +} + +void RelativeVolumeFrame::setVolumeAdjustment(float adjustment, ChannelType type) +{ + d->channels[type].volumeAdjustment = short(adjustment / float(512)); } void RelativeVolumeFrame::setVolumeAdjustment(float adjustment) { - d->volumeAdjustment = short(adjustment / float(512)); + setVolumeAdjustment(adjustment, MasterVolume); +} + +RelativeVolumeFrame::PeakVolume RelativeVolumeFrame::peakVolume(ChannelType type) const +{ + return d->channels.contains(type) ? d->channels[type].peakVolume : PeakVolume(); } RelativeVolumeFrame::PeakVolume RelativeVolumeFrame::peakVolume() const { - return d->peakVolume; + return peakVolume(MasterVolume); +} + +void RelativeVolumeFrame::setPeakVolume(const PeakVolume &peak, ChannelType type) +{ + d->channels[type].peakVolume = peak; } void RelativeVolumeFrame::setPeakVolume(const PeakVolume &peak) { - d->peakVolume = peak; + setPeakVolume(peak, MasterVolume); } //////////////////////////////////////////////////////////////////////////////// @@ -103,24 +159,29 @@ void RelativeVolumeFrame::setPeakVolume(const PeakVolume &peak) void RelativeVolumeFrame::parseFields(const ByteVector &data) { - if(data.size() < 6) { - debug("A relative volume frame must contain at least 6 bytes."); - return; - } - - int pos = data.find(textDelimiter(String::Latin1)); + uint pos = data.find(textDelimiter(String::Latin1)); d->identification = String(data.mid(0, pos), String::Latin1); - d->channelType = ChannelType(data[pos]); - pos += 1; + // Each channel is at least 4 bytes. - d->volumeAdjustment = data.mid(pos, 2).toShort(); - pos += 2; + while(pos <= data.size() - 4) { - d->peakVolume.bitsRepresentingPeak = data[pos]; - pos += 1; - d->peakVolume.peakVolume = data.mid(pos, d->peakVolume.bitsRepresentingPeak); + ChannelType type = ChannelType(data[pos]); + pos += 1; + + ChannelData &channel = d->channels[type]; + + channel.volumeAdjustment = data.mid(pos, 2).toShort(); + pos += 2; + + channel.peakVolume.bitsRepresentingPeak = data[pos]; + pos += 1; + + int bytes = bitsToBytes(channel.peakVolume.bitsRepresentingPeak); + channel.peakVolume.peakVolume = data.mid(pos, bytes); + pos += bytes; + } } ByteVector RelativeVolumeFrame::renderFields() const @@ -129,10 +190,18 @@ ByteVector RelativeVolumeFrame::renderFields() const data.append(d->identification.data(String::Latin1)); data.append(textDelimiter(String::Latin1)); - data.append(char(d->channelType)); - data.append(ByteVector::fromShort(d->volumeAdjustment)); - data.append(char(d->peakVolume.bitsRepresentingPeak)); - data.append(d->peakVolume.peakVolume); + + Map::ConstIterator it = d->channels.begin(); + + for(; it != d->channels.end(); ++it) { + ChannelType type = (*it).first; + const ChannelData &channel = (*it).second; + + data.append(char(type)); + data.append(ByteVector::fromShort(channel.volumeAdjustment)); + data.append(char(channel.peakVolume.bitsRepresentingPeak)); + data.append(channel.peakVolume.peakVolume); + } return data; } diff --git a/mpeg/id3v2/frames/relativevolumeframe.h b/mpeg/id3v2/frames/relativevolumeframe.h index f2d9a9dc..981ea2e8 100644 --- a/mpeg/id3v2/frames/relativevolumeframe.h +++ b/mpeg/id3v2/frames/relativevolumeframe.h @@ -22,6 +22,7 @@ #ifndef TAGLIB_RELATIVEVOLUMEFRAME_H #define TAGLIB_RELATIVEVOLUMEFRAME_H +#include #include namespace TagLib { @@ -118,38 +119,52 @@ namespace TagLib { virtual String toString() const; /*! - * Returns the channel type that this frame refers to. - * - * \see setChannelType() + * Returns a list of channels with information currently in the frame. + */ + List channels() const; + + /*! + * \deprecated Always returns master volume. */ ChannelType channelType() const; /*! - * Sets the channel type that this frame refers to. - * - * \see channelType() + * \deprecated This method no longer has any effect. */ void setChannelType(ChannelType t); + /* + * There was a terrible API goof here, and while this can't be changed to + * the way it appears below for binary compaibility reasons, let's at + * least pretend that it looks clean. + */ + +#ifdef DOXYGEN + /*! * Returns the relative volume adjustment "index". As indicated by the * ID3v2 standard this is a 16-bit signed integer that reflects the * decibils of adjustment when divided by 512. * + * This defaults to returning the value for the master volume channel if + * available and returns 0 if the specified channel does not exist. + * * \see setVolumeAdjustmentIndex() * \see volumeAjustment() */ - short volumeAdjustmentIndex() const; + short volumeAdjustmentIndex(ChannelType type = MasterVolume) const; /*! * Set the volume adjustment to \a index. As indicated by the ID3v2 * standard this is a 16-bit signed integer that reflects the decibils of * adjustment when divided by 512. * + * By default this sets the value for the master volume. + * * \see volumeAdjustmentIndex() * \see setVolumeAjustment() */ - void setVolumeAdjustmentIndex(short index); + void setVolumeAdjustmentIndex(short index, ChannelType type = MasterVolume); /*! * Returns the relative volume adjustment in decibels. @@ -158,14 +173,19 @@ namespace TagLib { * value the value returned by this method may not be identical to the * value set using setVolumeAdjustment(). * + * This defaults to returning the value for the master volume channel if + * available and returns 0 if the specified channel does not exist. + * * \see setVolumeAdjustment() * \see volumeAdjustmentIndex() */ - float volumeAdjustment() const; + float volumeAdjustment(ChannelType type = MasterVolume) const; /*! * Set the relative volume adjustment in decibels to \a adjustment. * + * By default this sets the value for the master volume. + * * \note Because this is actually stored internally as an "index" to this * value the value set by this method may not be identical to the one * returned by volumeAdjustment(). @@ -173,22 +193,52 @@ namespace TagLib { * \see setVolumeAdjustment() * \see volumeAdjustmentIndex() */ - void setVolumeAdjustment(float adjustment); + void setVolumeAdjustment(float adjustment, ChannelType type = MasterVolume); /*! * Returns the peak volume (represented as a length and a string of bits). * + * This defaults to returning the value for the master volume channel if + * available and returns 0 if the specified channel does not exist. + * * \see setPeakVolume() */ - PeakVolume peakVolume() const; + PeakVolume peakVolume(ChannelType type = MasterVolume) const; /*! * Sets the peak volume to \a peak. * + * By default this sets the value for the master volume. + * * \see peakVolume() */ + void setPeakVolume(const PeakVolume &peak, ChannelType type = MasterVolume); + +#else + + // BIC: Combine each of the following pairs of functions (or maybe just + // rework this junk altogether). + + short volumeAdjustmentIndex(ChannelType type) const; + short volumeAdjustmentIndex() const; + + void setVolumeAdjustmentIndex(short index, ChannelType type); + void setVolumeAdjustmentIndex(short index); + + float volumeAdjustment(ChannelType type) const; + float volumeAdjustment() const; + + void setVolumeAdjustment(float adjustment, ChannelType type); + void setVolumeAdjustment(float adjustment); + + PeakVolume peakVolume(ChannelType type) const; + PeakVolume peakVolume() const; + + void setPeakVolume(const PeakVolume &peak, ChannelType type); void setPeakVolume(const PeakVolume &peak); +#endif + protected: virtual void parseFields(const ByteVector &data); virtual ByteVector renderFields() const;