***************************************************************************/
#include <tdebug.h>
+#include <tmap.h>
#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<ChannelType, ChannelData> channels;
};
////////////////////////////////////////////////////////////////////////////////
return d->identification;
}
+List<RelativeVolumeFrame::ChannelType> RelativeVolumeFrame::channels() const
+{
+ List<ChannelType> l;
+
+ Map<ChannelType, ChannelData>::ConstIterator it = d->channels.begin();
+ for(; it != d->channels.end(); ++it)
+ l.append((*it).first);
+
+ return l;
+}
+
+// deprecated
+
RelativeVolumeFrame::ChannelType RelativeVolumeFrame::channelType() const
{
- return d->channelType;
+ return MasterVolume;
}
-void RelativeVolumeFrame::setChannelType(ChannelType t)
+// deprecated
+
+void RelativeVolumeFrame::setChannelType(ChannelType)
{
- d->channelType = t;
+
+}
+
+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);
}
////////////////////////////////////////////////////////////////////////////////
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.
+
+ while(pos <= data.size() - 4) {
+
+
+ ChannelType type = ChannelType(data[pos]);
+ pos += 1;
- d->volumeAdjustment = data.mid(pos, 2).toShort();
- pos += 2;
+ ChannelData &channel = d->channels[type];
- d->peakVolume.bitsRepresentingPeak = data[pos];
- pos += 1;
+ channel.volumeAdjustment = data.mid(pos, 2).toShort();
+ pos += 2;
- d->peakVolume.peakVolume = data.mid(pos, d->peakVolume.bitsRepresentingPeak);
+ 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
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<ChannelType, ChannelData>::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;
}
#ifndef TAGLIB_RELATIVEVOLUMEFRAME_H
#define TAGLIB_RELATIVEVOLUMEFRAME_H
+#include <tlist.h>
#include <id3v2frame.h>
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<ChannelType> 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.
* 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().
* \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;