added .mod file support

This commit is contained in:
Mathias Panzenböck 2011-06-14 03:47:08 +02:00
parent 812826fe3a
commit f75f5ac9bb
17 changed files with 374 additions and 123 deletions

View File

@ -102,14 +102,15 @@ set(tag_HDRS
riff/wav/wavfile.h
riff/wav/wavproperties.h
mod/modfilebase.h
mod/modfile.h
mod/modtag.h
mod/modproperties.h
it/itfile.h
it/itproperties.h
s3m/s3mfile.h
s3m/s3mproperties.h
xm/xmfile.h
xm/xmproperties.h
xm/xmtag.h
)
if(WITH_ASF)
set(tag_HDRS ${tag_HDRS}
@ -252,7 +253,9 @@ set(wav_SRCS
set(mod_SRCS
mod/modfilebase.cpp
mod/modfile.cpp
mod/modtag.cpp
mod/modproperties.cpp
)
set(s3m_SRCS
@ -267,7 +270,6 @@ set(it_SRCS
set(xm_SRCS
xm/xmfile.cpp
xm/xmtag.cpp
xm/xmproperties.cpp
)

View File

@ -49,6 +49,7 @@
#include "aifffile.h"
#include "wavfile.h"
#include "apefile.h"
#include "modfile.h"
#include "s3mfile.h"
#include "itfile.h"
#include "xmfile.h"
@ -266,6 +267,9 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")

View File

@ -119,7 +119,7 @@ void IT::File::read(bool)
for(ushort i = 0; i < instrumentCount; ++ i)
{
seek(192 + length + (i << 2));
seek(192L + length + ((long)i << 2));
READ_U32L_AS(instrumentOffset);
seek(instrumentOffset);
@ -137,7 +137,7 @@ void IT::File::read(bool)
for(ushort i = 0; i < sampleCount; ++ i)
{
seek(192 + length + (instrumentCount << 2) + (i << 2));
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
READ_U32L_AS(sampleOffset);
seek(sampleOffset);
@ -182,4 +182,5 @@ void IT::File::read(bool)
}
d->tag.setComment(comment.toString("\n"));
d->tag.setTrackerName("Impulse Tracker");
}

View File

@ -22,8 +22,6 @@
#ifndef TAGLIB_ITFILE_H
#define TAGLIB_ITFILE_H
#include <stdint.h>
#include "tfile.h"
#include "audioproperties.h"
#include "taglib_export.h"

164
taglib/mod/modfile.cpp Normal file
View File

@ -0,0 +1,164 @@
/***************************************************************************
copyright : (C) 2011 by Mathias Panzenböck
email : grosser.meister.morti@gmx.net
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#include "modfile.h"
#include "tstringlist.h"
#include "modfileprivate.h"
using namespace TagLib;
using namespace Mod;
class Mod::File::FilePrivate
{
public:
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: properties(propertiesStyle)
{
}
Mod::Tag tag;
Mod::Properties properties;
};
Mod::File::File(FileName file, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
Mod::FileBase(file),
d(new FilePrivate(propertiesStyle))
{
read(readProperties);
}
Mod::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
Mod::FileBase(stream),
d(new FilePrivate(propertiesStyle))
{
read(readProperties);
}
Mod::File::~File()
{
delete d;
}
Mod::Tag *Mod::File::tag() const
{
return &d->tag;
}
Mod::Properties *Mod::File::audioProperties() const
{
return &d->properties;
}
bool Mod::File::save()
{
// note: if title starts with "Extended Module: "
// the file would look like an .xm file
seek(0);
writeString(d->tag.title(), 20, ' ');
// TODO: write comment as sample names
return true;
}
void Mod::File::read(bool)
{
if(!isOpen())
return;
seek(1080);
ByteVector modId = readBlock(4);
READ_ASSERT(modId.size() == 4);
int channels = 4;
int instruments = 31;
if(modId == "M.K." || modId == "M!K!" || modId == "M&K!" || modId == "N.T.")
{
d->tag.setTrackerName("ProTracker");
channels = 4;
}
else if(modId.startsWith("FLT") || modId.startsWith("TDZ"))
{
d->tag.setTrackerName("StarTrekker");
char digit = modId[3];
READ_ASSERT(digit >= '0' && digit <= '9');
channels = digit - '0';
}
else if(modId.endsWith("CHN"))
{
d->tag.setTrackerName("StarTrekker");
char digit = modId[0];
READ_ASSERT(digit >= '0' && digit <= '9');
channels = digit - '0';
}
else if(modId == "CD81" || modId == "OKTA")
{
d->tag.setTrackerName("Atari Oktalyzer");
channels = 8;
}
else if(modId.endsWith("CH") || modId.endsWith("CN"))
{
d->tag.setTrackerName("TakeTracker");
char digit = modId[0];
READ_ASSERT(digit >= '0' && digit <= '9');
channels = (digit - '0') * 10;
digit = modId[1];
READ_ASSERT(digit >= '0' && digit <= '9');
channels += digit - '0';
}
else
{
d->tag.setTrackerName("NoiseTracker"); // probably
channels = 4;
instruments = 15;
}
d->properties.setChannels(channels);
d->properties.setInstrumentCount(instruments);
seek(0);
READ_STRING(d->tag.setTitle, 20);
StringList comment;
for(int i = 0; i < instruments; ++ i)
{
READ_STRING_AS(instrumentName, 22);
READ_U16B_AS(instrumentLength);
READ_BYTE_AS(fineTuneByte);
int fineTune = fineTuneByte & 0xF;
// > 7 means nagative value
if(fineTune > 7) fineTune -= 16;
READ_BYTE_AS(volume);
if(volume > 64) volume = 64;
READ_U16B_AS(repeatStart);
// (int)repatStart << 1;
READ_U16B_AS(repatLength);
// (int)repatLength << 1;
comment.append(instrumentName);
}
READ_BYTE(d->properties.setPatternCount);
d->tag.setComment(comment.toString("\n"));
}

89
taglib/mod/modfile.h Normal file
View File

@ -0,0 +1,89 @@
/***************************************************************************
copyright : (C) 2011 by Mathias Panzenböck
email : grosser.meister.morti@gmx.net
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#ifndef TAGLIB_MODFILE_H
#define TAGLIB_MODFILE_H
#include "tfile.h"
#include "audioproperties.h"
#include "taglib_export.h"
#include "modfilebase.h"
#include "modtag.h"
#include "modproperties.h"
namespace TagLib {
namespace Mod {
class TAGLIB_EXPORT File : public TagLib::Mod::FileBase {
public:
/*!
* Contructs a Protracker file from \a file. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Contructs a Protracker file from \a stream. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
Mod::Tag *tag() const;
/*!
* Returns the Mod::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Mod::Properties *audioProperties() const;
/*!
* Save the file.
* This is the same as calling save(AllTags);
*
* \note Saving Protracker tags is not supported.
*/
bool save();
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@ -32,10 +32,10 @@ Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
{
}
void Mod::FileBase::writeString(const String &s, ulong size)
void Mod::FileBase::writeString(const String &s, ulong size, char padding)
{
ByteVector data(s.data(String::Latin1));
data.resize(size, 0);
data.resize(size, padding);
writeBlock(data);
}
@ -76,3 +76,18 @@ bool Mod::FileBase::readU32L(ulong &number) {
number = data.toUInt(false);
return true;
}
bool Mod::FileBase::readU16B(ushort &number)
{
ByteVector data(readBlock(2));
if(data.size() < 2) return false;
number = data.toUShort(true);
return true;
}
bool Mod::FileBase::readU32B(ulong &number) {
ByteVector data(readBlock(4));
if(data.size() < 4) return false;
number = data.toUInt(true);
return true;
}

View File

@ -19,8 +19,8 @@
* MA 02110-1301 USA *
***************************************************************************/
#ifndef TAGLIB_MODFILE_H
#define TAGLIB_MODFILE_H
#ifndef TAGLIB_MODFILEBASE_H
#define TAGLIB_MODFILEBASE_H
#include "taglib.h"
#include "tfile.h"
@ -34,11 +34,13 @@ namespace TagLib {
FileBase(FileName file);
FileBase(IOStream *stream);
void writeString(const String &s, ulong size);
void writeString(const String &s, ulong size, char padding = 0);
bool readString(String &s, ulong size);
bool readByte(uchar &byte);
bool readU16L(ushort &number);
bool readU32L(ulong &number);
bool readU16B(ushort &number);
bool readU32B(ulong &number);
};
}
}

View File

@ -40,6 +40,8 @@
#define READ_BYTE(setter) READ(setter,uchar,readByte)
#define READ_U16L(setter) READ(setter,ushort,readU16L)
#define READ_U32L(setter) READ(setter,ulong,readU32L)
#define READ_U16B(setter) READ(setter,ushort,readU16B)
#define READ_U32B(setter) READ(setter,ulong,readU32B)
#define READ_STRING(setter,size) \
{ \
@ -55,6 +57,8 @@
#define READ_BYTE_AS(name) READ_AS(uchar,name,readByte)
#define READ_U16L_AS(name) READ_AS(ushort,name,readU16L)
#define READ_U32L_AS(name) READ_AS(ulong,name,readU32L)
#define READ_U16B_AS(name) READ_AS(ushort,name,readU16B)
#define READ_U32B_AS(name) READ_AS(ulong,name,readU32B)
#define READ_STRING_AS(name,size) \
String name; \

View File

@ -19,102 +19,78 @@
* MA 02110-1301 USA *
***************************************************************************/
#include "xmtag.h"
#include "modproperties.h"
using namespace TagLib;
using namespace XM;
using namespace Mod;
class XM::Tag::TagPrivate
class Mod::Properties::PropertiesPrivate
{
public:
TagPrivate() {}
String title;
String comment;
String trackerName;
PropertiesPrivate() :
channels(0),
instrumentCount(0),
patternCount(0)
{
}
int channels;
uint instrumentCount;
uint patternCount;
};
XM::Tag::Tag() : TagLib::Tag()
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle),
d(new PropertiesPrivate)
{
d = new TagPrivate;
}
XM::Tag::~Tag()
Mod::Properties::~Properties()
{
delete d;
}
String XM::Tag::title() const
{
return d->title;
}
String XM::Tag::artist() const
{
return String::null;
}
String XM::Tag::album() const
{
return String::null;
}
String XM::Tag::comment() const
{
return d->comment;
}
String XM::Tag::genre() const
{
return String::null;
}
uint XM::Tag::year() const
int Mod::Properties::length() const
{
return 0;
}
uint XM::Tag::track() const
int Mod::Properties::bitrate() const
{
return 0;
}
String XM::Tag::trackerName() const
int Mod::Properties::sampleRate() const
{
return d->trackerName;
return 0;
}
void XM::Tag::setTitle(const String &title)
int Mod::Properties::channels() const
{
d->title = title;
return d->channels;
}
void XM::Tag::setArtist(const String &)
uint Mod::Properties::instrumentCount() const
{
return d->instrumentCount;
}
void XM::Tag::setAlbum(const String &)
uint Mod::Properties::patternCount() const
{
return d->patternCount;
}
void XM::Tag::setComment(const String &comment)
void Mod::Properties::setChannels(int channels)
{
d->comment = comment;
d->channels = channels;
}
void XM::Tag::setGenre(const String &)
void Mod::Properties::setInstrumentCount(uint instrumentCount)
{
d->instrumentCount = instrumentCount;
}
void XM::Tag::setYear(uint)
void Mod::Properties::setPatternCount(uint patternCount)
{
}
void XM::Tag::setTrack(uint)
{
}
void XM::Tag::setTrackerName(const String &trackerName)
{
d->trackerName = trackerName;
d->patternCount = patternCount;
}

View File

@ -19,42 +19,40 @@
* MA 02110-1301 USA *
***************************************************************************/
#ifndef TAGLIB_XMTAG_H
#define TAGLIB_XMTAG_H
#ifndef TAGLIB_MODPROPERTIES_H
#define TAGLIB_MODPROPERTIES_H
#include "modtag.h"
#include "taglib.h"
#include "audioproperties.h"
namespace TagLib {
namespace XM {
class Tag : public TagLib::Tag {
public:
Tag();
virtual ~Tag();
namespace Mod {
class TAGLIB_EXPORT Properties : public AudioProperties {
friend class File;
public:
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
int length() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
String title() const;
String artist() const;
String album() const;
String comment() const;
String genre() const;
uint year() const;
uint track() const;
String trackerName() const;
uint instrumentCount() const;
uint patternCount() const;
void setTitle (const String &title);
void setArtist (const String &artist);
void setAlbum (const String &album);
void setComment(const String &comment);
void setGenre (const String &genre);
void setYear (uint year);
void setTrack(uint track);
void setTrackerName(const String &trackerName);
protected:
void setChannels(int channels);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
void setInstrumentCount(uint sampleCount);
void setPatternCount(uint patternCount);
class TagPrivate;
TagPrivate *d;
private:
Properties(const Properties&);
Properties &operator=(const Properties&);
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}

View File

@ -31,6 +31,7 @@ public:
String title;
String comment;
String trackerName;
};
Mod::Tag::Tag() : TagLib::Tag()
@ -78,6 +79,11 @@ uint Mod::Tag::track() const
return 0;
}
String Mod::Tag::trackerName() const
{
return d->trackerName;
}
void Mod::Tag::setTitle(const String &title)
{
d->title = title;
@ -107,3 +113,8 @@ void Mod::Tag::setYear(uint)
void Mod::Tag::setTrack(uint)
{
}
void Mod::Tag::setTrackerName(const String &trackerName)
{
d->trackerName = trackerName;
}

View File

@ -38,6 +38,7 @@ namespace TagLib {
String genre() const;
uint year() const;
uint track() const;
String trackerName() const;
void setTitle (const String &title);
void setArtist (const String &artist);
@ -46,6 +47,7 @@ namespace TagLib {
void setGenre (const String &genre);
void setYear (uint year);
void setTrack(uint track);
void setTrackerName(const String &trackerName);
private:
Tag(const Tag &);

View File

@ -106,7 +106,7 @@ void S3M::File::read(bool)
READ_ASSERT(readBlock(4) == "SCRM");
READ_BYTE_AS(baseVolume);
d->properties.setBaseVolume(baseVolume << 1);
d->properties.setBaseVolume((int)baseVolume << 1);
READ_BYTE(d->properties.setTempo);
READ_BYTE(d->properties.setBpmSpeed);
@ -133,10 +133,10 @@ void S3M::File::read(bool)
StringList comment;
for(ushort i = 0; i < sampleCount; ++ i)
{
seek(96 + length + (i << 1));
seek(96L + length + ((long)i << 1));
READ_U16L_AS(instrumentOffset);
seek(instrumentOffset << 4);
seek((long)instrumentOffset << 4);
READ_BYTE_AS(sampleType);
READ_STRING_AS(dosFileName, 13);
@ -158,4 +158,5 @@ void S3M::File::read(bool)
}
d->tag.setComment(comment.toString("\n"));
d->tag.setTrackerName("ScreamTracker III");
}

View File

@ -22,8 +22,6 @@
#ifndef TAGLIB_S3MFILE_H
#define TAGLIB_S3MFILE_H
#include <stdint.h>
#include "tfile.h"
#include "audioproperties.h"
#include "taglib_export.h"

View File

@ -36,7 +36,7 @@ public:
{
}
XM::Tag tag;
Mod::Tag tag;
XM::Properties properties;
};
@ -61,7 +61,7 @@ XM::File::~File()
delete d;
}
XM::Tag *XM::File::tag() const
Mod::Tag *XM::File::tag() const
{
return &d->tag;
}
@ -131,27 +131,15 @@ void XM::File::read(bool)
if(instrumentSize > 4)
{
if(!readString(instrumentName, std::min(22UL, instrumentSize-4)))
{
setValid(false);
return;
}
READ_ASSERT(readString(instrumentName, std::min(22UL, instrumentSize-4)));
if(instrumentSize >= (4+22+1))
{
if(!readByte(instrumentType))
{
setValid(false);
return;
}
READ_ASSERT(readByte(instrumentType));
if(instrumentSize >= (4+22+1+2))
{
if(!readU16L(sampleCount))
{
setValid(false);
return;
}
READ_ASSERT(readU16L(sampleCount));
}
}
}

View File

@ -22,13 +22,11 @@
#ifndef TAGLIB_XMFILE_H
#define TAGLIB_XMFILE_H
#include <stdint.h>
#include "tfile.h"
#include "audioproperties.h"
#include "taglib_export.h"
#include "modfilebase.h"
#include "xmtag.h"
#include "modtag.h"
#include "xmproperties.h"
namespace TagLib {
@ -60,7 +58,7 @@ namespace TagLib {
*/
virtual ~File();
XM::Tag *tag() const;
Mod::Tag *tag() const;
/*!
* Returns the XM::Properties for this file. If no audio properties