NowPlaying: Update TagLib to 1.9.1

This commit is contained in:
Birunthan Mohanathas
2013-10-19 21:12:01 +03:00
parent dcdd1151c6
commit cea85dd0f0
144 changed files with 7731 additions and 6329 deletions

View File

@@ -79,13 +79,6 @@ bool CCover::GetEmbedded(const TagLib::FileRef& fr, const std::wstring& target)
found = ExtractAPE(file->APETag(), target); found = ExtractAPE(file->APETag(), target);
} }
} }
else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fr.file()))
{
if (file->tag())
{
found = ExtractMP4(file, target);
}
}
else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fr.file())) else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fr.file()))
{ {
found = ExtractFLAC(file, target); found = ExtractFLAC(file, target);
@@ -113,13 +106,6 @@ bool CCover::GetEmbedded(const TagLib::FileRef& fr, const std::wstring& target)
found = ExtractAPE(file->APETag(), target); found = ExtractAPE(file->APETag(), target);
} }
} }
else if (TagLib::WavPack::File* file = dynamic_cast<TagLib::WavPack::File*>(fr.file()))
{
if (file->APETag())
{
found = ExtractAPE(file->APETag(), target);
}
}
return found; return found;
} }
@@ -222,25 +208,6 @@ bool CCover::ExtractFLAC(TagLib::FLAC::File* file, const std::wstring& target)
return false; return false;
} }
/*
** Extracts cover art embedded in MP4-like files.
**
*/
bool CCover::ExtractMP4(TagLib::MP4::File* file, const std::wstring& target)
{
TagLib::MP4::Tag* tag = file->tag();
if (tag->itemListMap().contains("covr"))
{
TagLib::MP4::CoverArtList coverList = tag->itemListMap()["covr"].toCoverArtList();
if (coverList[0].data().size() > 0)
{
return WriteCover(coverList[0].data(), target);
}
}
return false;
}
/* /*
** Write cover data to file. ** Write cover data to file.
** **

View File

@@ -30,14 +30,12 @@
#include "id3v1genres.h" #include "id3v1genres.h"
#include "id3v2tag.h" #include "id3v2tag.h"
#include "mpcfile.h" #include "mpcfile.h"
#include "mp4file.h"
#include "mpegfile.h" #include "mpegfile.h"
#include "tag.h" #include "tag.h"
#include "taglib.h" #include "taglib.h"
#include "textidentificationframe.h" #include "textidentificationframe.h"
#include "tstring.h" #include "tstring.h"
#include "vorbisfile.h" #include "vorbisfile.h"
#include "wavpackfile.h"
class CCover class CCover
{ {
@@ -52,7 +50,6 @@ private:
static bool ExtractID3(TagLib::ID3v2::Tag* tag, const std::wstring& target); static bool ExtractID3(TagLib::ID3v2::Tag* tag, const std::wstring& target);
static bool ExtractASF(TagLib::ASF::File* file, const std::wstring& target); static bool ExtractASF(TagLib::ASF::File* file, const std::wstring& target);
static bool ExtractFLAC(TagLib::FLAC::File* file, const std::wstring& target); static bool ExtractFLAC(TagLib::FLAC::File* file, const std::wstring& target);
static bool ExtractMP4(TagLib::MP4::File* file, const std::wstring& target);
static bool WriteCover(const TagLib::ByteVector& data, const std::wstring& target); static bool WriteCover(const TagLib::ByteVector& data, const std::wstring& target);
}; };

View File

@@ -18,20 +18,19 @@
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <ClCompile>
<PreprocessorDefinitions>_USRDLL;HAVE_CONFIG_H;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_USRDLL;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<DisableSpecificWarnings>4018;4090;4114;4351;4786;4800;4996</DisableSpecificWarnings> <AdditionalIncludeDirectories>./taglib;./taglib/toolkit;./taglib/asf;./taglib/mpeg;./taglib/ogg;./taglib/ogg/flac;./taglib/flac;./taglib/mpc;./taglib/mp4;./taglib/ogg/vorbis;./taglib/mpeg/id3v2;./taglib/mpeg/id3v2/frames;./taglib/mpeg/id3v1;./taglib/ape;./taglib/wavpack;.\SDKs\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.\sha2;.\taglib;.\taglib\toolkit;.\taglib\mpeg\id3v2\frames;.\taglib\ogg;.\taglib\asf;.\taglib\mp4;.\taglib\ogg\vorbis;.\taglib\ogg\flac;.\taglib\ogg\speex;.\taglib\riff;.\taglib\riff\wav;.\taglib\riff\aiff;.\taglib\flac;.\taglib\mpeg;.\taglib\mpeg\id3v1;.\taglib\mpeg\id3v2;.\taglib\mpc;.\taglib\ape;.\taglib\trueaudio;.\taglib\wavpack;.\SDKs\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <DisableSpecificWarnings>4351;4800;4273;4251</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>Psapi.lib;WinInet.lib;comsuppwd.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>Psapi.lib;WinInet.lib;comsuppwd.lib;D:\Biru\Downloads\taglib-1.9.1.tar\taglib-1.9.1\taglib-1.9.1\taglib\Debug\tag.lib;%(AdditionalDependencies)</AdditionalDependencies>
<RuntimeTypeInfo>true</RuntimeTypeInfo> <RuntimeTypeInfo>true</RuntimeTypeInfo>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
<PreprocessorDefinitions>_USRDLL;HAVE_CONFIG_H;NowPlaying_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_USRDLL;NowPlaying_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<DisableSpecificWarnings>4018;4090;4114;4351;4786;4800;4996</DisableSpecificWarnings> <AdditionalIncludeDirectories>./taglib;./taglib/toolkit;./taglib/asf;./taglib/mpeg;./taglib/ogg;./taglib/ogg/flac;./taglib/flac;./taglib/mpc;./taglib/mp4;./taglib/ogg/vorbis;./taglib/mpeg/id3v2;./taglib/mpeg/id3v2/frames;./taglib/mpeg/id3v1;./taglib/ape;./taglib/wavpack;.\SDKs\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.\sha2;.\taglib;.\taglib\toolkit;.\taglib\mpeg\id3v2\frames;.\taglib\ogg;.\taglib\asf;.\taglib\mp4;.\taglib\ogg\vorbis;.\taglib\ogg\flac;.\taglib\ogg\speex;.\taglib\riff;.\taglib\riff\wav;.\taglib\riff\aiff;.\taglib\flac;.\taglib\mpeg;.\taglib\mpeg\id3v1;.\taglib\mpeg\id3v2;.\taglib\mpc;.\taglib\ape;.\taglib\trueaudio;.\taglib\wavpack;.\SDKs\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>Psapi.lib;WinInet.lib;comsuppwd.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>Psapi.lib;WinInet.lib;comsuppwd.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -40,9 +39,8 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
<PreprocessorDefinitions>_USRDLL;HAVE_CONFIG_H;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_USRDLL;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<DisableSpecificWarnings>4018;4090;4099;4114;4244;4267;4309;4351;4390;4530;4786;4800;4996</DisableSpecificWarnings> <AdditionalIncludeDirectories>./taglib;./taglib/toolkit;./taglib/asf;./taglib/mpeg;./taglib/ogg;./taglib/ogg/flac;./taglib/flac;./taglib/mpc;./taglib/mp4;./taglib/ogg/vorbis;./taglib/mpeg/id3v2;./taglib/mpeg/id3v2/frames;./taglib/mpeg/id3v1;./taglib/ape;./taglib/wavpack;.\SDKs\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.\sha2;.\taglib;.\taglib\toolkit;.\taglib\mpeg\id3v2\frames;.\taglib\ogg;.\taglib\asf;.\taglib\mp4;.\taglib\ogg\vorbis;.\taglib\ogg\flac;.\taglib\ogg\speex;.\taglib\riff;.\taglib\riff\wav;.\taglib\riff\aiff;.\taglib\flac;.\taglib\mpeg;.\taglib\mpeg\id3v1;.\taglib\mpeg\id3v2;.\taglib\mpc;.\taglib\ape;.\taglib\trueaudio;.\taglib\wavpack;.\SDKs\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<ExceptionHandling>false</ExceptionHandling> <ExceptionHandling>false</ExceptionHandling>
<MultiProcessorCompilation>true</MultiProcessorCompilation> <MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile> </ClCompile>
@@ -53,11 +51,11 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
<PreprocessorDefinitions>_USRDLL;HAVE_CONFIG_H;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_USRDLL;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<DisableSpecificWarnings>4018;4090;4099;4114;4244;4267;4309;4351;4390;4530;4786;4800;4996</DisableSpecificWarnings> <AdditionalIncludeDirectories>./taglib;./taglib/toolkit;./taglib/asf;./taglib/mpeg;./taglib/ogg;./taglib/ogg/flac;./taglib/flac;./taglib/mpc;./taglib/mp4;./taglib/ogg/vorbis;./taglib/mpeg/id3v2;./taglib/mpeg/id3v2/frames;./taglib/mpeg/id3v1;./taglib/ape;./taglib/wavpack;.\SDKs\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.\sha2;.\taglib;.\taglib\toolkit;.\taglib\mpeg\id3v2\frames;.\taglib\ogg;.\taglib\asf;.\taglib\mp4;.\taglib\ogg\vorbis;.\taglib\ogg\flac;.\taglib\ogg\speex;.\taglib\riff;.\taglib\riff\wav;.\taglib\riff\aiff;.\taglib\flac;.\taglib\mpeg;.\taglib\mpeg\id3v1;.\taglib\mpeg\id3v2;.\taglib\mpc;.\taglib\ape;.\taglib\trueaudio;.\taglib\wavpack;.\SDKs\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<ExceptionHandling>false</ExceptionHandling> <ExceptionHandling>false</ExceptionHandling>
<MultiProcessorCompilation>true</MultiProcessorCompilation> <MultiProcessorCompilation>true</MultiProcessorCompilation>
<DisableSpecificWarnings>4351;4800;4267</DisableSpecificWarnings>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>Psapi.lib;WinInet.lib;comsuppw.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>Psapi.lib;WinInet.lib;comsuppw.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -65,6 +63,7 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\..\Downloads\taglib-1.9.1.tar\taglib-1.9.1\taglib-1.9.1\taglib\toolkit\tpropertymap.cpp" />
<ClCompile Include="Cover.cpp" /> <ClCompile Include="Cover.cpp" />
<ClCompile Include="Internet.cpp" /> <ClCompile Include="Internet.cpp" />
<ClCompile Include="Lyrics.cpp" /> <ClCompile Include="Lyrics.cpp" />
@@ -79,75 +78,9 @@
<ClCompile Include="PlayerWMP.cpp" /> <ClCompile Include="PlayerWMP.cpp" />
<ClCompile Include="SDKs\iTunes\iTunesCOMInterface_i.c" /> <ClCompile Include="SDKs\iTunes\iTunesCOMInterface_i.c" />
<ClCompile Include="StdAfx.cpp" /> <ClCompile Include="StdAfx.cpp" />
<ClCompile Include="taglib\ape\apefile.cpp" />
<ClCompile Include="taglib\ape\apefooter.cpp" />
<ClCompile Include="taglib\ape\apeitem.cpp" />
<ClCompile Include="taglib\ape\apeproperties.cpp" />
<ClCompile Include="taglib\ape\apetag.cpp" />
<ClCompile Include="taglib\asf\asfattribute.cpp" />
<ClCompile Include="taglib\asf\asffile.cpp" />
<ClCompile Include="taglib\asf\asfpicture.cpp" />
<ClCompile Include="taglib\asf\asfproperties.cpp" />
<ClCompile Include="taglib\asf\asftag.cpp" />
<ClCompile Include="taglib\audioproperties.cpp" />
<ClCompile Include="taglib\fileref.cpp" />
<ClCompile Include="taglib\flac\flacfile.cpp" />
<ClCompile Include="taglib\flac\flacmetadatablock.cpp" />
<ClCompile Include="taglib\flac\flacpicture.cpp" />
<ClCompile Include="taglib\flac\flacproperties.cpp" />
<ClCompile Include="taglib\flac\flacunknownmetadatablock.cpp" />
<ClCompile Include="taglib\mp4\mp4atom.cpp" />
<ClCompile Include="taglib\mp4\mp4coverart.cpp" />
<ClCompile Include="taglib\mp4\mp4file.cpp" />
<ClCompile Include="taglib\mp4\mp4item.cpp" />
<ClCompile Include="taglib\mp4\mp4properties.cpp" />
<ClCompile Include="taglib\mp4\mp4tag.cpp" />
<ClCompile Include="taglib\mpc\mpcfile.cpp" />
<ClCompile Include="taglib\mpc\mpcproperties.cpp" />
<ClCompile Include="taglib\mpeg\id3v1\id3v1genres.cpp" />
<ClCompile Include="taglib\mpeg\id3v1\id3v1tag.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\attachedpictureframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\commentsframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\generalencapsulatedobjectframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\popularimeterframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\privateframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\relativevolumeframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\textidentificationframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\uniquefileidentifierframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\unknownframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\unsynchronizedlyricsframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\urllinkframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2extendedheader.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2footer.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2frame.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2framefactory.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2header.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2synchdata.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2tag.cpp" />
<ClCompile Include="taglib\mpeg\mpegfile.cpp" />
<ClCompile Include="taglib\mpeg\mpegheader.cpp" />
<ClCompile Include="taglib\mpeg\mpegproperties.cpp" />
<ClCompile Include="taglib\mpeg\xingheader.cpp" />
<ClCompile Include="taglib\ogg\flac\oggflacfile.cpp" />
<ClCompile Include="taglib\ogg\oggfile.cpp" />
<ClCompile Include="taglib\ogg\oggpage.cpp" />
<ClCompile Include="taglib\ogg\oggpageheader.cpp" />
<ClCompile Include="taglib\ogg\vorbis\vorbisfile.cpp" />
<ClCompile Include="taglib\ogg\vorbis\vorbisproperties.cpp" />
<ClCompile Include="taglib\ogg\xiphcomment.cpp" />
<ClCompile Include="taglib\tag.cpp" />
<ClCompile Include="taglib\tagunion.cpp" />
<ClCompile Include="taglib\toolkit\tbytevector.cpp" />
<ClCompile Include="taglib\toolkit\tbytevectorlist.cpp" />
<ClCompile Include="taglib\toolkit\tdebug.cpp" />
<ClCompile Include="taglib\toolkit\tfile.cpp" />
<ClCompile Include="taglib\toolkit\tstring.cpp" />
<ClCompile Include="taglib\toolkit\tstringlist.cpp" />
<ClCompile Include="taglib\toolkit\unicode.cpp" />
<ClCompile Include="taglib\wavpack\wavpackfile.cpp" />
<ClCompile Include="taglib\wavpack\wavpackproperties.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\..\..\Downloads\taglib-1.9.1.tar\taglib-1.9.1\taglib-1.9.1\taglib\toolkit\tpropertymap.h" />
<ClInclude Include="Cover.h" /> <ClInclude Include="Cover.h" />
<ClInclude Include="Internet.h" /> <ClInclude Include="Internet.h" />
<ClInclude Include="Lyrics.h" /> <ClInclude Include="Lyrics.h" />
@@ -169,6 +102,139 @@
<ClInclude Include="SDKs\Winamp\wa_ipc.h" /> <ClInclude Include="SDKs\Winamp\wa_ipc.h" />
<ClInclude Include="StdAfx.h" /> <ClInclude Include="StdAfx.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClInclude Include="taglib\tag.h" />
<ClInclude Include="taglib\fileref.h" />
<ClInclude Include="taglib\audioproperties.h" />
<ClInclude Include="taglib\taglib_export.h" />
<ClInclude Include="taglib\toolkit\taglib.h" />
<ClInclude Include="taglib\toolkit\tstring.h" />
<ClInclude Include="taglib\toolkit\tlist.h" />
<ClInclude Include="taglib\toolkit\tstringlist.h" />
<ClInclude Include="taglib\toolkit\tbytevector.h" />
<ClInclude Include="taglib\toolkit\tbytevectorlist.h" />
<ClInclude Include="taglib\toolkit\tbytevectorstream.h" />
<ClInclude Include="taglib\toolkit\tiostream.h" />
<ClInclude Include="taglib\toolkit\tfile.h" />
<ClInclude Include="taglib\toolkit\tfilestream.h" />
<ClInclude Include="taglib\toolkit\tmap.h" />
<ClInclude Include="taglib\toolkit\trefcounter.h" />
<ClInclude Include="taglib\toolkit\tdebuglistener.h" />
<ClInclude Include="taglib\mpeg\mpegfile.h" />
<ClInclude Include="taglib\mpeg\mpegproperties.h" />
<ClInclude Include="taglib\mpeg\mpegheader.h" />
<ClInclude Include="taglib\mpeg\xingheader.h" />
<ClInclude Include="taglib\mpeg\id3v1\id3v1tag.h" />
<ClInclude Include="taglib\mpeg\id3v1\id3v1genres.h" />
<ClInclude Include="taglib\mpeg\id3v2\id3v2extendedheader.h" />
<ClInclude Include="taglib\mpeg\id3v2\id3v2frame.h" />
<ClInclude Include="taglib\mpeg\id3v2\id3v2header.h" />
<ClInclude Include="taglib\mpeg\id3v2\id3v2synchdata.h" />
<ClInclude Include="taglib\mpeg\id3v2\id3v2footer.h" />
<ClInclude Include="taglib\mpeg\id3v2\id3v2framefactory.h" />
<ClInclude Include="taglib\mpeg\id3v2\id3v2tag.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\attachedpictureframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\commentsframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\generalencapsulatedobjectframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\ownershipframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\popularimeterframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\privateframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\relativevolumeframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\textidentificationframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\uniquefileidentifierframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\unknownframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\unsynchronizedlyricsframe.h" />
<ClInclude Include="taglib\mpeg\id3v2\frames\urllinkframe.h" />
<ClInclude Include="taglib\ogg\oggfile.h" />
<ClInclude Include="taglib\ogg\oggpage.h" />
<ClInclude Include="taglib\ogg\oggpageheader.h" />
<ClInclude Include="taglib\ogg\xiphcomment.h" />
<ClInclude Include="taglib\ogg\vorbis\vorbisfile.h" />
<ClInclude Include="taglib\ogg\vorbis\vorbisproperties.h" />
<ClInclude Include="taglib\ogg\flac\oggflacfile.h" />
<ClInclude Include="taglib\flac\flacfile.h" />
<ClInclude Include="taglib\flac\flacpicture.h" />
<ClInclude Include="taglib\flac\flacproperties.h" />
<ClInclude Include="taglib\flac\flacmetadatablock.h" />
<ClInclude Include="taglib\ape\apefile.h" />
<ClInclude Include="taglib\ape\apeproperties.h" />
<ClInclude Include="taglib\ape\apetag.h" />
<ClInclude Include="taglib\ape\apefooter.h" />
<ClInclude Include="taglib\ape\apeitem.h" />
<ClInclude Include="taglib\mpc\mpcfile.h" />
<ClInclude Include="taglib\mpc\mpcproperties.h" />
<ClInclude Include="taglib\riff\wav\infotag.h" />
<ClInclude Include="taglib\asf\asffile.h" />
<ClInclude Include="taglib\asf\asfproperties.h" />
<ClInclude Include="taglib\asf\asftag.h" />
<ClInclude Include="taglib\asf\asfattribute.h" />
<ClInclude Include="taglib\asf\asfpicture.h" />
<ClCompile Include="taglib\mpeg\mpegfile.cpp" />
<ClCompile Include="taglib\mpeg\mpegproperties.cpp" />
<ClCompile Include="taglib\mpeg\mpegheader.cpp" />
<ClCompile Include="taglib\mpeg\xingheader.cpp" />
<ClCompile Include="taglib\mpeg\id3v1\id3v1tag.cpp" />
<ClCompile Include="taglib\mpeg\id3v1\id3v1genres.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2framefactory.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2synchdata.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2tag.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2header.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2frame.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2footer.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\id3v2extendedheader.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\attachedpictureframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\commentsframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\generalencapsulatedobjectframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\ownershipframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\popularimeterframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\privateframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\relativevolumeframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\textidentificationframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\uniquefileidentifierframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\unknownframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\unsynchronizedlyricsframe.cpp" />
<ClCompile Include="taglib\mpeg\id3v2\frames\urllinkframe.cpp" />
<ClCompile Include="taglib\ogg\oggfile.cpp" />
<ClCompile Include="taglib\ogg\oggpage.cpp" />
<ClCompile Include="taglib\ogg\oggpageheader.cpp" />
<ClCompile Include="taglib\ogg\xiphcomment.cpp" />
<ClCompile Include="taglib\ogg\vorbis\vorbisfile.cpp" />
<ClCompile Include="taglib\ogg\vorbis\vorbisproperties.cpp" />
<ClCompile Include="taglib\ogg\flac\oggflacfile.cpp" />
<ClCompile Include="taglib\mpc\mpcfile.cpp" />
<ClCompile Include="taglib\mpc\mpcproperties.cpp" />
<ClCompile Include="taglib\ape\apetag.cpp" />
<ClCompile Include="taglib\ape\apefooter.cpp" />
<ClCompile Include="taglib\ape\apeitem.cpp" />
<ClCompile Include="taglib\ape\apefile.cpp" />
<ClCompile Include="taglib\ape\apeproperties.cpp" />
<ClCompile Include="taglib\toolkit\tstring.cpp" />
<ClCompile Include="taglib\toolkit\tstringlist.cpp" />
<ClCompile Include="taglib\toolkit\tbytevector.cpp" />
<ClCompile Include="taglib\toolkit\tbytevectorlist.cpp" />
<ClCompile Include="taglib\toolkit\tbytevectorstream.cpp" />
<ClCompile Include="taglib\toolkit\tiostream.cpp" />
<ClCompile Include="taglib\toolkit\tfile.cpp" />
<ClCompile Include="taglib\toolkit\tfilestream.cpp" />
<ClCompile Include="taglib\toolkit\tdebug.cpp" />
<ClCompile Include="taglib\toolkit\trefcounter.cpp" />
<ClCompile Include="taglib\toolkit\tdebuglistener.cpp" />
<ClCompile Include="taglib\toolkit\unicode.cpp" />
<ClCompile Include="taglib\flac\flacfile.cpp" />
<ClCompile Include="taglib\flac\flacpicture.cpp" />
<ClCompile Include="taglib\flac\flacproperties.cpp" />
<ClCompile Include="taglib\flac\flacmetadatablock.cpp" />
<ClCompile Include="taglib\flac\flacunknownmetadatablock.cpp" />
<ClCompile Include="taglib\asf\asftag.cpp" />
<ClCompile Include="taglib\asf\asffile.cpp" />
<ClCompile Include="taglib\asf\asfproperties.cpp" />
<ClCompile Include="taglib\asf\asfattribute.cpp" />
<ClCompile Include="taglib\asf\asfpicture.cpp" />
<ClCompile Include="taglib\tag.cpp" />
<ClCompile Include="taglib\tagunion.cpp" />
<ClCompile Include="taglib\fileref.cpp" />
<ClCompile Include="taglib\audioproperties.cpp" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="PluginNowPlaying.rc" /> <ResourceCompile Include="PluginNowPlaying.rc" />
</ItemGroup> </ItemGroup>

View File

@@ -30,207 +30,6 @@
<ClCompile Include="SDKs\iTunes\iTunesCOMInterface_i.c"> <ClCompile Include="SDKs\iTunes\iTunesCOMInterface_i.c">
<Filter>SDKs\iTunes</Filter> <Filter>SDKs\iTunes</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="taglib\ape\apefile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ape\apefooter.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ape\apeitem.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ape\apeproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ape\apetag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asfattribute.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asfpicture.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asfproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asftag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\attachedpictureframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\audioproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\commentsframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacmetadatablock.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacpicture.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacunknownmetadatablock.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v1\id3v1genres.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v1\id3v1tag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2extendedheader.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2footer.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2frame.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2framefactory.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2header.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2synchdata.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2tag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mp4\mp4atom.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mp4\mp4coverart.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mp4\mp4file.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mp4\mp4item.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mp4\mp4properties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mp4\mp4tag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\mpegfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\mpegheader.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\mpegproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\oggfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\flac\oggflacfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\oggpage.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\oggpageheader.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\privateframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\tag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\tagunion.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tbytevector.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tbytevectorlist.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tdebug.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\textidentificationframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tstring.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tstringlist.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\unicode.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\uniquefileidentifierframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\unknownframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\urllinkframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\vorbis\vorbisfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\vorbis\vorbisproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\xingheader.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\xiphcomment.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\fileref.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\relativevolumeframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\popularimeterframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\generalencapsulatedobjectframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asffile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\unsynchronizedlyricsframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\wavpack\wavpackproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\wavpack\wavpackfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpc\mpcproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpc\mpcfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="NowPlaying.cpp"> <ClCompile Include="NowPlaying.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@@ -270,6 +69,204 @@
<ClCompile Include="Internet.cpp"> <ClCompile Include="Internet.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="taglib\ape\apefile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ape\apefooter.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ape\apeitem.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ape\apeproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ape\apetag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asfattribute.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asffile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asfpicture.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asfproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\asf\asftag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\attachedpictureframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\audioproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\commentsframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\fileref.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacmetadatablock.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacpicture.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\flac\flacunknownmetadatablock.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\generalencapsulatedobjectframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v1\id3v1genres.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v1\id3v1tag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2extendedheader.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2footer.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2frame.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2framefactory.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2header.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2synchdata.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\id3v2tag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpc\mpcfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpc\mpcproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\mpegfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\mpegheader.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\mpegproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\oggfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\flac\oggflacfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\oggpage.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\oggpageheader.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\ownershipframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\popularimeterframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\privateframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\relativevolumeframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\tag.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\tagunion.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tbytevector.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tbytevectorlist.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tbytevectorstream.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tdebug.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tdebuglistener.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\textidentificationframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tfilestream.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tiostream.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\trefcounter.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tstring.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\tstringlist.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\toolkit\unicode.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\uniquefileidentifierframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\unknownframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\unsynchronizedlyricsframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\id3v2\frames\urllinkframe.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\vorbis\vorbisfile.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\vorbis\vorbisproperties.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\mpeg\xingheader.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="taglib\ogg\xiphcomment.cpp">
<Filter>taglib</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\Downloads\taglib-1.9.1.tar\taglib-1.9.1\taglib-1.9.1\taglib\toolkit\tpropertymap.cpp">
<Filter>taglib</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="SDKs\AIMP\aimp2_sdk.h"> <ClInclude Include="SDKs\AIMP\aimp2_sdk.h">
@@ -332,6 +329,207 @@
<ClInclude Include="Internet.h"> <ClInclude Include="Internet.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="taglib\ape\apefile.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ape\apefooter.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ape\apeitem.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ape\apeproperties.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ape\apetag.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\asf\asfattribute.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\asf\asffile.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\asf\asfpicture.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\asf\asfproperties.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\asf\asftag.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\attachedpictureframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\audioproperties.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\commentsframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\fileref.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\flac\flacfile.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\flac\flacmetadatablock.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\flac\flacpicture.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\flac\flacproperties.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\generalencapsulatedobjectframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v1\id3v1genres.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v1\id3v1tag.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\id3v2extendedheader.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\id3v2footer.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\id3v2frame.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\id3v2framefactory.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\id3v2header.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\id3v2synchdata.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\id3v2tag.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\riff\wav\infotag.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpc\mpcfile.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpc\mpcproperties.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\mpegfile.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\mpegheader.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\mpegproperties.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ogg\oggfile.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ogg\flac\oggflacfile.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ogg\oggpage.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ogg\oggpageheader.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\ownershipframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\popularimeterframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\privateframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\relativevolumeframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\tag.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\taglib.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\taglib_export.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tbytevector.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tbytevectorlist.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tbytevectorstream.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tdebuglistener.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\textidentificationframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tfile.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tfilestream.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tiostream.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tlist.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tmap.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\trefcounter.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tstring.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\toolkit\tstringlist.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\uniquefileidentifierframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\unknownframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\unsynchronizedlyricsframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\id3v2\frames\urllinkframe.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ogg\vorbis\vorbisfile.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ogg\vorbis\vorbisproperties.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\mpeg\xingheader.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="taglib\ogg\xiphcomment.h">
<Filter>taglib</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\Downloads\taglib-1.9.1.tar\taglib-1.9.1\taglib-1.9.1\taglib\toolkit\tpropertymap.h">
<Filter>taglib</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="PluginNowPlaying.rc"> <ResourceCompile Include="PluginNowPlaying.rc">

View File

@@ -36,6 +36,7 @@
#include <tdebug.h> #include <tdebug.h>
#include <tagunion.h> #include <tagunion.h>
#include <id3v1tag.h> #include <id3v1tag.h>
#include <tpropertymap.h>
#include "apefile.h" #include "apefile.h"
@@ -46,7 +47,7 @@ using namespace TagLib;
namespace namespace
{ {
enum { APEIndex, ID3v1Index }; enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
} }
class APE::File::FilePrivate class APE::File::FilePrivate
@@ -89,6 +90,15 @@ APE::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file) Properties::ReadStyle propertiesStyle) : TagLib::File(file)
{ {
d = new FilePrivate; d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
APE::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
@@ -102,6 +112,30 @@ TagLib::Tag *APE::File::tag() const
return &d->tag; return &d->tag;
} }
PropertyMap APE::File::properties() const
{
if(d->hasAPE)
return d->tag.access<APE::Tag>(ApeAPEIndex, false)->properties();
if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->properties();
return PropertyMap();
}
void APE::File::removeUnsupportedProperties(const StringList &properties)
{
if(d->hasAPE)
d->tag.access<APE::Tag>(ApeAPEIndex, false)->removeUnsupportedProperties(properties);
if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->removeUnsupportedProperties(properties);
}
PropertyMap APE::File::setProperties(const PropertyMap &properties)
{
if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
}
APE::Properties *APE::File::audioProperties() const APE::Properties *APE::File::audioProperties() const
{ {
return d->properties; return d->properties;
@@ -178,29 +212,39 @@ bool APE::File::save()
ID3v1::Tag *APE::File::ID3v1Tag(bool create) ID3v1::Tag *APE::File::ID3v1Tag(bool create)
{ {
return d->tag.access<ID3v1::Tag>(ID3v1Index, create); return d->tag.access<ID3v1::Tag>(ApeID3v1Index, create);
} }
APE::Tag *APE::File::APETag(bool create) APE::Tag *APE::File::APETag(bool create)
{ {
return d->tag.access<APE::Tag>(APEIndex, create); return d->tag.access<APE::Tag>(ApeAPEIndex, create);
} }
void APE::File::strip(int tags) void APE::File::strip(int tags)
{ {
if(tags & ID3v1) { if(tags & ID3v1) {
d->tag.set(ID3v1Index, 0); d->tag.set(ApeID3v1Index, 0);
APETag(true); APETag(true);
} }
if(tags & APE) { if(tags & APE) {
d->tag.set(APEIndex, 0); d->tag.set(ApeAPEIndex, 0);
if(!ID3v1Tag()) if(!ID3v1Tag())
APETag(true); APETag(true);
} }
} }
bool APE::File::hasAPETag() const
{
return d->hasAPE;
}
bool APE::File::hasID3v1Tag() const
{
return d->hasID3v1;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -212,7 +256,7 @@ void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
d->ID3v1Location = findID3v1(); d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) { if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true; d->hasID3v1 = true;
} }
@@ -221,7 +265,7 @@ void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
d->APELocation = findAPE(); d->APELocation = findAPE();
if(d->APELocation >= 0) { if(d->APELocation >= 0) {
d->tag.set(APEIndex, new APE::Tag(this, d->APELocation)); d->tag.set(ApeAPEIndex, new APE::Tag(this, d->APELocation));
d->APESize = APETag()->footer()->completeTagSize(); d->APESize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize; d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
d->hasAPE = true; d->hasAPE = true;

View File

@@ -59,7 +59,7 @@ namespace TagLib {
//! An implementation of TagLib::File with APE specific methods //! An implementation of TagLib::File with APE specific methods
/*! /*!
* This implements and provides an interface APE WavPack files to the * This implements and provides an interface for APE files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional * the abstract TagLib::File API as well as providing some additional
* information specific to APE files. * information specific to APE files.
@@ -84,13 +84,26 @@ namespace TagLib {
}; };
/*! /*!
* Contructs an WavPack file from \a file. If \a readProperties is true the * Constructs an APE file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If * file's audio properties will also be read.
* false, \a propertiesStyle is ignored. *
* \note In the current implementation, \a propertiesStyle is ignored.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an APE file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
*/ */
@@ -102,6 +115,26 @@ namespace TagLib {
*/ */
virtual TagLib::Tag *tag() const; virtual TagLib::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains both an APE and an ID3v1 tag, only APE
* will be converted to the PropertyMap.
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* Creates an APEv2 tag if necessary. A pontentially existing ID3v1
* tag will be updated as well.
*/
PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the APE::Properties for this file. If no audio properties * Returns the APE::Properties for this file. If no audio properties
* were read then this will return a null pointer. * were read then this will return a null pointer.
@@ -119,27 +152,38 @@ namespace TagLib {
/*! /*!
* Returns a pointer to the ID3v1 tag of the file. * Returns a pointer to the ID3v1 tag of the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this may return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create * if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist. If there is already an APE tag, the * an ID3v1 tag if one does not exist and returns a valid pointer.
* new ID3v1 tag will be placed after it.
* *
* \note The Tag <b>is still</b> owned by the APE::File and should not be * \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasID3v1Tag()
*/ */
ID3v1::Tag *ID3v1Tag(bool create = false); ID3v1::Tag *ID3v1Tag(bool create = false);
/*! /*!
* Returns a pointer to the APE tag of the file. * Returns a pointer to the APE tag of the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is true it will create * if there is no valid APE tag. If \a create is true it will create
* a APE tag if one does not exist. * an APE tag if one does not exist and returns a valid pointer.
* *
* \note The Tag <b>is still</b> owned by the APE::File and should not be * \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasAPETag()
*/ */
APE::Tag *APETag(bool create = false); APE::Tag *APETag(bool create = false);
@@ -153,6 +197,20 @@ namespace TagLib {
*/ */
void strip(int tags = AllTags); void strip(int tags = AllTags);
/*!
* Returns whether or not the file on disk actually has an APE tag.
*
* \see APETag()
*/
bool hasAPETag() const;
/*!
* Returns whether or not the file on disk actually has an ID3v1 tag.
*
* \see ID3v1Tag()
*/
bool hasID3v1Tag() const;
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@@ -35,7 +35,7 @@
using namespace TagLib; using namespace TagLib;
using namespace APE; using namespace APE;
class Footer::FooterPrivate class APE::Footer::FooterPrivate
{ {
public: public:
FooterPrivate() : version(0), FooterPrivate() : version(0),
@@ -64,12 +64,12 @@ public:
// static members // static members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TagLib::uint Footer::size() TagLib::uint APE::Footer::size()
{ {
return FooterPrivate::size; return FooterPrivate::size;
} }
ByteVector Footer::fileIdentifier() ByteVector APE::Footer::fileIdentifier()
{ {
return ByteVector::fromCString("APETAGEX"); return ByteVector::fromCString("APETAGEX");
} }
@@ -78,63 +78,63 @@ ByteVector Footer::fileIdentifier()
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Footer::Footer() APE::Footer::Footer()
{ {
d = new FooterPrivate; d = new FooterPrivate;
} }
Footer::Footer(const ByteVector &data) APE::Footer::Footer(const ByteVector &data)
{ {
d = new FooterPrivate; d = new FooterPrivate;
parse(data); parse(data);
} }
Footer::~Footer() APE::Footer::~Footer()
{ {
delete d; delete d;
} }
TagLib::uint Footer::version() const TagLib::uint APE::Footer::version() const
{ {
return d->version; return d->version;
} }
bool Footer::headerPresent() const bool APE::Footer::headerPresent() const
{ {
return d->headerPresent; return d->headerPresent;
} }
bool Footer::footerPresent() const bool APE::Footer::footerPresent() const
{ {
return d->footerPresent; return d->footerPresent;
} }
bool Footer::isHeader() const bool APE::Footer::isHeader() const
{ {
return d->isHeader; return d->isHeader;
} }
void Footer::setHeaderPresent(bool b) const void APE::Footer::setHeaderPresent(bool b) const
{ {
d->headerPresent = b; d->headerPresent = b;
} }
TagLib::uint Footer::itemCount() const TagLib::uint APE::Footer::itemCount() const
{ {
return d->itemCount; return d->itemCount;
} }
void Footer::setItemCount(uint s) void APE::Footer::setItemCount(uint s)
{ {
d->itemCount = s; d->itemCount = s;
} }
TagLib::uint Footer::tagSize() const TagLib::uint APE::Footer::tagSize() const
{ {
return d->tagSize; return d->tagSize;
} }
TagLib::uint Footer::completeTagSize() const TagLib::uint APE::Footer::completeTagSize() const
{ {
if(d->headerPresent) if(d->headerPresent)
return d->tagSize + d->size; return d->tagSize + d->size;
@@ -142,22 +142,22 @@ TagLib::uint Footer::completeTagSize() const
return d->tagSize; return d->tagSize;
} }
void Footer::setTagSize(uint s) void APE::Footer::setTagSize(uint s)
{ {
d->tagSize = s; d->tagSize = s;
} }
void Footer::setData(const ByteVector &data) void APE::Footer::setData(const ByteVector &data)
{ {
parse(data); parse(data);
} }
ByteVector Footer::renderFooter() const ByteVector APE::Footer::renderFooter() const
{ {
return render(false); return render(false);
} }
ByteVector Footer::renderHeader() const ByteVector APE::Footer::renderHeader() const
{ {
if (!d->headerPresent) return ByteVector(); if (!d->headerPresent) return ByteVector();
@@ -168,7 +168,7 @@ ByteVector Footer::renderHeader() const
// protected members // protected members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Footer::parse(const ByteVector &data) void APE::Footer::parse(const ByteVector &data)
{ {
if(data.size() < size()) if(data.size() < size())
return; return;
@@ -177,19 +177,19 @@ void Footer::parse(const ByteVector &data)
// Read the version number // Read the version number
d->version = data.mid(8, 4).toUInt(false); d->version = data.toUInt(8, false);
// Read the tag size // Read the tag size
d->tagSize = data.mid(12, 4).toUInt(false); d->tagSize = data.toUInt(12, false);
// Read the item count // Read the item count
d->itemCount = data.mid(16, 4).toUInt(false); d->itemCount = data.toUInt(16, false);
// Read the flags // Read the flags
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(20, 4).toUInt(false))); std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
d->headerPresent = flags[31]; d->headerPresent = flags[31];
d->footerPresent = !flags[30]; d->footerPresent = !flags[30];
@@ -197,7 +197,7 @@ void Footer::parse(const ByteVector &data)
} }
ByteVector Footer::render(bool isHeader) const ByteVector APE::Footer::render(bool isHeader) const
{ {
ByteVector v; ByteVector v;

View File

@@ -62,6 +62,18 @@ APE::Item::Item(const String &key, const StringList &values)
d->text = values; d->text = values;
} }
APE::Item::Item(const String &key, const ByteVector &value, bool binary)
{
d = new ItemPrivate;
d->key = key;
if(binary) {
d->type = Binary;
d->value = value;
}
else
d->text.append(value);
}
APE::Item::Item(const Item &item) APE::Item::Item(const Item &item)
{ {
d = new ItemPrivate(*item.d); d = new ItemPrivate(*item.d);
@@ -104,6 +116,18 @@ String APE::Item::key() const
return d->key; return d->key;
} }
ByteVector APE::Item::binaryData() const
{
return d->value;
}
void APE::Item::setBinaryData(const ByteVector &value)
{
d->type = Binary;
d->value = value;
d->text.clear();
}
ByteVector APE::Item::value() const ByteVector APE::Item::value() const
{ {
// This seems incorrect as it won't be actually rendering the value to keep it // This seems incorrect as it won't be actually rendering the value to keep it
@@ -119,27 +143,54 @@ void APE::Item::setKey(const String &key)
void APE::Item::setValue(const String &value) void APE::Item::setValue(const String &value)
{ {
d->type = Text;
d->text = value; d->text = value;
d->value.clear();
} }
void APE::Item::setValues(const StringList &value) void APE::Item::setValues(const StringList &value)
{ {
d->type = Text;
d->text = value; d->text = value;
d->value.clear();
} }
void APE::Item::appendValue(const String &value) void APE::Item::appendValue(const String &value)
{ {
d->type = Text;
d->text.append(value); d->text.append(value);
d->value.clear();
} }
void APE::Item::appendValues(const StringList &values) void APE::Item::appendValues(const StringList &values)
{ {
d->type = Text;
d->text.append(values); d->text.append(values);
d->value.clear();
} }
int APE::Item::size() const int APE::Item::size() const
{ {
return 8 + d->key.size() + 1 + d->value.size(); // SFB: Why is d->key.size() used when size() returns the length in UniChars and not UTF-8?
int result = 8 + d->key.size() /* d->key.data(String::UTF8).size() */ + 1;
switch (d->type) {
case Text:
if(d->text.size()) {
StringList::ConstIterator it = d->text.begin();
result += it->data(String::UTF8).size();
it++;
for(; it != d->text.end(); ++it)
result += 1 + it->data(String::UTF8).size();
}
break;
case Binary:
case Locator:
result += d->value.size();
break;
}
return result;
} }
StringList APE::Item::toStringList() const StringList APE::Item::toStringList() const
@@ -154,19 +205,22 @@ StringList APE::Item::values() const
String APE::Item::toString() const String APE::Item::toString() const
{ {
return isEmpty() ? String::null : d->text.front(); if(d->type == Text && !isEmpty())
return d->text.front();
else
return String::null;
} }
bool APE::Item::isEmpty() const bool APE::Item::isEmpty() const
{ {
switch(d->type) { switch(d->type) {
case Text: case Text:
case Binary:
if(d->text.isEmpty()) if(d->text.isEmpty())
return true; return true;
if(d->text.size() == 1 && d->text.front().isEmpty()) if(d->text.size() == 1 && d->text.front().isEmpty())
return true; return true;
return false; return false;
case Binary:
case Locator: case Locator:
return d->value.isEmpty(); return d->value.isEmpty();
default: default:
@@ -183,18 +237,20 @@ void APE::Item::parse(const ByteVector &data)
return; return;
} }
uint valueLength = data.mid(0, 4).toUInt(false); const uint valueLength = data.toUInt(0, false);
uint flags = data.mid(4, 4).toUInt(false); const uint flags = data.toUInt(4, false);
d->key = String(data.mid(8), String::UTF8); d->key = String(data.mid(8), String::UTF8);
d->value = data.mid(8 + d->key.size() + 1, valueLength); const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
setReadOnly(flags & 1); setReadOnly(flags & 1);
setType(ItemTypes((flags >> 1) & 3)); setType(ItemTypes((flags >> 1) & 3));
if(int(d->type) < 2) if(Text == d->type)
d->text = StringList(ByteVectorList::split(d->value, '\0'), String::UTF8); d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
else
d->value = value;
} }
ByteVector APE::Item::render() const ByteVector APE::Item::render() const

View File

@@ -59,16 +59,22 @@ namespace TagLib {
Item(); Item();
/*! /*!
* Constructs an item with \a key and \a value. * Constructs a text item with \a key and \a value.
*/ */
// BIC: Remove this, StringList has a constructor from a single string // BIC: Remove this, StringList has a constructor from a single string
Item(const String &key, const String &value); Item(const String &key, const String &value);
/*! /*!
* Constructs an item with \a key and \a values. * Constructs a text item with \a key and \a values.
*/ */
Item(const String &key, const StringList &values); Item(const String &key, const StringList &values);
/*!
* Constructs an item with \a key and \a value.
* If \a binary is true a Binary item will be created, otherwise \a value will be interpreted as text
*/
Item(const String &key, const ByteVector &value, bool binary);
/*! /*!
* Construct an item as a copy of \a item. * Construct an item as a copy of \a item.
*/ */
@@ -91,12 +97,20 @@ namespace TagLib {
/*! /*!
* Returns the binary value. * Returns the binary value.
* * If the item type is not \a Binary, always returns an empty ByteVector.
* \deprecated This will be removed in the next binary incompatible version
* as it is not kept in sync with the things that are set using setValue()
* and friends.
*/ */
ByteVector binaryData() const;
/*!
* Set the binary value to \a value
* The item's type will also be set to \a Binary
*/
void setBinaryData(const ByteVector &value);
#ifndef DO_NOT_DOCUMENT
/* Remove in next binary incompatible release */
ByteVector value() const; ByteVector value() const;
#endif
/*! /*!
* Sets the key for the item to \a key. * Sets the key for the item to \a key.
@@ -104,14 +118,14 @@ namespace TagLib {
void setKey(const String &key); void setKey(const String &key);
/*! /*!
* Sets the value of the item to \a value and clears any previous contents. * Sets the text value of the item to \a value and clears any previous contents.
* *
* \see toString() * \see toString()
*/ */
void setValue(const String &value); void setValue(const String &value);
/*! /*!
* Sets the value of the item to the list of values in \a value and clears * Sets the text value of the item to the list of values in \a value and clears
* any previous contents. * any previous contents.
* *
* \see toStringList() * \see toStringList()
@@ -119,14 +133,14 @@ namespace TagLib {
void setValues(const StringList &values); void setValues(const StringList &values);
/*! /*!
* Appends \a value to create (or extend) the current list of values. * Appends \a value to create (or extend) the current list of text values.
* *
* \see toString() * \see toString()
*/ */
void appendValue(const String &value); void appendValue(const String &value);
/*! /*!
* Appends \a values to extend the current list of values. * Appends \a values to extend the current list of text values.
* *
* \see toStringList() * \see toStringList()
*/ */
@@ -139,18 +153,19 @@ namespace TagLib {
/*! /*!
* Returns the value as a single string. In case of multiple strings, * Returns the value as a single string. In case of multiple strings,
* the first is returned. * the first is returned. If the data type is not \a Text, always returns
* an empty String.
*/ */
String toString() const; String toString() const;
/*! #ifndef DO_NOT_DOCUMENT
* \deprecated /* Remove in next binary incompatible release */
* \see values
*/
StringList toStringList() const; StringList toStringList() const;
#endif
/*! /*!
* Returns the list of values. * Returns the list of text values. If the data type is not \a Text, always
* returns an empty StringList.
*/ */
StringList values() const; StringList values() const;

View File

@@ -46,6 +46,7 @@ public:
channels(0), channels(0),
version(0), version(0),
bitsPerSample(0), bitsPerSample(0),
sampleFrames(0),
file(file), file(file),
streamLength(streamLength) {} streamLength(streamLength) {}
@@ -55,6 +56,7 @@ public:
int channels; int channels;
int version; int version;
int bitsPerSample; int bitsPerSample;
uint sampleFrames;
File *file; File *file;
long streamLength; long streamLength;
}; };
@@ -104,6 +106,11 @@ int APE::Properties::bitsPerSample() const
return d->bitsPerSample; return d->bitsPerSample;
} }
TagLib::uint APE::Properties::sampleFrames() const
{
return d->sampleFrames;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -118,10 +125,10 @@ void APE::Properties::read()
// Then we read the header common for all versions of APE // Then we read the header common for all versions of APE
d->file->seek(offset); d->file->seek(offset);
ByteVector commonHeader=d->file->readBlock(6); ByteVector commonHeader = d->file->readBlock(6);
if(!commonHeader.startsWith("MAC ")) if(!commonHeader.startsWith("MAC "))
return; return;
d->version = commonHeader.mid(4).toUInt(false); d->version = commonHeader.toUShort(4, false);
if(d->version >= 3980) { if(d->version >= 3980) {
analyzeCurrent(); analyzeCurrent();
@@ -137,7 +144,7 @@ long APE::Properties::findDescriptor()
long ID3v2OriginalSize = 0; long ID3v2OriginalSize = 0;
bool hasID3v2 = false; bool hasID3v2 = false;
if(ID3v2Location >= 0) { if(ID3v2Location >= 0) {
ID3v2::Tag tag(d->file, ID3v2Location, 0); ID3v2::Tag tag(d->file, ID3v2Location);
ID3v2OriginalSize = tag.header()->completeTagSize(); ID3v2OriginalSize = tag.header()->completeTagSize();
if(tag.header()->tagSize() > 0) if(tag.header()->tagSize() > 0)
hasID3v2 = true; hasID3v2 = true;
@@ -175,7 +182,7 @@ void APE::Properties::analyzeCurrent()
// Read the descriptor // Read the descriptor
d->file->seek(2, File::Current); d->file->seek(2, File::Current);
ByteVector descriptor = d->file->readBlock(44); ByteVector descriptor = d->file->readBlock(44);
uint descriptorBytes = descriptor.mid(0,4).toUInt(false); const uint descriptorBytes = descriptor.toUInt(0, false);
if ((descriptorBytes - 52) > 0) if ((descriptorBytes - 52) > 0)
d->file->seek(descriptorBytes - 52, File::Current); d->file->seek(descriptorBytes - 52, File::Current);
@@ -184,29 +191,29 @@ void APE::Properties::analyzeCurrent()
ByteVector header = d->file->readBlock(24); ByteVector header = d->file->readBlock(24);
// Get the APE info // Get the APE info
d->channels = header.mid(18, 2).toShort(false); d->channels = header.toShort(18, false);
d->sampleRate = header.mid(20, 4).toUInt(false); d->sampleRate = header.toUInt(20, false);
d->bitsPerSample = header.mid(16, 2).toShort(false); d->bitsPerSample = header.toShort(16, false);
//d->compressionLevel = //d->compressionLevel =
uint totalFrames = header.mid(12, 4).toUInt(false); const uint totalFrames = header.toUInt(12, false);
uint blocksPerFrame = header.mid(4, 4).toUInt(false); const uint blocksPerFrame = header.toUInt(4, false);
uint finalFrameBlocks = header.mid(8, 4).toUInt(false); const uint finalFrameBlocks = header.toUInt(8, false);
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0; d->sampleFrames = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = d->sampleRate > 0 ? totalBlocks / d->sampleRate : 0; d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
} }
void APE::Properties::analyzeOld() void APE::Properties::analyzeOld()
{ {
ByteVector header = d->file->readBlock(26); ByteVector header = d->file->readBlock(26);
uint totalFrames = header.mid(18, 4).toUInt(false); const uint totalFrames = header.toUInt(18, false);
// Fail on 0 length APE files (catches non-finalized APE files) // Fail on 0 length APE files (catches non-finalized APE files)
if(totalFrames == 0) if(totalFrames == 0)
return; return;
short compressionLevel = header.mid(0, 2).toShort(false); const short compressionLevel = header.toShort(0, false);
uint blocksPerFrame; uint blocksPerFrame;
if(d->version >= 3950) if(d->version >= 3950)
blocksPerFrame = 73728 * 4; blocksPerFrame = 73728 * 4;
@@ -214,10 +221,11 @@ void APE::Properties::analyzeOld()
blocksPerFrame = 73728; blocksPerFrame = 73728;
else else
blocksPerFrame = 9216; blocksPerFrame = 9216;
d->channels = header.mid(4, 2).toShort(false); d->channels = header.toShort(4, false);
d->sampleRate = header.mid(6, 4).toUInt(false); d->sampleRate = header.toUInt(6, false);
uint finalFrameBlocks = header.mid(22, 4).toUInt(false); const uint finalFrameBlocks = header.toUInt(22, false);
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0; const uint totalBlocks
= totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = totalBlocks / d->sampleRate; d->length = totalBlocks / d->sampleRate;
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
} }

View File

@@ -71,6 +71,7 @@ namespace TagLib {
* Returns number of bits per sample. * Returns number of bits per sample.
*/ */
int bitsPerSample() const; int bitsPerSample() const;
uint sampleFrames() const;
/*! /*!
* Returns APE version. * Returns APE version.

View File

@@ -34,6 +34,7 @@
#include <tfile.h> #include <tfile.h>
#include <tstring.h> #include <tstring.h>
#include <tmap.h> #include <tmap.h>
#include <tpropertymap.h>
#include "apetag.h" #include "apetag.h"
#include "apefooter.h" #include "apefooter.h"
@@ -47,7 +48,7 @@ class APE::Tag::TagPrivate
public: public:
TagPrivate() : file(0), footerLocation(-1), tagLength(0) {} TagPrivate() : file(0), footerLocation(-1), tagLength(0) {}
File *file; TagLib::File *file;
long footerLocation; long footerLocation;
long tagLength; long tagLength;
@@ -65,7 +66,7 @@ APE::Tag::Tag() : TagLib::Tag()
d = new TagPrivate; d = new TagPrivate;
} }
APE::Tag::Tag(File *file, long footerLocation) : TagLib::Tag() APE::Tag::Tag(TagLib::File *file, long footerLocation) : TagLib::Tag()
{ {
d = new TagPrivate; d = new TagPrivate;
d->file = file; d->file = file;
@@ -174,6 +175,103 @@ void APE::Tag::setTrack(uint i)
addValue("TRACK", String::number(i), true); addValue("TRACK", String::number(i), true);
} }
// conversions of tag keys between what we use in PropertyMap and what's usual
// for APE tags
static const TagLib::uint keyConversionsSize = 5; //usual, APE
static const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
{"DATE", "YEAR" },
{"ALBUMARTIST", "ALBUM ARTIST"},
{"DISCNUMBER", "DISC" },
{"REMIXER", "MIXARTIST" }};
PropertyMap APE::Tag::properties() const
{
PropertyMap properties;
ItemListMap::ConstIterator it = itemListMap().begin();
for(; it != itemListMap().end(); ++it) {
String tagName = it->first.upper();
// if the item is Binary or Locator, or if the key is an invalid string,
// add to unsupportedData
if(it->second.type() != Item::Text || tagName.isNull())
properties.unsupportedData().append(it->first);
else {
// Some tags need to be handled specially
for(uint i = 0; i < keyConversionsSize; ++i)
if(tagName == keyConversions[i][1])
tagName = keyConversions[i][0];
properties[tagName].append(it->second.toStringList());
}
}
return properties;
}
void APE::Tag::removeUnsupportedProperties(const StringList &properties)
{
StringList::ConstIterator it = properties.begin();
for(; it != properties.end(); ++it)
removeItem(*it);
}
PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap properties(origProps); // make a local copy that can be modified
// see comment in properties()
for(uint i = 0; i < keyConversionsSize; ++i)
if(properties.contains(keyConversions[i][0])) {
properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]);
properties.erase(keyConversions[i][0]);
}
// first check if tags need to be removed completely
StringList toRemove;
ItemListMap::ConstIterator remIt = itemListMap().begin();
for(; remIt != itemListMap().end(); ++remIt) {
String key = remIt->first.upper();
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
if(!key.isNull() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
toRemove.append(remIt->first);
}
for (StringList::Iterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
removeItem(*removeIt);
// now sync in the "forward direction"
PropertyMap::ConstIterator it = properties.begin();
PropertyMap invalid;
for(; it != properties.end(); ++it) {
const String &tagName = it->first;
if(!checkKey(tagName))
invalid.insert(it->first, it->second);
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
if(it->second.size() == 0)
removeItem(tagName);
else {
StringList::ConstIterator valueIt = it->second.begin();
addValue(tagName, *valueIt, true);
++valueIt;
for(; valueIt != it->second.end(); ++valueIt)
addValue(tagName, *valueIt, false);
}
}
}
return invalid;
}
bool APE::Tag::checkKey(const String &key)
{
if(key.size() < 2 || key.size() > 16)
return false;
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
// only allow printable ASCII including space (32..127)
if (*it < 32 || *it >= 128)
return false;
String upperKey = key.upper();
if (upperKey=="ID3" || upperKey=="TAG" || upperKey=="OGGS" || upperKey=="MP+")
return false;
return true;
}
APE::Footer *APE::Tag::footer() const APE::Footer *APE::Tag::footer() const
{ {
return &d->footer; return &d->footer;
@@ -195,16 +293,30 @@ void APE::Tag::addValue(const String &key, const String &value, bool replace)
{ {
if(replace) if(replace)
removeItem(key); removeItem(key);
if(!value.isEmpty()) { if(!key.isEmpty() && !value.isEmpty()) {
if(d->itemListMap.contains(key) || !replace) if(!replace && d->itemListMap.contains(key)) {
// Text items may contain more than one value
if(APE::Item::Text == d->itemListMap.begin()->second.type())
d->itemListMap[key.upper()].appendValue(value); d->itemListMap[key.upper()].appendValue(value);
// Binary or locator items may have only one value
else
setItem(key, Item(key, value));
}
else else
setItem(key, Item(key, value)); setItem(key, Item(key, value));
} }
} }
void APE::Tag::setData(const String &key, const ByteVector &value)
{
removeItem(key);
if(!key.isEmpty() && !value.isEmpty())
setItem(key, Item(key, value, true));
}
void APE::Tag::setItem(const String &key, const Item &item) void APE::Tag::setItem(const String &key, const Item &item)
{ {
if(!key.isEmpty())
d->itemListMap.insert(key.upper(), item); d->itemListMap.insert(key.upper(), item);
} }

View File

@@ -103,6 +103,37 @@ namespace TagLib {
virtual void setYear(uint i); virtual void setYear(uint i);
virtual void setTrack(uint i); virtual void setTrack(uint i);
/*!
* Implements the unified tag dictionary interface -- export function.
* APE tags are perfectly compatible with the dictionary interface because they
* support both arbitrary tag names and multiple values. Currently only
* APE items of type *Text* are handled by the dictionary interface; all *Binary*
* and *Locator* items will be put into the unsupportedData list and can be
* deleted on request using removeUnsupportedProperties(). The same happens
* to Text items if their key is invalid for PropertyMap (which should actually
* never happen).
*
* The only conversion done by this export function is to rename the APE tags
* TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively,
* in order to be compliant with the names used in other formats.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified tag dictionary interface -- import function. The same
* comments as for the export function apply; additionally note that the APE tag
* specification requires keys to have between 2 and 16 printable ASCII characters
* with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+".
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Check if the given String is a valid APE tag key.
*/
static bool checkKey(const String&);
/*! /*!
* Returns a pointer to the tag's footer. * Returns a pointer to the tag's footer.
*/ */
@@ -128,12 +159,19 @@ namespace TagLib {
void removeItem(const String &key); void removeItem(const String &key);
/*! /*!
* Adds to the item specified by \a key the data \a value. If \a replace * Adds to the text item specified by \a key the data \a value. If \a replace
* is true, then all of the other values on the same key will be removed * is true, then all of the other values on the same key will be removed
* first. * first. If a binary item exists for \a key it will be removed first.
*/ */
void addValue(const String &key, const String &value, bool replace = true); void addValue(const String &key, const String &value, bool replace = true);
/*!
* Set the binary data for the key specified by \a item to \a value
* This will convert the item to type \a Binary if it isn't already and
* all of the other values on the same key will be removed.
*/
void setData(const String &key, const ByteVector &value);
/*! /*!
* Sets the \a key item to the value of \a item. If an item with the \a key is already * Sets the \a key item to the value of \a item. If an item with the \a key is already
* present, it will be replaced. * present, it will be replaced.

View File

@@ -23,14 +23,9 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_ASF
#include <taglib.h> #include <taglib.h>
#include <tdebug.h> #include <tdebug.h>
#include "trefcounter.h"
#include "asfattribute.h" #include "asfattribute.h"
#include "asffile.h" #include "asffile.h"
@@ -353,4 +348,3 @@ void ASF::Attribute::setStream(int value)
d->stream = value; d->stream = value;
} }
#endif

View File

@@ -23,14 +23,9 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_ASF
#include <tdebug.h> #include <tdebug.h>
#include <tbytevectorlist.h> #include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tstring.h> #include <tstring.h>
#include "asffile.h" #include "asffile.h"
#include "asftag.h" #include "asftag.h"
@@ -69,6 +64,9 @@ static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x
static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16); static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16); static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16); static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
static ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
static ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
static ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
class ASF::File::BaseObject class ASF::File::BaseObject
{ {
@@ -185,7 +183,8 @@ ByteVector ASF::File::FilePropertiesObject::guid()
void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size) void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
{ {
BaseObject::parse(file, size); BaseObject::parse(file, size);
file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L)); file->d->properties->setLength(
(int)(data.toLongLong(40, false) / 10000000L - data.toLongLong(56, false) / 1000L));
} }
ByteVector ASF::File::StreamPropertiesObject::guid() ByteVector ASF::File::StreamPropertiesObject::guid()
@@ -196,9 +195,9 @@ ByteVector ASF::File::StreamPropertiesObject::guid()
void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size) void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
{ {
BaseObject::parse(file, size); BaseObject::parse(file, size);
file->d->properties->setChannels(data.mid(56, 2).toShort(false)); file->d->properties->setChannels(data.toShort(56, false));
file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false)); file->d->properties->setSampleRate(data.toUInt(58, false));
file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000); file->d->properties->setBitrate(data.toUInt(62, false) * 8 / 1000);
} }
ByteVector ASF::File::ContentDescriptionObject::guid() ByteVector ASF::File::ContentDescriptionObject::guid()
@@ -347,7 +346,7 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
else { else {
obj = new UnknownObject(guid); obj = new UnknownObject(guid);
} }
obj->parse(file, size); obj->parse(file, (unsigned int)size);
objects.append(obj); objects.append(obj);
dataPos += size; dataPos += size;
} }
@@ -371,6 +370,15 @@ ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle proper
: TagLib::File(file) : TagLib::File(file)
{ {
d = new FilePrivate; d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
: TagLib::File(stream)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
@@ -393,6 +401,21 @@ ASF::Tag *ASF::File::tag() const
return d->tag; return d->tag;
} }
PropertyMap ASF::File::properties() const
{
return d->tag->properties();
}
void ASF::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag->removeUnsupportedProperties(properties);
}
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
ASF::Properties *ASF::File::audioProperties() const ASF::Properties *ASF::File::audioProperties() const
{ {
return d->properties; return d->properties;
@@ -454,6 +477,11 @@ void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*properties
obj = new HeaderExtensionObject(); obj = new HeaderExtensionObject();
} }
else { else {
if(guid == contentEncryptionGuid ||
guid == extendedContentEncryptionGuid ||
guid == advancedContentEncryptionGuid) {
d->properties->setEncrypted(true);
}
obj = new UnknownObject(guid); obj = new UnknownObject(guid);
} }
obj->parse(this, size); obj->parse(this, size);
@@ -522,7 +550,7 @@ bool ASF::File::save()
data.append(d->objects[i]->render(this)); data.append(d->objects[i]->render(this));
} }
data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data; data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
insert(data, 0, d->size); insert(data, 0, (TagLib::ulong)d->size);
return true; return true;
} }
@@ -600,4 +628,3 @@ ByteVector ASF::File::renderString(const String &str, bool includeLength)
return data; return data;
} }
#endif

View File

@@ -48,14 +48,27 @@ namespace TagLib {
public: public:
/*! /*!
* Contructs an ASF file from \a file. If \a readProperties is true the * Constructs an ASF file from \a file.
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* *
* \note In the current implementation, both \a readProperties and * \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. * \a propertiesStyle are ignored. The audio properties are always
* read.
*/ */
File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an ASF file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
@@ -74,6 +87,22 @@ namespace TagLib {
*/ */
virtual Tag *tag() const; virtual Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the ASF audio properties for this file. * Returns the ASF audio properties for this file.
*/ */
@@ -87,7 +116,6 @@ namespace TagLib {
virtual bool save(); virtual bool save();
private: private:
int readBYTE(bool *ok = 0); int readBYTE(bool *ok = 0);
int readWORD(bool *ok = 0); int readWORD(bool *ok = 0);
unsigned int readDWORD(bool *ok = 0); unsigned int readDWORD(bool *ok = 0);

View File

@@ -23,21 +23,16 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_ASF
#include <taglib.h> #include <taglib.h>
#include <tdebug.h> #include <tdebug.h>
#include "trefcounter.h"
#include "asfattribute.h" #include "asfattribute.h"
#include "asffile.h" #include "asffile.h"
#include "asfpicture.h" #include "asfpicture.h"
using namespace TagLib; using namespace TagLib;
class ASF::Picture::PicturePriavte : public RefCounter class ASF::Picture::PicturePrivate : public RefCounter
{ {
public: public:
bool valid; bool valid;
@@ -53,7 +48,7 @@ public:
ASF::Picture::Picture() ASF::Picture::Picture()
{ {
d = new PicturePriavte(); d = new PicturePrivate();
d->valid = true; d->valid = true;
} }
@@ -151,7 +146,7 @@ void ASF::Picture::parse(const ByteVector& bytes)
return; return;
int pos = 0; int pos = 0;
d->type = (Type)bytes[0]; ++pos; d->type = (Type)bytes[0]; ++pos;
uint dataLen = bytes.mid(pos, 4).toUInt(false); pos+=4; const uint dataLen = bytes.toUInt(pos, false); pos+=4;
const ByteVector nullStringTerminator(2, 0); const ByteVector nullStringTerminator(2, 0);
@@ -182,4 +177,3 @@ ASF::Picture ASF::Picture::fromInvalid()
return ret; return ret;
} }
#endif

View File

@@ -208,8 +208,8 @@ namespace TagLib
friend class Attribute; friend class Attribute;
#endif #endif
private: private:
struct PicturePriavte; class PicturePrivate;
PicturePriavte *d; PicturePrivate *d;
}; };
} }
} }

View File

@@ -23,12 +23,6 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_ASF
#include <tdebug.h> #include <tdebug.h>
#include <tstring.h> #include <tstring.h>
#include "asfproperties.h" #include "asfproperties.h"
@@ -38,11 +32,12 @@ using namespace TagLib;
class ASF::Properties::PropertiesPrivate class ASF::Properties::PropertiesPrivate
{ {
public: public:
PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0) {} PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0), encrypted(false) {}
int length; int length;
int bitrate; int bitrate;
int sampleRate; int sampleRate;
int channels; int channels;
bool encrypted;
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -80,6 +75,11 @@ int ASF::Properties::channels() const
return d->channels; return d->channels;
} }
bool ASF::Properties::isEncrypted() const
{
return d->encrypted;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -104,4 +104,8 @@ void ASF::Properties::setChannels(int length)
d->channels = length; d->channels = length;
} }
#endif void ASF::Properties::setEncrypted(bool encrypted)
{
d->encrypted = encrypted;
}

View File

@@ -54,12 +54,14 @@ namespace TagLib {
virtual int bitrate() const; virtual int bitrate() const;
virtual int sampleRate() const; virtual int sampleRate() const;
virtual int channels() const; virtual int channels() const;
bool isEncrypted() const;
#ifndef DO_NOT_DOCUMENT #ifndef DO_NOT_DOCUMENT
void setLength(int value); void setLength(int value);
void setBitrate(int value); void setBitrate(int value);
void setSampleRate(int value); void setSampleRate(int value);
void setChannels(int value); void setChannels(int value);
void setEncrypted(bool value);
#endif #endif
private: private:

View File

@@ -23,12 +23,7 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H #include <tpropertymap.h>
#include <config.h>
#endif
#ifdef WITH_ASF
#include "asftag.h" #include "asftag.h"
using namespace TagLib; using namespace TagLib;
@@ -198,4 +193,162 @@ bool ASF::Tag::isEmpty() const
d->attributeListMap.isEmpty(); d->attributeListMap.isEmpty();
} }
#endif static const char *keyTranslation[][2] = {
{ "WM/AlbumTitle", "ALBUM" },
{ "WM/Composer", "COMPOSER" },
{ "WM/Writer", "WRITER" },
{ "WM/Conductor", "CONDUCTOR" },
{ "WM/ModifiedBy", "REMIXER" },
{ "WM/Year", "DATE" },
{ "WM/OriginalReleaseYear", "ORIGINALDATE" },
{ "WM/Producer", "PRODUCER" },
{ "WM/ContentGroupDescription", "GROUPING" },
{ "WM/SubTitle", "SUBTITLE" },
{ "WM/SetSubTitle", "DISCSUBTITLE" },
{ "WM/TrackNumber", "TRACKNUMBER" },
{ "WM/PartOfSet", "DISCNUMBER" },
{ "WM/Genre", "GENRE" },
{ "WM/BeatsPerMinute", "BPM" },
{ "WM/Mood", "MOOD" },
{ "WM/ISRC", "ISRC" },
{ "WM/Lyrics", "LYRICS" },
{ "WM/Media", "MEDIA" },
{ "WM/Publisher", "LABEL" },
{ "WM/CatalogNo", "CATALOGNUMBER" },
{ "WM/Barcode", "BARCODE" },
{ "WM/EncodedBy", "ENCODEDBY" },
{ "WM/AlbumSortOrder", "ALBUMSORT" },
{ "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" },
{ "WM/ArtistSortOrder", "ARTISTSORT" },
{ "WM/TitleSortOrder", "TITLESORT" },
{ "WM/Script", "SCRIPT" },
{ "WM/Language", "LANGUAGE" },
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
{ "MusicIP/PUID", "MUSICIP_PUID" },
{ "Acoustid/Id", "ACOUSTID_ID" },
{ "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" },
};
PropertyMap ASF::Tag::properties() const
{
static Map<String, String> keyMap;
if(keyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
}
}
PropertyMap props;
if(!d->title.isEmpty()) {
props["TITLE"] = d->title;
}
if(!d->artist.isEmpty()) {
props["ARTIST"] = d->artist;
}
if(!d->copyright.isEmpty()) {
props["COPYRIGHT"] = d->copyright;
}
if(!d->comment.isEmpty()) {
props["COMMENT"] = d->comment;
}
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
for(; it != d->attributeListMap.end(); ++it) {
if(keyMap.contains(it->first)) {
String key = keyMap[it->first];
AttributeList::ConstIterator it2 = it->second.begin();
for(; it2 != it->second.end(); ++it2) {
if(key == "TRACKNUMBER") {
if(it2->type() == ASF::Attribute::DWordType)
props.insert(key, String::number(it2->toUInt()));
else
props.insert(key, it2->toString());
}
else {
props.insert(key, it2->toString());
}
}
}
else {
props.unsupportedData().append(it->first);
}
}
return props;
}
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
{
StringList::ConstIterator it = props.begin();
for(; it != props.end(); ++it)
d->attributeListMap.erase(*it);
}
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
{
static Map<String, String> reverseKeyMap;
if(reverseKeyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
}
}
PropertyMap origProps = properties();
PropertyMap::ConstIterator it = origProps.begin();
for(; it != origProps.end(); ++it) {
if(!props.contains(it->first) || props[it->first].isEmpty()) {
if(it->first == "TITLE") {
d->title = String::null;
}
else if(it->first == "ARTIST") {
d->artist = String::null;
}
else if(it->first == "COMMENT") {
d->comment = String::null;
}
else if(it->first == "COPYRIGHT") {
d->copyright = String::null;
}
else {
d->attributeListMap.erase(reverseKeyMap[it->first]);
}
}
}
PropertyMap ignoredProps;
it = props.begin();
for(; it != props.end(); ++it) {
if(reverseKeyMap.contains(it->first)) {
String name = reverseKeyMap[it->first];
removeItem(name);
StringList::ConstIterator it2 = it->second.begin();
for(; it2 != it->second.end(); ++it2) {
addAttribute(name, *it2);
}
}
else if(it->first == "TITLE") {
d->title = it->second.toString();
}
else if(it->first == "ARTIST") {
d->artist = it->second.toString();
}
else if(it->first == "COMMENT") {
d->comment = it->second.toString();
}
else if(it->first == "COPYRIGHT") {
d->copyright = it->second.toString();
}
else {
ignoredProps.insert(it->first, it->second);
}
}
return ignoredProps;
}

View File

@@ -176,6 +176,10 @@ namespace TagLib {
*/ */
void addAttribute(const String &name, const Attribute &attribute); void addAttribute(const String &name, const Attribute &attribute);
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList& properties);
PropertyMap setProperties(const PropertyMap &properties);
private: private:
class TagPrivate; class TagPrivate;

View File

@@ -1,2 +0,0 @@
#define WITH_ASF 1
#define WITH_MP4 1

View File

@@ -27,13 +27,10 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tfile.h> #include <tfile.h>
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include "trefcounter.h"
#include "fileref.h" #include "fileref.h"
#include "asffile.h" #include "asffile.h"
@@ -42,13 +39,17 @@
#include "flacfile.h" #include "flacfile.h"
#include "oggflacfile.h" #include "oggflacfile.h"
#include "mpcfile.h" #include "mpcfile.h"
#include "mp4file.h" //#include "mp4file.h"
#include "wavpackfile.h" //#include "wavpackfile.h"
//#include "speexfile.h" //#include "speexfile.h"
//#include "trueaudiofile.h" //#include "trueaudiofile.h"
//#include "aifffile.h" //#include "aifffile.h"
//#include "wavfile.h" //#include "wavfile.h"
#include "apefile.h" #include "apefile.h"
//#include "modfile.h"
//#include "s3mfile.h"
//#include "itfile.h"
//#include "xmfile.h"
using namespace TagLib; using namespace TagLib;
@@ -144,24 +145,28 @@ StringList FileRef::defaultFileExtensions()
l.append("oga"); l.append("oga");
l.append("mp3"); l.append("mp3");
l.append("mpc"); l.append("mpc");
l.append("wv"); // l.append("wv");
//l.append("spx"); // l.append("spx");
//l.append("tta"); // l.append("tta");
#ifdef TAGLIB_WITH_MP4 // l.append("m4a");
l.append("m4a"); // l.append("m4r");
l.append("m4b"); // l.append("m4b");
l.append("m4p"); // l.append("m4p");
l.append("3g2"); // l.append("3g2");
l.append("mp4"); // l.append("mp4");
#endif
#ifdef TAGLIB_WITH_ASF
l.append("wma"); l.append("wma");
l.append("asf"); l.append("asf");
#endif // l.append("aif");
//l.append("aif"); // l.append("aiff");
//l.append("aiff"); // l.append("wav");
//l.append("wav");
l.append("ape"); l.append("ape");
// l.append("mod");
// l.append("module"); // alias for "mod"
// l.append("nst"); // alias for "mod"
// l.append("wow"); // alias for "mod"
// l.append("s3m");
// l.append("it");
// l.append("xm");
return l; return l;
} }
@@ -209,21 +214,28 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
// Ok, this is really dumb for now, but it works for testing. // Ok, this is really dumb for now, but it works for testing.
String s; String ext;
{
#ifdef _WIN32 #ifdef _WIN32
s = (wcslen((const wchar_t *) fileName) > 0) ? String((const wchar_t *) fileName) : String((const char *) fileName);
String s = fileName.toString();
#else #else
s = fileName;
#endif String s = fileName;
#endif
const int pos = s.rfind(".");
if(pos != -1)
ext = s.substr(pos + 1).upper();
}
// If this list is updated, the method defaultFileExtensions() should also be // If this list is updated, the method defaultFileExtensions() should also be
// updated. However at some point that list should be created at the same time // updated. However at some point that list should be created at the same time
// that a default file type resolver is created. // that a default file type resolver is created.
int pos = s.rfind("."); if(!ext.isEmpty()) {
if(pos != -1) {
String ext = s.substr(pos + 1).upper();
if(ext == "MP3") if(ext == "MP3")
return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle); return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OGG") if(ext == "OGG")
@@ -238,28 +250,35 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
} }
if(ext == "FLAC") if(ext == "FLAC")
return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle); return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "MPC") // if(ext == "MPC")
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle); // return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WV") // if(ext == "WV")
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle); // return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
// if(ext == "SPX") // if(ext == "SPX")
// return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle); // return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
// if(ext == "OPUS")
// return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
// if(ext == "TTA") // if(ext == "TTA")
// return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle); // return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
#ifdef TAGLIB_WITH_MP4 // if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
if(ext == "M4A" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2") // return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_ASF
if(ext == "WMA" || ext == "ASF") if(ext == "WMA" || ext == "ASF")
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle); return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
#endif
// if(ext == "AIF" || ext == "AIFF") // if(ext == "AIF" || ext == "AIFF")
// return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle); // return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
// if(ext == "WAV") // if(ext == "WAV")
// return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle); // return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "APE") if(ext == "APE")
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle); 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")
// return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
// if(ext == "XM")
// return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
} }
return 0; return 0;

View File

@@ -28,6 +28,7 @@
#include <tlist.h> #include <tlist.h>
#include <tdebug.h> #include <tdebug.h>
#include <tagunion.h> #include <tagunion.h>
#include <tpropertymap.h>
#include <id3v2header.h> #include <id3v2header.h>
#include <id3v2tag.h> #include <id3v2tag.h>
@@ -43,7 +44,7 @@ using namespace TagLib;
namespace namespace
{ {
enum { XiphIndex = 0, ID3v2Index = 1, ID3v1Index = 2 }; enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
enum { MinPaddingLength = 4096 }; enum { MinPaddingLength = 4096 };
enum { LastBlockFlag = 0x80 }; enum { LastBlockFlag = 0x80 };
} }
@@ -69,7 +70,8 @@ public:
~FilePrivate() ~FilePrivate()
{ {
for(uint i = 0; i < blocks.size(); i++) { uint size = blocks.size();
for(uint i = 0; i < size; i++) {
delete blocks[i]; delete blocks[i];
} }
delete properties; delete properties;
@@ -107,6 +109,7 @@ FLAC::File::File(FileName file, bool readProperties,
TagLib::File(file) TagLib::File(file)
{ {
d = new FilePrivate; d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
@@ -116,6 +119,17 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
{ {
d = new FilePrivate; d = new FilePrivate;
d->ID3v2FrameFactory = frameFactory; d->ID3v2FrameFactory = frameFactory;
if(isOpen())
read(readProperties, propertiesStyle);
}
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle propertiesStyle) :
TagLib::File(stream)
{
d = new FilePrivate;
d->ID3v2FrameFactory = frameFactory;
if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
@@ -129,6 +143,34 @@ TagLib::Tag *FLAC::File::tag() const
return &d->tag; return &d->tag;
} }
PropertyMap FLAC::File::properties() const
{
// once Tag::properties() is virtual, this case distinction could actually be done
// within TagUnion.
if(d->hasXiphComment)
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->properties();
if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->properties();
if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->properties();
return PropertyMap();
}
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
{
if(d->hasXiphComment)
d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->removeUnsupportedProperties(unsupported);
if(d->hasID3v2)
d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->removeUnsupportedProperties(unsupported);
if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->removeUnsupportedProperties(unsupported);
}
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
{
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
}
FLAC::Properties *FLAC::File::audioProperties() const FLAC::Properties *FLAC::File::audioProperties() const
{ {
return d->properties; return d->properties;
@@ -198,7 +240,7 @@ bool FLAC::File::save()
} }
ByteVector padding = ByteVector::fromUInt(paddingLength); ByteVector padding = ByteVector::fromUInt(paddingLength);
padding.resize(paddingLength + 4); padding.resize(paddingLength + 4);
padding[0] = FLAC::MetadataBlock::Padding | LastBlockFlag; padding[0] = (char)(FLAC::MetadataBlock::Padding | LastBlockFlag);
data.append(padding); data.append(padding);
// Write the data to the file // Write the data to the file
@@ -230,21 +272,21 @@ bool FLAC::File::save()
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create) ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{ {
if(!create || d->tag[ID3v2Index]) if(!create || d->tag[FlacID3v2Index])
return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]); return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
d->tag.set(ID3v2Index, new ID3v2::Tag); d->tag.set(FlacID3v2Index, new ID3v2::Tag);
return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]); return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
} }
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create) ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
{ {
return d->tag.access<ID3v1::Tag>(ID3v1Index, create); return d->tag.access<ID3v1::Tag>(FlacID3v1Index, create);
} }
Ogg::XiphComment *FLAC::File::xiphComment(bool create) Ogg::XiphComment *FLAC::File::xiphComment(bool create)
{ {
return d->tag.access<Ogg::XiphComment>(XiphIndex, create); return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create);
} }
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
@@ -265,12 +307,12 @@ void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
if(d->ID3v2Location >= 0) { if(d->ID3v2Location >= 0) {
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
if(ID3v2Tag()->header()->tagSize() <= 0) if(ID3v2Tag()->header()->tagSize() <= 0)
d->tag.set(ID3v2Index, 0); d->tag.set(FlacID3v2Index, 0);
else else
d->hasID3v2 = true; d->hasID3v2 = true;
} }
@@ -280,7 +322,7 @@ void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
d->ID3v1Location = findID3v1(); d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) { if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true; d->hasID3v1 = true;
} }
@@ -292,9 +334,9 @@ void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
return; return;
if(d->hasXiphComment) if(d->hasXiphComment)
d->tag.set(XiphIndex, new Ogg::XiphComment(xiphCommentData())); d->tag.set(FlacXiphIndex, new Ogg::XiphComment(xiphCommentData()));
else else
d->tag.set(XiphIndex, new Ogg::XiphComment); d->tag.set(FlacXiphIndex, new Ogg::XiphComment);
if(readProperties) if(readProperties)
d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle); d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
@@ -357,7 +399,7 @@ void FLAC::File::scan()
char blockType = header[0] & 0x7f; char blockType = header[0] & 0x7f;
bool isLastBlock = (header[0] & 0x80) != 0; bool isLastBlock = (header[0] & 0x80) != 0;
uint length = header.mid(1, 3).toUInt(); uint length = header.toUInt(1U, 3U);
// First block should be the stream_info metadata // First block should be the stream_info metadata
@@ -377,10 +419,10 @@ void FLAC::File::scan()
header = readBlock(4); header = readBlock(4);
blockType = header[0] & 0x7f; blockType = header[0] & 0x7f;
isLastBlock = (header[0] & 0x80) != 0; isLastBlock = (header[0] & 0x80) != 0;
length = header.mid(1, 3).toUInt(); length = header.toUInt(1U, 3U);
ByteVector data = readBlock(length); ByteVector data = readBlock(length);
if(data.size() != length) { if(data.size() != length || length == 0) {
debug("FLAC::File::scan() -- FLAC stream corrupted"); debug("FLAC::File::scan() -- FLAC stream corrupted");
setValid(false); setValid(false);
return; return;
@@ -484,6 +526,17 @@ void FLAC::File::addPicture(Picture *picture)
d->blocks.append(picture); d->blocks.append(picture);
} }
void FLAC::File::removePicture(Picture *picture, bool del)
{
MetadataBlock *block = picture;
List<MetadataBlock *>::Iterator it = d->blocks.find(block);
if(it != d->blocks.end())
d->blocks.erase(it);
if(del)
delete picture;
}
void FLAC::File::removePictures() void FLAC::File::removePictures()
{ {
List<MetadataBlock *> newBlocks; List<MetadataBlock *> newBlocks;
@@ -499,3 +552,17 @@ void FLAC::File::removePictures()
d->blocks = newBlocks; d->blocks = newBlocks;
} }
bool FLAC::File::hasXiphComment() const
{
return d->hasXiphComment;
}
bool FLAC::File::hasID3v1Tag() const
{
return d->hasID3v1;
}
bool FLAC::File::hasID3v2Tag() const
{
return d->hasID3v2;
}

View File

@@ -29,6 +29,7 @@
#include "taglib_export.h" #include "taglib_export.h"
#include "tfile.h" #include "tfile.h"
#include "tlist.h" #include "tlist.h"
#include "tag.h"
#include "flacpicture.h" #include "flacpicture.h"
#include "flacproperties.h" #include "flacproperties.h"
@@ -36,7 +37,6 @@
namespace TagLib { namespace TagLib {
class Tag; class Tag;
namespace ID3v2 { class FrameFactory; class Tag; } namespace ID3v2 { class FrameFactory; class Tag; }
namespace ID3v1 { class Tag; } namespace ID3v1 { class Tag; }
namespace Ogg { class XiphComment; } namespace Ogg { class XiphComment; }
@@ -67,9 +67,10 @@ namespace TagLib {
{ {
public: public:
/*! /*!
* Contructs a FLAC file from \a file. If \a readProperties is true the * Constructs a FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If * file's audio properties will also be read.
* false, \a propertiesStyle is ignored. *
* \note In the current implementation, \a propertiesStyle is ignored.
* *
* \deprecated This constructor will be dropped in favor of the one below * \deprecated This constructor will be dropped in favor of the one below
* in a future version. * in a future version.
@@ -78,18 +79,36 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Contructs a FLAC file from \a file. If \a readProperties is true the * Constructs an APE file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If * file's audio properties will also be read.
* false, \a propertiesStyle is ignored.
* *
* If this file contains and ID3v2 tag the frames will be created using * If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory. * \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/ */
// BIC: merge with the above constructor // BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory, File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs a FLAC file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
// BIC: merge with the above constructor
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
*/ */
@@ -105,6 +124,25 @@ namespace TagLib {
*/ */
virtual TagLib::Tag *tag() const; virtual TagLib::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains more than one tag (e.g. XiphComment and ID3v1),
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
* converted to the PropertyMap.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &);
/*!
* Implements the unified property interface -- import function.
* This always creates a Xiph comment, if none exists. The return value
* relates to the Xiph comment only.
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
* in the FLAC specification.
*/
PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the FLAC::Properties for this file. If no audio properties * Returns the FLAC::Properties for this file. If no audio properties
* were read then this will return a null pointer. * were read then this will return a null pointer.
@@ -123,39 +161,57 @@ namespace TagLib {
/*! /*!
* Returns a pointer to the ID3v2 tag of the file. * Returns a pointer to the ID3v2 tag of the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this returns a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create * if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist. * an ID3v2 tag if one does not exist and returns a valid pointer.
* *
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be * \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* on disk actually has an ID3v2 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasID3v2Tag()
*/ */
ID3v2::Tag *ID3v2Tag(bool create = false); ID3v2::Tag *ID3v2Tag(bool create = false);
/*! /*!
* Returns a pointer to the ID3v1 tag of the file. * Returns a pointer to the ID3v1 tag of the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this returns a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create * if there is no valid APE tag. If \a create is true it will create
* an ID3v1 tag if one does not exist. * an APE tag if one does not exist and returns a valid pointer.
* *
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be * \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasID3v1Tag()
*/ */
ID3v1::Tag *ID3v1Tag(bool create = false); ID3v1::Tag *ID3v1Tag(bool create = false);
/*! /*!
* Returns a pointer to the XiphComment for the file. * Returns a pointer to the XiphComment for the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this returns a null pointer
* if there is no valid XiphComment. If \a create is true it will create * if there is no valid XiphComment. If \a create is true it will create
* a XiphComment if one does not exist. * a XiphComment if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has a XiphComment. Use hasXiphComment() to check if the
* file on disk actually has a XiphComment.
* *
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be * \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasXiphComment()
*/ */
Ogg::XiphComment *xiphComment(bool create = false); Ogg::XiphComment *xiphComment(bool create = false);
@@ -189,6 +245,12 @@ namespace TagLib {
*/ */
List<Picture *> pictureList(); List<Picture *> pictureList();
/*!
* Removes an attached picture. If \a del is true the picture's memory
* will be freed; if it is false, it must be deleted by the user.
*/
void removePicture(Picture *picture, bool del = true);
/*! /*!
* Remove all attached images. * Remove all attached images.
*/ */
@@ -202,6 +264,27 @@ namespace TagLib {
*/ */
void addPicture(Picture *picture); void addPicture(Picture *picture);
/*!
* Returns whether or not the file on disk actually has a XiphComment.
*
* \see xiphComment()
*/
bool hasXiphComment() const;
/*!
* Returns whether or not the file on disk actually has an ID3v1 tag.
*
* \see ID3v1Tag()
*/
bool hasID3v1Tag() const;
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
*
* \see ID3v2Tag()
*/
bool hasID3v2Tag() const;
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@@ -23,10 +23,6 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h> #include <taglib.h>
#include <tdebug.h> #include <tdebug.h>
#include "flacmetadatablock.h" #include "flacmetadatablock.h"

View File

@@ -23,10 +23,6 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h> #include <taglib.h>
#include <tdebug.h> #include <tdebug.h>
#include "flacpicture.h" #include "flacpicture.h"
@@ -82,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
return false; return false;
} }
int pos = 0; uint pos = 0;
d->type = FLAC::Picture::Type(data.mid(pos, 4).toUInt()); d->type = FLAC::Picture::Type(data.toUInt(pos));
pos += 4; pos += 4;
uint mimeTypeLength = data.mid(pos, 4).toUInt(); uint mimeTypeLength = data.toUInt(pos);
pos += 4; pos += 4;
if(pos + mimeTypeLength + 24 > data.size()) { if(pos + mimeTypeLength + 24 > data.size()) {
debug("Invalid picture block."); debug("Invalid picture block.");
@@ -93,7 +89,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
} }
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8); d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
pos += mimeTypeLength; pos += mimeTypeLength;
uint descriptionLength = data.mid(pos, 4).toUInt(); uint descriptionLength = data.toUInt(pos);
pos += 4; pos += 4;
if(pos + descriptionLength + 20 > data.size()) { if(pos + descriptionLength + 20 > data.size()) {
debug("Invalid picture block."); debug("Invalid picture block.");
@@ -101,15 +97,15 @@ bool FLAC::Picture::parse(const ByteVector &data)
} }
d->description = String(data.mid(pos, descriptionLength), String::UTF8); d->description = String(data.mid(pos, descriptionLength), String::UTF8);
pos += descriptionLength; pos += descriptionLength;
d->width = data.mid(pos, 4).toUInt(); d->width = data.toUInt(pos);
pos += 4; pos += 4;
d->height = data.mid(pos, 4).toUInt(); d->height = data.toUInt(pos);
pos += 4; pos += 4;
d->colorDepth = data.mid(pos, 4).toUInt(); d->colorDepth = data.toUInt(pos);
pos += 4; pos += 4;
d->numColors = data.mid(pos, 4).toUInt(); d->numColors = data.toUInt(pos);
pos += 4; pos += 4;
uint dataLength = data.mid(pos, 4).toUInt(); uint dataLength = data.toUInt(pos);
pos += 4; pos += 4;
if(pos + dataLength > data.size()) { if(pos + dataLength > data.size()) {
debug("Invalid picture block."); debug("Invalid picture block.");

View File

@@ -42,7 +42,8 @@ public:
bitrate(0), bitrate(0),
sampleRate(0), sampleRate(0),
sampleWidth(0), sampleWidth(0),
channels(0) {} channels(0),
sampleFrames(0) {}
ByteVector data; ByteVector data;
long streamLength; long streamLength;
@@ -52,6 +53,7 @@ public:
int sampleRate; int sampleRate;
int sampleWidth; int sampleWidth;
int channels; int channels;
unsigned long long sampleFrames;
ByteVector signature; ByteVector signature;
}; };
@@ -101,6 +103,11 @@ int FLAC::Properties::channels() const
return d->channels; return d->channels;
} }
unsigned long long FLAC::Properties::sampleFrames() const
{
return d->sampleFrames;
}
ByteVector FLAC::Properties::signature() const ByteVector FLAC::Properties::signature() const
{ {
return d->signature; return d->signature;
@@ -117,7 +124,7 @@ void FLAC::Properties::read()
return; return;
} }
int pos = 0; uint pos = 0;
// Minimum block size (in samples) // Minimum block size (in samples)
pos += 2; pos += 2;
@@ -131,7 +138,9 @@ void FLAC::Properties::read()
// Maximum frame size (in bytes) // Maximum frame size (in bytes)
pos += 3; pos += 3;
uint flags = d->data.mid(pos, 4).toUInt(true); uint flags = d->data.toUInt(pos, true);
pos += 4;
d->sampleRate = flags >> 12; d->sampleRate = flags >> 12;
d->channels = ((flags >> 9) & 7) + 1; d->channels = ((flags >> 9) & 7) + 1;
d->sampleWidth = ((flags >> 4) & 31) + 1; d->sampleWidth = ((flags >> 4) & 31) + 1;
@@ -139,12 +148,14 @@ void FLAC::Properties::read()
// The last 4 bits are the most significant 4 bits for the 36 bit // The last 4 bits are the most significant 4 bits for the 36 bit
// stream length in samples. (Audio files measured in days) // stream length in samples. (Audio files measured in days)
uint highLength =d->sampleRate > 0 ? (((flags & 0xf) << 28) / d->sampleRate) << 4 : 0; unsigned long long hi = flags & 0xf;
unsigned long long lo = d->data.toUInt(pos, true);
pos += 4; pos += 4;
d->length = d->sampleRate > 0 ? d->sampleFrames = (hi << 32) | lo;
(d->data.mid(pos, 4).toUInt(true)) / d->sampleRate + highLength : 0;
pos += 4; if(d->sampleRate > 0)
d->length = int(d->sampleFrames / d->sampleRate);
// Uncompressed bitrate: // Uncompressed bitrate:

View File

@@ -77,6 +77,11 @@ namespace TagLib {
*/ */
int sampleWidth() const; int sampleWidth() const;
/*!
* Return the number of sample frames
*/
unsigned long long sampleFrames() const;
/*! /*!
* Returns the MD5 signature of the uncompressed audio stream as read * Returns the MD5 signature of the uncompressed audio stream as read
* from the stream info header header. * from the stream info header header.

View File

@@ -23,10 +23,6 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h> #include <taglib.h>
#include <tdebug.h> #include <tdebug.h>
#include <tstring.h> #include <tstring.h>

View File

@@ -1,197 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_MP4
#include <tdebug.h>
#include <tstring.h>
#include "mp4atom.h"
using namespace TagLib;
const char *MP4::Atom::containers[10] = {
"moov", "udta", "mdia", "meta", "ilst",
"stbl", "minf", "moof", "traf", "trak",
};
MP4::Atom::Atom(File *file)
{
offset = file->tell();
ByteVector header = file->readBlock(8);
if (header.size() != 8) {
// The atom header must be 8 bytes long, otherwise there is either
// trailing garbage or the file is truncated
debug("MP4: Couldn't read 8 bytes of data for atom header");
length = 0;
file->seek(0, File::End);
return;
}
length = header.mid(0, 4).toUInt();
if (length == 1) {
long long longLength = file->readBlock(8).toLongLong();
if (longLength >= 8 && longLength <= 0xFFFFFFFF) {
// The atom has a 64-bit length, but it's actually a 32-bit value
length = (long)longLength;
}
else {
debug("MP4: 64-bit atoms are not supported");
length = 0;
file->seek(0, File::End);
return;
}
}
if (length < 8) {
debug("MP4: Invalid atom size");
length = 0;
file->seek(0, File::End);
return;
}
name = header.mid(4, 4);
for(int i = 0; i < numContainers; i++) {
if(name == containers[i]) {
if(name == "meta") {
file->seek(4, File::Current);
}
while(file->tell() < offset + length) {
MP4::Atom *child = new MP4::Atom(file);
children.append(child);
if (child->length == 0)
return;
}
return;
}
}
file->seek(offset + length);
}
MP4::Atom::~Atom()
{
for(unsigned int i = 0; i < children.size(); i++) {
delete children[i];
}
children.clear();
}
MP4::Atom *
MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4)
{
if(name1 == 0) {
return this;
}
for(unsigned int i = 0; i < children.size(); i++) {
if(children[i]->name == name1) {
return children[i]->find(name2, name3, name4);
}
}
return 0;
}
MP4::AtomList
MP4::Atom::findall(const char *name, bool recursive)
{
MP4::AtomList result;
for(unsigned int i = 0; i < children.size(); i++) {
if(children[i]->name == name) {
result.append(children[i]);
}
if(recursive) {
result.append(children[i]->findall(name, recursive));
}
}
return result;
}
bool
MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3)
{
path.append(this);
if(name1 == 0) {
return true;
}
for(unsigned int i = 0; i < children.size(); i++) {
if(children[i]->name == name1) {
return children[i]->path(path, name2, name3);
}
}
return false;
}
MP4::Atoms::Atoms(File *file)
{
file->seek(0, File::End);
long end = file->tell();
file->seek(0);
while(file->tell() + 8 <= end) {
MP4::Atom *atom = new MP4::Atom(file);
atoms.append(atom);
if (atom->length == 0)
break;
}
}
MP4::Atoms::~Atoms()
{
for(unsigned int i = 0; i < atoms.size(); i++) {
delete atoms[i];
}
atoms.clear();
}
MP4::Atom *
MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4)
{
for(unsigned int i = 0; i < atoms.size(); i++) {
if(atoms[i]->name == name1) {
return atoms[i]->find(name2, name3, name4);
}
}
return 0;
}
MP4::AtomList
MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4)
{
MP4::AtomList path;
for(unsigned int i = 0; i < atoms.size(); i++) {
if(atoms[i]->name == name1) {
if(!atoms[i]->path(path, name2, name3, name4)) {
path.clear();
}
return path;
}
}
return path;
}
#endif

View File

@@ -1,77 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
// This file is not part of the public API!
#ifndef DO_NOT_DOCUMENT
#ifndef TAGLIB_MP4ATOM_H
#define TAGLIB_MP4ATOM_H
#include "tfile.h"
#include "tlist.h"
namespace TagLib {
namespace MP4 {
class Atom;
typedef TagLib::List<Atom *> AtomList;
class Atom
{
public:
Atom(File *file);
~Atom();
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
AtomList findall(const char *name, bool recursive = false);
long offset;
long length;
TagLib::ByteVector name;
AtomList children;
private:
static const int numContainers = 10;
static const char *containers[10];
};
//! Root-level atoms
class Atoms
{
public:
Atoms(File *file);
~Atoms();
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
AtomList atoms;
};
}
}
#endif
#endif

View File

@@ -1,145 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_MP4
#include <tdebug.h>
#include <tstring.h>
#include "mp4atom.h"
#include "mp4tag.h"
#include "mp4file.h"
using namespace TagLib;
class MP4::File::FilePrivate
{
public:
FilePrivate() : tag(0), atoms(0), properties(0)
{
}
~FilePrivate()
{
if(atoms) {
delete atoms;
atoms = 0;
}
if(tag) {
delete tag;
tag = 0;
}
if(properties) {
delete properties;
properties = 0;
}
}
MP4::Tag *tag;
MP4::Atoms *atoms;
MP4::Properties *properties;
};
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle)
: TagLib::File(file)
{
d = new FilePrivate;
read(readProperties, audioPropertiesStyle);
}
MP4::File::~File()
{
delete d;
}
MP4::Tag *
MP4::File::tag() const
{
return d->tag;
}
MP4::Properties *
MP4::File::audioProperties() const
{
return d->properties;
}
bool
MP4::File::checkValid(const MP4::AtomList &list)
{
for(uint i = 0; i < list.size(); i++) {
if(list[i]->length == 0)
return false;
if(!checkValid(list[i]->children))
return false;
}
return true;
}
void
MP4::File::read(bool readProperties, Properties::ReadStyle audioPropertiesStyle)
{
if(!isValid())
return;
d->atoms = new Atoms(this);
if (!checkValid(d->atoms->atoms)) {
setValid(false);
return;
}
// must have a moov atom, otherwise consider it invalid
MP4::Atom *moov = d->atoms->find("moov");
if(!moov) {
setValid(false);
return;
}
d->tag = new Tag(this, d->atoms);
if(readProperties) {
d->properties = new Properties(this, d->atoms, audioPropertiesStyle);
}
}
bool
MP4::File::save()
{
if(readOnly()) {
debug("MP4::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("MP4::File::save() -- Trying to save invalid file.");
return false;
}
return d->tag->save();
}
#endif

View File

@@ -1,103 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_MP4FILE_H
#define TAGLIB_MP4FILE_H
#include "tag.h"
#include "tfile.h"
#include "taglib_export.h"
#include "mp4properties.h"
#include "mp4tag.h"
namespace TagLib {
//! An implementation of MP4 (AAC, ALAC, ...) metadata
namespace MP4 {
class Atoms;
/*!
* This implements and provides an interface for MP4 files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to MP4 files.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* Contructs a MP4 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.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored.
*/
File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns a pointer to the MP4 tag of the file.
*
* MP4::Tag implements the tag interface, so this serves as the
* reimplementation of TagLib::File::tag().
*
* \note The Tag <b>is still</b> owned by the MP4::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
Tag *tag() const;
/*!
* Returns the MP4 audio properties for this file.
*/
Properties *audioProperties() const;
/*!
* Save the file.
*
* This returns true if the save was successful.
*/
bool save();
private:
void read(bool readProperties, Properties::ReadStyle audioPropertiesStyle);
bool checkValid(const MP4::AtomList &list);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -1,149 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_MP4
#include <taglib.h>
#include <tdebug.h>
#include "mp4item.h"
using namespace TagLib;
class MP4::Item::ItemPrivate : public RefCounter
{
public:
ItemPrivate() : RefCounter(), valid(true) {}
bool valid;
union {
bool m_bool;
int m_int;
IntPair m_intPair;
};
StringList m_stringList;
MP4::CoverArtList m_coverArtList;
};
MP4::Item::Item()
{
d = new ItemPrivate;
d->valid = false;
}
MP4::Item::Item(const Item &item) : d(item.d)
{
d->ref();
}
MP4::Item &
MP4::Item::operator=(const Item &item)
{
if(d->deref()) {
delete d;
}
d = item.d;
d->ref();
return *this;
}
MP4::Item::~Item()
{
if(d->deref()) {
delete d;
}
}
MP4::Item::Item(bool value)
{
d = new ItemPrivate;
d->m_bool = value;
}
MP4::Item::Item(int value)
{
d = new ItemPrivate;
d->m_int = value;
}
MP4::Item::Item(int value1, int value2)
{
d = new ItemPrivate;
d->m_intPair.first = value1;
d->m_intPair.second = value2;
}
MP4::Item::Item(const StringList &value)
{
d = new ItemPrivate;
d->m_stringList = value;
}
MP4::Item::Item(const MP4::CoverArtList &value)
{
d = new ItemPrivate;
d->m_coverArtList = value;
}
bool
MP4::Item::toBool() const
{
return d->m_bool;
}
int
MP4::Item::toInt() const
{
return d->m_int;
}
MP4::Item::IntPair
MP4::Item::toIntPair() const
{
return d->m_intPair;
}
StringList
MP4::Item::toStringList() const
{
return d->m_stringList;
}
MP4::CoverArtList
MP4::Item::toCoverArtList() const
{
return d->m_coverArtList;
}
bool
MP4::Item::isValid() const
{
return d->valid;
}
#endif

View File

@@ -1,72 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_MP4ITEM_H
#define TAGLIB_MP4ITEM_H
#include "tstringlist.h"
#include "mp4coverart.h"
#include "taglib_export.h"
namespace TagLib {
namespace MP4 {
class TAGLIB_EXPORT Item
{
public:
struct IntPair {
int first, second;
};
Item();
Item(const Item &item);
Item &operator=(const Item &item);
~Item();
Item(int value);
Item(bool value);
Item(int first, int second);
Item(const StringList &value);
Item(const CoverArtList &value);
int toInt() const;
bool toBool() const;
IntPair toIntPair() const;
StringList toStringList() const;
CoverArtList toCoverArtList() const;
bool isValid() const;
private:
class ItemPrivate;
ItemPrivate *d;
};
}
}
#endif

View File

@@ -1,178 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_MP4
#include <tdebug.h>
#include <tstring.h>
#include "mp4file.h"
#include "mp4atom.h"
#include "mp4properties.h"
using namespace TagLib;
class MP4::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0) {}
int length;
int bitrate;
int sampleRate;
int channels;
int bitsPerSample;
};
MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
: AudioProperties(style)
{
d = new PropertiesPrivate;
MP4::Atom *moov = atoms->find("moov");
if(!moov) {
debug("MP4: Atom 'moov' not found");
return;
}
MP4::Atom *trak = 0;
ByteVector data;
MP4::AtomList trakList = moov->findall("trak");
for (unsigned int i = 0; i < trakList.size(); i++) {
trak = trakList[i];
MP4::Atom *hdlr = trak->find("mdia", "hdlr");
if(!hdlr) {
debug("MP4: Atom 'trak.mdia.hdlr' not found");
return;
}
file->seek(hdlr->offset);
data = file->readBlock(hdlr->length);
if(data.mid(16, 4) == "soun") {
break;
}
trak = 0;
}
if (!trak) {
debug("MP4: No audio tracks");
return;
}
MP4::Atom *mdhd = trak->find("mdia", "mdhd");
if(!mdhd) {
debug("MP4: Atom 'trak.mdia.mdhd' not found");
return;
}
file->seek(mdhd->offset);
data = file->readBlock(mdhd->length);
uint version = data[8];
if(version == 1) {
if (data.size() < 36 + 8) {
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
long long unit = data.mid(28, 8).toLongLong();
long long length = data.mid(36, 8).toLongLong();
d->length = unit ? int(length / unit) : 0;
}
else {
if (data.size() < 24 + 4) {
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
unsigned int unit = data.mid(20, 4).toUInt();
unsigned int length = data.mid(24, 4).toUInt();
d->length = unit ? length / unit : 0;
}
MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
if(!atom) {
return;
}
file->seek(atom->offset);
data = file->readBlock(atom->length);
if(data.mid(20, 4) == "mp4a") {
d->channels = data.mid(40, 2).toShort();
d->bitsPerSample = data.mid(42, 2).toShort();
d->sampleRate = data.mid(46, 4).toUInt();
if(data.mid(56, 4) == "esds" && data[64] == 0x03) {
long pos = 65;
if(data.mid(pos, 3) == "\x80\x80\x80") {
pos += 3;
}
pos += 4;
if(data[pos] == 0x04) {
pos += 1;
if(data.mid(pos, 3) == "\x80\x80\x80") {
pos += 3;
}
pos += 10;
d->bitrate = (data.mid(pos, 4).toUInt() + 500) / 1000;
}
}
}
}
MP4::Properties::~Properties()
{
delete d;
}
int
MP4::Properties::channels() const
{
return d->channels;
}
int
MP4::Properties::sampleRate() const
{
return d->sampleRate;
}
int
MP4::Properties::length() const
{
return d->length;
}
int
MP4::Properties::bitrate() const
{
return d->bitrate;
}
int
MP4::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
#endif

View File

@@ -1,61 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_MP4PROPERTIES_H
#define TAGLIB_MP4PROPERTIES_H
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
namespace MP4 {
class Atoms;
class File;
//! An implementation of MP4 audio properties
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
Properties(File *file, Atoms *atoms, ReadStyle style = Average);
virtual ~Properties();
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
virtual int bitsPerSample() const;
private:
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
#endif

View File

@@ -1,636 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_MP4
#include <tdebug.h>
#include <tstring.h>
#include "mp4atom.h"
#include "mp4tag.h"
#include "id3v1genres.h"
using namespace TagLib;
class MP4::Tag::TagPrivate
{
public:
TagPrivate() : file(0), atoms(0) {}
~TagPrivate() {}
TagLib::File *file;
Atoms *atoms;
ItemListMap items;
};
MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms)
{
d = new TagPrivate;
d->file = file;
d->atoms = atoms;
MP4::Atom *ilst = atoms->find("moov", "udta", "meta", "ilst");
if(!ilst) {
//debug("Atom moov.udta.meta.ilst not found.");
return;
}
for(unsigned int i = 0; i < ilst->children.size(); i++) {
MP4::Atom *atom = ilst->children[i];
file->seek(atom->offset + 8);
if(atom->name == "----") {
parseFreeForm(atom, file);
}
else if(atom->name == "trkn" || atom->name == "disk") {
parseIntPair(atom, file);
}
else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst") {
parseBool(atom, file);
}
else if(atom->name == "tmpo") {
parseInt(atom, file);
}
else if(atom->name == "gnre") {
parseGnre(atom, file);
}
else if(atom->name == "covr") {
parseCovr(atom, file);
}
else {
parseText(atom, file);
}
}
}
MP4::Tag::~Tag()
{
delete d;
}
ByteVectorList
MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
{
ByteVectorList result;
ByteVector data = file->readBlock(atom->length - 8);
int i = 0;
unsigned int pos = 0;
while(pos < data.size()) {
int length = data.mid(pos, 4).toUInt();
ByteVector name = data.mid(pos + 4, 4);
int flags = data.mid(pos + 8, 4).toUInt();
if(freeForm && i < 2) {
if(i == 0 && name != "mean") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
return result;
}
else if(i == 1 && name != "name") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
return result;
}
result.append(data.mid(pos + 12, length - 12));
}
else {
if(name != "data") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
return result;
}
if(expectedFlags == -1 || flags == expectedFlags) {
result.append(data.mid(pos + 16, length - 16));
}
}
pos += length;
i++;
}
return result;
}
void
MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
d->items.insert(atom->name, (int)data[0].toShort());
}
}
void
MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
int idx = (int)data[0].toShort();
if(!d->items.contains("\251gen") && idx > 0) {
d->items.insert("\251gen", StringList(ID3v1::genre(idx - 1)));
}
}
}
void
MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
int a = data[0].mid(2, 2).toShort();
int b = data[0].mid(4, 2).toShort();
d->items.insert(atom->name, MP4::Item(a, b));
}
}
void
MP4::Tag::parseBool(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
bool value = data[0].size() ? data[0][0] != '\0' : false;
d->items.insert(atom->name, value);
}
}
void
MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags)
{
ByteVectorList data = parseData(atom, file, expectedFlags);
if(data.size()) {
StringList value;
for(unsigned int i = 0; i < data.size(); i++) {
value.append(String(data[i], String::UTF8));
}
d->items.insert(atom->name, value);
}
}
void
MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file, 1, true);
if(data.size() > 2) {
StringList value;
for(unsigned int i = 2; i < data.size(); i++) {
value.append(String(data[i], String::UTF8));
}
String name = "----:" + data[0] + ':' + data[1];
d->items.insert(name, value);
}
}
void
MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
{
MP4::CoverArtList value;
ByteVector data = file->readBlock(atom->length - 8);
unsigned int pos = 0;
while(pos < data.size()) {
int length = data.mid(pos, 4).toUInt();
ByteVector name = data.mid(pos + 4, 4);
int flags = data.mid(pos + 8, 4).toUInt();
if(name != "data") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
break;
}
if(flags == MP4::CoverArt::PNG || flags == MP4::CoverArt::JPEG) {
value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
data.mid(pos + 16, length - 16)));
}
pos += length;
}
if(value.size() > 0)
d->items.insert(atom->name, value);
}
ByteVector
MP4::Tag::padIlst(const ByteVector &data, int length)
{
if (length == -1) {
length = ((data.size() + 1023) & ~1023) - data.size();
}
return renderAtom("free", ByteVector(length, '\1'));
}
ByteVector
MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data)
{
return ByteVector::fromUInt(data.size() + 8) + name + data;
}
ByteVector
MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data)
{
ByteVector result;
for(unsigned int i = 0; i < data.size(); i++) {
result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + data[i]));
}
return renderAtom(name, result);
}
ByteVector
MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector(1, item.toBool() ? '\1' : '\0'));
return renderData(name, 0x15, data);
}
ByteVector
MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector::fromShort(item.toInt()));
return renderData(name, 0x15, data);
}
ByteVector
MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector(2, '\0') +
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second) +
ByteVector(2, '\0'));
return renderData(name, 0x00, data);
}
ByteVector
MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector(2, '\0') +
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second));
return renderData(name, 0x00, data);
}
ByteVector
MP4::Tag::renderText(const ByteVector &name, MP4::Item &item, int flags)
{
ByteVectorList data;
StringList value = item.toStringList();
for(unsigned int i = 0; i < value.size(); i++) {
data.append(value[i].data(String::UTF8));
}
return renderData(name, flags, data);
}
ByteVector
MP4::Tag::renderCovr(const ByteVector &name, MP4::Item &item)
{
ByteVector data;
MP4::CoverArtList value = item.toCoverArtList();
for(unsigned int i = 0; i < value.size(); i++) {
data.append(renderAtom("data", ByteVector::fromUInt(value[i].format()) +
ByteVector(4, '\0') + value[i].data()));
}
return renderAtom(name, data);
}
ByteVector
MP4::Tag::renderFreeForm(const String &name, MP4::Item &item)
{
StringList header = StringList::split(name, ":");
if (header.size() != 3) {
debug("MP4: Invalid free-form item name \"" + name + "\"");
return ByteVector::null;
}
ByteVector data;
data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8)));
data.append(renderAtom("name", ByteVector::fromUInt(0) + header[2].data(String::UTF8)));
StringList value = item.toStringList();
for(unsigned int i = 0; i < value.size(); i++) {
data.append(renderAtom("data", ByteVector::fromUInt(1) + ByteVector(4, '\0') + value[i].data(String::UTF8)));
}
return renderAtom("----", data);
}
bool
MP4::Tag::save()
{
ByteVector data;
for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
const String name = i->first;
if(name.startsWith("----")) {
data.append(renderFreeForm(name, i->second));
}
else if(name == "trkn") {
data.append(renderIntPair(name.data(String::Latin1), i->second));
}
else if(name == "disk") {
data.append(renderIntPairNoTrailing(name.data(String::Latin1), i->second));
}
else if(name == "cpil" || name == "pgap" || name == "pcst") {
data.append(renderBool(name.data(String::Latin1), i->second));
}
else if(name == "tmpo") {
data.append(renderInt(name.data(String::Latin1), i->second));
}
else if(name == "covr") {
data.append(renderCovr(name.data(String::Latin1), i->second));
}
else if(name.size() == 4){
data.append(renderText(name.data(String::Latin1), i->second));
}
else {
debug("MP4: Unknown item name \"" + name + "\"");
}
}
data = renderAtom("ilst", data);
AtomList path = d->atoms->path("moov", "udta", "meta", "ilst");
if(path.size() == 4) {
saveExisting(data, path);
}
else {
saveNew(data);
}
return true;
}
void
MP4::Tag::updateParents(AtomList &path, long delta, int ignore)
{
for(unsigned int i = 0; i < path.size() - ignore; i++) {
d->file->seek(path[i]->offset);
long size = d->file->readBlock(4).toUInt();
// 64-bit
if (size == 1) {
d->file->seek(4, File::Current); // Skip name
long long longSize = d->file->readBlock(8).toLongLong();
// Seek the offset of the 64-bit size
d->file->seek(path[i]->offset + 8);
d->file->writeBlock(ByteVector::fromLongLong(longSize + delta));
}
// 32-bit
else {
d->file->seek(path[i]->offset);
d->file->writeBlock(ByteVector::fromUInt(size + delta));
}
}
}
void
MP4::Tag::updateOffsets(long delta, long offset)
{
MP4::Atom *moov = d->atoms->find("moov");
if(moov) {
MP4::AtomList stco = moov->findall("stco", true);
for(unsigned int i = 0; i < stco.size(); i++) {
MP4::Atom *atom = stco[i];
if(atom->offset > offset) {
atom->offset += delta;
}
d->file->seek(atom->offset + 12);
ByteVector data = d->file->readBlock(atom->length - 12);
unsigned int count = data.mid(0, 4).toUInt();
d->file->seek(atom->offset + 16);
int pos = 4;
while(count--) {
long o = data.mid(pos, 4).toUInt();
if(o > offset) {
o += delta;
}
d->file->writeBlock(ByteVector::fromUInt(o));
pos += 4;
}
}
MP4::AtomList co64 = moov->findall("co64", true);
for(unsigned int i = 0; i < co64.size(); i++) {
MP4::Atom *atom = co64[i];
if(atom->offset > offset) {
atom->offset += delta;
}
d->file->seek(atom->offset + 12);
ByteVector data = d->file->readBlock(atom->length - 12);
unsigned int count = data.mid(0, 4).toUInt();
d->file->seek(atom->offset + 16);
int pos = 4;
while(count--) {
long long o = data.mid(pos, 8).toLongLong();
if(o > offset) {
o += delta;
}
d->file->writeBlock(ByteVector::fromLongLong(o));
pos += 8;
}
}
}
MP4::Atom *moof = d->atoms->find("moof");
if(moof) {
MP4::AtomList tfhd = moof->findall("tfhd", true);
for(unsigned int i = 0; i < tfhd.size(); i++) {
MP4::Atom *atom = tfhd[i];
if(atom->offset > offset) {
atom->offset += delta;
}
d->file->seek(atom->offset + 9);
ByteVector data = d->file->readBlock(atom->offset - 9);
unsigned int flags = (ByteVector(1, '\0') + data.mid(0, 3)).toUInt();
if(flags & 1) {
long long o = data.mid(7, 8).toLongLong();
if(o > offset) {
o += delta;
}
d->file->seek(atom->offset + 16);
d->file->writeBlock(ByteVector::fromLongLong(o));
}
}
}
}
void
MP4::Tag::saveNew(ByteVector &data)
{
data = renderAtom("meta", TagLib::ByteVector(4, '\0') +
renderAtom("hdlr", TagLib::ByteVector(8, '\0') + TagLib::ByteVector("mdirappl") + TagLib::ByteVector(9, '\0')) +
data + padIlst(data));
AtomList path = d->atoms->path("moov", "udta");
if(path.size() != 2) {
path = d->atoms->path("moov");
data = renderAtom("udta", data);
}
long offset = path[path.size() - 1]->offset + 8;
d->file->insert(data, offset, 0);
updateParents(path, data.size());
updateOffsets(data.size(), offset);
}
void
MP4::Tag::saveExisting(ByteVector &data, AtomList &path)
{
MP4::Atom *ilst = path[path.size() - 1];
long offset = ilst->offset;
long length = ilst->length;
MP4::Atom *meta = path[path.size() - 2];
AtomList::Iterator index = meta->children.find(ilst);
// check if there is an atom before 'ilst', and possibly use it as padding
if(index != meta->children.begin()) {
AtomList::Iterator prevIndex = index;
prevIndex--;
MP4::Atom *prev = *prevIndex;
if(prev->name == "free") {
offset = prev->offset;
length += prev->length;
}
}
// check if there is an atom after 'ilst', and possibly use it as padding
AtomList::Iterator nextIndex = index;
nextIndex++;
if(nextIndex != meta->children.end()) {
MP4::Atom *next = *nextIndex;
if(next->name == "free") {
length += next->length;
}
}
long delta = data.size() - length;
if(delta > 0 || (delta < 0 && delta > -8)) {
data.append(padIlst(data));
delta = data.size() - length;
}
else if(delta < 0) {
data.append(padIlst(data, -delta - 8));
delta = 0;
}
d->file->insert(data, offset, length);
if(delta) {
updateParents(path, delta, 1);
updateOffsets(delta, offset);
}
}
String
MP4::Tag::title() const
{
if(d->items.contains("\251nam"))
return d->items["\251nam"].toStringList().toString(", ");
return String::null;
}
String
MP4::Tag::artist() const
{
if(d->items.contains("\251ART"))
return d->items["\251ART"].toStringList().toString(", ");
return String::null;
}
String
MP4::Tag::album() const
{
if(d->items.contains("\251alb"))
return d->items["\251alb"].toStringList().toString(", ");
return String::null;
}
String
MP4::Tag::comment() const
{
if(d->items.contains("\251cmt"))
return d->items["\251cmt"].toStringList().toString(", ");
return String::null;
}
String
MP4::Tag::genre() const
{
if(d->items.contains("\251gen"))
return d->items["\251gen"].toStringList().toString(", ");
return String::null;
}
unsigned int
MP4::Tag::year() const
{
if(d->items.contains("\251day"))
return d->items["\251day"].toStringList().toString().toInt();
return 0;
}
unsigned int
MP4::Tag::track() const
{
if(d->items.contains("trkn"))
return d->items["trkn"].toIntPair().first;
return 0;
}
void
MP4::Tag::setTitle(const String &value)
{
d->items["\251nam"] = StringList(value);
}
void
MP4::Tag::setArtist(const String &value)
{
d->items["\251ART"] = StringList(value);
}
void
MP4::Tag::setAlbum(const String &value)
{
d->items["\251alb"] = StringList(value);
}
void
MP4::Tag::setComment(const String &value)
{
d->items["\251cmt"] = StringList(value);
}
void
MP4::Tag::setGenre(const String &value)
{
d->items["\251gen"] = StringList(value);
}
void
MP4::Tag::setYear(uint value)
{
d->items["\251day"] = StringList(String::number(value));
}
void
MP4::Tag::setTrack(uint value)
{
d->items["trkn"] = MP4::Item(value, 0);
}
MP4::ItemListMap &
MP4::Tag::itemListMap()
{
return d->items;
}
#endif

View File

@@ -1,104 +0,0 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_MP4TAG_H
#define TAGLIB_MP4TAG_H
#include "tag.h"
#include "tbytevectorlist.h"
#include "tfile.h"
#include "tmap.h"
#include "tstringlist.h"
#include "taglib_export.h"
#include "mp4atom.h"
#include "mp4item.h"
namespace TagLib {
namespace MP4 {
typedef TagLib::Map<String, Item> ItemListMap;
class TAGLIB_EXPORT Tag: public TagLib::Tag
{
public:
Tag(TagLib::File *file, Atoms *atoms);
~Tag();
bool save();
String title() const;
String artist() const;
String album() const;
String comment() const;
String genre() const;
uint year() const;
uint track() const;
void setTitle(const String &value);
void setArtist(const String &value);
void setAlbum(const String &value);
void setComment(const String &value);
void setGenre(const String &value);
void setYear(uint value);
void setTrack(uint value);
ItemListMap &itemListMap();
private:
TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
void parseText(Atom *atom, TagLib::File *file, int expectedFlags = 1);
void parseFreeForm(Atom *atom, TagLib::File *file);
void parseInt(Atom *atom, TagLib::File *file);
void parseGnre(Atom *atom, TagLib::File *file);
void parseIntPair(Atom *atom, TagLib::File *file);
void parseBool(Atom *atom, TagLib::File *file);
void parseCovr(Atom *atom, TagLib::File *file);
TagLib::ByteVector padIlst(const ByteVector &data, int length = -1);
TagLib::ByteVector renderAtom(const ByteVector &name, const TagLib::ByteVector &data);
TagLib::ByteVector renderData(const ByteVector &name, int flags, const TagLib::ByteVectorList &data);
TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = 1);
TagLib::ByteVector renderFreeForm(const String &name, Item &item);
TagLib::ByteVector renderBool(const ByteVector &name, Item &item);
TagLib::ByteVector renderInt(const ByteVector &name, Item &item);
TagLib::ByteVector renderIntPair(const ByteVector &name, Item &item);
TagLib::ByteVector renderIntPairNoTrailing(const ByteVector &name, Item &item);
TagLib::ByteVector renderCovr(const ByteVector &name, Item &item);
void updateParents(AtomList &path, long delta, int ignore = 0);
void updateOffsets(long delta, long offset);
void saveNew(TagLib::ByteVector &data);
void saveExisting(TagLib::ByteVector &data, AtomList &path);
class TagPrivate;
TagPrivate *d;
};
}
}
#endif

View File

@@ -27,6 +27,7 @@
#include <tstring.h> #include <tstring.h>
#include <tagunion.h> #include <tagunion.h>
#include <tdebug.h> #include <tdebug.h>
#include <tpropertymap.h>
#include "mpcfile.h" #include "mpcfile.h"
#include "id3v1tag.h" #include "id3v1tag.h"
@@ -38,7 +39,7 @@ using namespace TagLib;
namespace namespace
{ {
enum { APEIndex, ID3v1Index }; enum { MPCAPEIndex = 0, MPCID3v1Index = 1 };
} }
class MPC::File::FilePrivate class MPC::File::FilePrivate
@@ -93,6 +94,15 @@ MPC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file) Properties::ReadStyle propertiesStyle) : TagLib::File(file)
{ {
d = new FilePrivate; d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
MPC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
@@ -106,6 +116,30 @@ TagLib::Tag *MPC::File::tag() const
return &d->tag; return &d->tag;
} }
PropertyMap MPC::File::properties() const
{
if(d->hasAPE)
return d->tag.access<APE::Tag>(MPCAPEIndex, false)->properties();
if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(MPCID3v1Index, false)->properties();
return PropertyMap();
}
void MPC::File::removeUnsupportedProperties(const StringList &properties)
{
if(d->hasAPE)
d->tag.access<APE::Tag>(MPCAPEIndex, false)->removeUnsupportedProperties(properties);
if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(MPCID3v1Index, false)->removeUnsupportedProperties(properties);
}
PropertyMap MPC::File::setProperties(const PropertyMap &properties)
{
if(d->hasID3v1)
d->tag.access<APE::Tag>(MPCID3v1Index, false)->setProperties(properties);
return d->tag.access<APE::Tag>(MPCAPEIndex, true)->setProperties(properties);
}
MPC::Properties *MPC::File::audioProperties() const MPC::Properties *MPC::File::audioProperties() const
{ {
return d->properties; return d->properties;
@@ -189,18 +223,18 @@ bool MPC::File::save()
ID3v1::Tag *MPC::File::ID3v1Tag(bool create) ID3v1::Tag *MPC::File::ID3v1Tag(bool create)
{ {
return d->tag.access<ID3v1::Tag>(ID3v1Index, create); return d->tag.access<ID3v1::Tag>(MPCID3v1Index, create);
} }
APE::Tag *MPC::File::APETag(bool create) APE::Tag *MPC::File::APETag(bool create)
{ {
return d->tag.access<APE::Tag>(APEIndex, create); return d->tag.access<APE::Tag>(MPCAPEIndex, create);
} }
void MPC::File::strip(int tags) void MPC::File::strip(int tags)
{ {
if(tags & ID3v1) { if(tags & ID3v1) {
d->tag.set(ID3v1Index, 0); d->tag.set(MPCID3v1Index, 0);
APETag(true); APETag(true);
} }
@@ -210,7 +244,7 @@ void MPC::File::strip(int tags)
} }
if(tags & APE) { if(tags & APE) {
d->tag.set(APEIndex, 0); d->tag.set(MPCAPEIndex, 0);
if(!ID3v1Tag()) if(!ID3v1Tag())
APETag(true); APETag(true);
@@ -222,6 +256,15 @@ void MPC::File::remove(int tags)
strip(tags); strip(tags);
} }
bool MPC::File::hasID3v1Tag() const
{
return d->hasID3v1;
}
bool MPC::File::hasAPETag() const
{
return d->hasAPE;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
@@ -234,7 +277,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
d->ID3v1Location = findID3v1(); d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) { if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); d->tag.set(MPCID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true; d->hasID3v1 = true;
} }
@@ -245,7 +288,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
d->APELocation = findAPE(); d->APELocation = findAPE();
if(d->APELocation >= 0) { if(d->APELocation >= 0) {
d->tag.set(APEIndex, new APE::Tag(this, d->APELocation)); d->tag.set(MPCAPEIndex, new APE::Tag(this, d->APELocation));
d->APESize = APETag()->footer()->completeTagSize(); d->APESize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize; d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
@@ -274,8 +317,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
// Look for MPC metadata // Look for MPC metadata
if(readProperties) { if(readProperties) {
d->properties = new Properties(readBlock(MPC::HeaderSize), d->properties = new Properties(this, length() - d->ID3v2Size - d->APESize);
length() - d->ID3v2Size - d->APESize);
} }
} }

View File

@@ -28,9 +28,12 @@
#include "taglib_export.h" #include "taglib_export.h"
#include "tfile.h" #include "tfile.h"
#include "tag.h"
#include "mpcproperties.h" #include "mpcproperties.h"
#include "tlist.h"
namespace TagLib { namespace TagLib {
class Tag; class Tag;
@@ -81,13 +84,26 @@ namespace TagLib {
}; };
/*! /*!
* Contructs an MPC file from \a file. If \a readProperties is true the * Constructs an MPC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If * file's audio properties will also be read.
* false, \a propertiesStyle is ignored. *
* \note In the current implementation, \a propertiesStyle is ignored.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an MPC file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
*/ */
@@ -99,6 +115,22 @@ namespace TagLib {
*/ */
virtual TagLib::Tag *tag() const; virtual TagLib::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains both an APE and an ID3v1 tag, only the APE
* tag will be converted to the PropertyMap.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* Affects only the APEv2 tag which will be created if necessary.
* If an ID3v1 tag exists, it will be updated as well.
*/
PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the MPC::Properties for this file. If no audio properties * Returns the MPC::Properties for this file. If no audio properties
* were read then this will return a null pointer. * were read then this will return a null pointer.
@@ -113,28 +145,39 @@ namespace TagLib {
/*! /*!
* Returns a pointer to the ID3v1 tag of the file. * Returns a pointer to the ID3v1 tag of the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this returns a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create * if there is no valid APE tag. If \a create is true it will create
* an ID3v1 tag if one does not exist. If there is already an APE tag, the * an APE tag if one does not exist and returns a valid pointer.
* new ID3v1 tag will be placed after it.
* *
* \note The Tag <b>is still</b> owned by the APE::File and should not be * \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasID3v1Tag()
*/ */
ID3v1::Tag *ID3v1Tag(bool create = false); ID3v1::Tag *ID3v1Tag(bool create = false);
/*! /*!
* Returns a pointer to the APE tag of the file. * Returns a pointer to the APE tag of the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is true it will create * if there is no valid APE tag. If \a create is true it will create
* a APE tag if one does not exist. If there is already an ID3v1 tag, thes * an APE tag if one does not exist and returns a valid pointer. If
* new APE tag will be placed before it. * there already be an ID3v1 tag, the new APE tag will be placed before it.
* *
* \note The Tag <b>is still</b> owned by the APE::File and should not be * \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasAPETag()
*/ */
APE::Tag *APETag(bool create = false); APE::Tag *APETag(bool create = false);
@@ -155,6 +198,19 @@ namespace TagLib {
*/ */
void remove(int tags = AllTags); void remove(int tags = AllTags);
/*!
* Returns whether or not the file on disk actually has an ID3v1 tag.
*
* \see ID3v1Tag()
*/
bool hasID3v1Tag() const;
/*!
* Returns whether or not the file on disk actually has an APE tag.
*
* \see APETag()
*/
bool hasAPETag() const;
private: private:
File(const File &); File(const File &);

View File

@@ -26,6 +26,7 @@
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include <bitset> #include <bitset>
#include <math.h>
#include "mpcproperties.h" #include "mpcproperties.h"
#include "mpcfile.h" #include "mpcfile.h"
@@ -35,17 +36,21 @@ using namespace TagLib;
class MPC::Properties::PropertiesPrivate class MPC::Properties::PropertiesPrivate
{ {
public: public:
PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) : PropertiesPrivate(long length, ReadStyle s) :
data(d),
streamLength(length), streamLength(length),
style(s), style(s),
version(0), version(0),
length(0), length(0),
bitrate(0), bitrate(0),
sampleRate(0), sampleRate(0),
channels(0) {} channels(0),
totalFrames(0),
sampleFrames(0),
trackGain(0),
trackPeak(0),
albumGain(0),
albumPeak(0) {}
ByteVector data;
long streamLength; long streamLength;
ReadStyle style; ReadStyle style;
int version; int version;
@@ -53,6 +58,13 @@ public:
int bitrate; int bitrate;
int sampleRate; int sampleRate;
int channels; int channels;
uint totalFrames;
uint sampleFrames;
uint trackGain;
uint trackPeak;
uint albumGain;
uint albumPeak;
String flags;
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -61,8 +73,22 @@ public:
MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style) MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
{ {
d = new PropertiesPrivate(data, streamLength, style); d = new PropertiesPrivate(streamLength, style);
read(); readSV7(data);
}
MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(streamLength, style);
ByteVector magic = file->readBlock(4);
if(magic == "MPCK") {
// Musepack version 8
readSV8(file);
}
else {
// Musepack version 7 or older, fixed size header
readSV7(magic + file->readBlock(MPC::HeaderSize - 4));
}
} }
MPC::Properties::~Properties() MPC::Properties::~Properties()
@@ -95,30 +121,179 @@ int MPC::Properties::mpcVersion() const
return d->version; return d->version;
} }
TagLib::uint MPC::Properties::totalFrames() const
{
return d->totalFrames;
}
TagLib::uint MPC::Properties::sampleFrames() const
{
return d->sampleFrames;
}
int MPC::Properties::trackGain() const
{
return d->trackGain;
}
int MPC::Properties::trackPeak() const
{
return d->trackPeak;
}
int MPC::Properties::albumGain() const
{
return d->albumGain;
}
int MPC::Properties::albumPeak() const
{
return d->albumPeak;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
unsigned long readSize(File *file, TagLib::uint &sizelength)
{
unsigned char tmp;
unsigned long size = 0;
do {
ByteVector b = file->readBlock(1);
tmp = b[0];
size = (size << 7) | (tmp & 0x7F);
sizelength++;
} while((tmp & 0x80));
return size;
}
unsigned long readSize(const ByteVector &data, TagLib::uint &sizelength)
{
unsigned char tmp;
unsigned long size = 0;
unsigned long pos = 0;
do {
tmp = data[pos++];
size = (size << 7) | (tmp & 0x7F);
sizelength++;
} while((tmp & 0x80) && (pos < data.size()));
return size;
}
static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 }; static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 };
void MPC::Properties::read() void MPC::Properties::readSV8(File *file)
{ {
if(!d->data.startsWith("MP+")) bool readSH = false, readRG = false;
while(!readSH && !readRG) {
ByteVector packetType = file->readBlock(2);
uint packetSizeLength = 0;
unsigned long packetSize = readSize(file, packetSizeLength);
unsigned long dataSize = packetSize - 2 - packetSizeLength;
if(packetType == "SH") {
// Stream Header
// http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket
ByteVector data = file->readBlock(dataSize);
readSH = true;
TagLib::uint pos = 4;
d->version = data[pos];
pos += 1;
d->sampleFrames = readSize(data.mid(pos), pos);
ulong begSilence = readSize(data.mid(pos), pos);
std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.toUShort(pos, true)));
pos += 2;
d->sampleRate = sftable[flags[15] * 4 + flags[14] * 2 + flags[13]];
d->channels = flags[7] * 8 + flags[6] * 4 + flags[5] * 2 + flags[4] + 1;
if((d->sampleFrames - begSilence) != 0)
d->bitrate = (int)(d->streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence));
d->bitrate = d->bitrate / 1000;
d->length = (d->sampleFrames - begSilence) / d->sampleRate;
}
else if (packetType == "RG") {
// Replay Gain
// http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket
ByteVector data = file->readBlock(dataSize);
readRG = true;
int replayGainVersion = data[0];
if(replayGainVersion == 1) {
d->trackGain = data.toShort(1, true);
d->trackPeak = data.toShort(3, true);
d->albumGain = data.toShort(5, true);
d->albumPeak = data.toShort(7, true);
}
}
else if(packetType == "SE") {
break;
}
else {
file->seek(dataSize, File::Current);
}
}
}
void MPC::Properties::readSV7(const ByteVector &data)
{
if(data.startsWith("MP+")) {
d->version = data[3] & 15;
if(d->version < 7)
return; return;
d->version = d->data[3] & 15; d->totalFrames = data.toUInt(4, false);
unsigned int frames; std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(8, false)));
if(d->version >= 7) {
frames = d->data.mid(4, 4).toUInt(false);
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(d->data.mid(8, 4).toUInt(false)));
d->sampleRate = sftable[flags[17] * 2 + flags[16]]; d->sampleRate = sftable[flags[17] * 2 + flags[16]];
d->channels = 2; d->channels = 2;
uint gapless = data.toUInt(5, false);
d->trackGain = data.toShort(14, false);
d->trackPeak = data.toShort(12, false);
d->albumGain = data.toShort(18, false);
d->albumPeak = data.toShort(16, false);
// convert gain info
if(d->trackGain != 0) {
int tmp = (int)((64.82 - (short)d->trackGain / 100.) * 256. + .5);
if(tmp >= (1 << 16) || tmp < 0) tmp = 0;
d->trackGain = tmp;
}
if(d->albumGain != 0) {
int tmp = (int)((64.82 - d->albumGain / 100.) * 256. + .5);
if(tmp >= (1 << 16) || tmp < 0) tmp = 0;
d->albumGain = tmp;
}
if (d->trackPeak != 0)
d->trackPeak = (int)(log10((double)d->trackPeak) * 20 * 256 + .5);
if (d->albumPeak != 0)
d->albumPeak = (int)(log10((double)d->albumPeak) * 20 * 256 + .5);
bool trueGapless = (gapless >> 31) & 0x0001;
if(trueGapless) {
uint lastFrameSamples = (gapless >> 20) & 0x07FF;
d->sampleFrames = d->totalFrames * 1152 - lastFrameSamples;
}
else
d->sampleFrames = d->totalFrames * 1152 - 576;
} }
else { else {
uint headerData = d->data.mid(0, 4).toUInt(false); uint headerData = data.toUInt(0, false);
d->bitrate = (headerData >> 23) & 0x01ff; d->bitrate = (headerData >> 23) & 0x01ff;
d->version = (headerData >> 11) & 0x03ff; d->version = (headerData >> 11) & 0x03ff;
@@ -126,15 +301,16 @@ void MPC::Properties::read()
d->channels = 2; d->channels = 2;
if(d->version >= 5) if(d->version >= 5)
frames = d->data.mid(4, 4).toUInt(false); d->totalFrames = data.toUInt(4, false);
else else
frames = d->data.mid(6, 2).toUInt(false); d->totalFrames = data.toUShort(6, false);
d->sampleFrames = d->totalFrames * 1152 - 576;
} }
uint samples = frames * 1152 - 576; d->length = d->sampleRate > 0 ? (d->sampleFrames + (d->sampleRate / 2)) / d->sampleRate : 0;
d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0;
if(!d->bitrate) if(!d->bitrate)
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
} }

View File

@@ -50,9 +50,17 @@ namespace TagLib {
/*! /*!
* Create an instance of MPC::Properties with the data read from the * Create an instance of MPC::Properties with the data read from the
* ByteVector \a data. * ByteVector \a data.
*
* This constructor is deprecated. It only works for MPC version up to 7.
*/ */
Properties(const ByteVector &data, long streamLength, ReadStyle style = Average); Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
/*!
* Create an instance of MPC::Properties with the data read directly
* from a MPC::File.
*/
Properties(File *file, long streamLength, ReadStyle style = Average);
/*! /*!
* Destroys this MPC::Properties instance. * Destroys this MPC::Properties instance.
*/ */
@@ -66,15 +74,44 @@ namespace TagLib {
virtual int channels() const; virtual int channels() const;
/*! /*!
* Returns the version of the bitstream (SV4-SV7) * Returns the version of the bitstream (SV4-SV8)
*/ */
int mpcVersion() const; int mpcVersion() const;
uint totalFrames() const;
uint sampleFrames() const;
/*!
* Returns the track gain as an integer value,
* to convert to dB: trackGain in dB = 64.82 - (trackGain / 256)
*/
int trackGain() const;
/*!
* Returns the track peak as an integer value,
* to convert to dB: trackPeak in dB = trackPeak / 256
* to convert to floating [-1..1]: trackPeak = 10^(trackPeak / 256 / 20)/32768
*/
int trackPeak() const;
/*!
* Returns the album gain as an integer value,
* to convert to dB: albumGain in dB = 64.82 - (albumGain / 256)
*/
int albumGain() const;
/*!
* Returns the album peak as an integer value,
* to convert to dB: albumPeak in dB = albumPeak / 256
* to convert to floating [-1..1]: albumPeak = 10^(albumPeak / 256 / 20)/32768
*/
int albumPeak() const;
private: private:
Properties(const Properties &); Properties(const Properties &);
Properties &operator=(const Properties &); Properties &operator=(const Properties &);
void read(); void readSV7(const ByteVector &data);
void readSV8(File *file);
class PropertiesPrivate; class PropertiesPrivate;
PropertiesPrivate *d; PropertiesPrivate *d;

View File

@@ -207,7 +207,7 @@ ID3v1::GenreMap ID3v1::genreMap()
String ID3v1::genre(int i) String ID3v1::genre(int i)
{ {
if(i >= 0 && i < genresSize) if(i >= 0 && i < genresSize)
return genres[i]; return genres[i] + String::null; // always make a copy
return String::null; return String::null;
} }

View File

@@ -58,6 +58,10 @@ const ID3v1::StringHandler *ID3v1::Tag::TagPrivate::stringHandler = &defaultStri
// StringHandler implementation // StringHandler implementation
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
StringHandler::StringHandler()
{
}
String ID3v1::StringHandler::parse(const ByteVector &data) const String ID3v1::StringHandler::parse(const ByteVector &data) const
{ {
return String(data, String::Latin1).stripWhiteSpace(); return String(data, String::Latin1).stripWhiteSpace();
@@ -178,22 +182,32 @@ void ID3v1::Tag::setGenre(const String &s)
d->genre = ID3v1::genreIndex(s); d->genre = ID3v1::genreIndex(s);
} }
void ID3v1::Tag::setYear(uint i) void ID3v1::Tag::setYear(TagLib::uint i)
{ {
d->year = i > 0 ? String::number(i) : String::null; d->year = i > 0 ? String::number(i) : String::null;
} }
void ID3v1::Tag::setTrack(uint i) void ID3v1::Tag::setTrack(TagLib::uint i)
{ {
d->track = i < 256 ? i : 0; d->track = i < 256 ? i : 0;
} }
TagLib::uint ID3v1::Tag::genreNumber() const
{
return d->genre;
}
void ID3v1::Tag::setGenreNumber(TagLib::uint i)
{
d->genre = i < 256 ? i : 255;
}
void ID3v1::Tag::setStringHandler(const StringHandler *handler) void ID3v1::Tag::setStringHandler(const StringHandler *handler)
{ {
if(TagPrivate::stringHandler != &defaultStringHandler) if (handler)
delete TagPrivate::stringHandler;
TagPrivate::stringHandler = handler; TagPrivate::stringHandler = handler;
else
TagPrivate::stringHandler = &defaultStringHandler;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -62,6 +62,7 @@ namespace TagLib {
TAGLIB_IGNORE_MISSING_DESTRUCTOR TAGLIB_IGNORE_MISSING_DESTRUCTOR
public: public:
// BIC: Add virtual destructor. // BIC: Add virtual destructor.
StringHandler();
/*! /*!
* Decode a string from \a data. The default implementation assumes that * Decode a string from \a data. The default implementation assumes that
@@ -139,20 +140,40 @@ namespace TagLib {
virtual String album() const; virtual String album() const;
virtual String comment() const; virtual String comment() const;
virtual String genre() const; virtual String genre() const;
virtual uint year() const; virtual TagLib::uint year() const;
virtual uint track() const; virtual TagLib::uint track() 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);
virtual void setComment(const String &s); virtual void setComment(const String &s);
virtual void setGenre(const String &s); virtual void setGenre(const String &s);
virtual void setYear(uint i); virtual void setYear(TagLib::uint i);
virtual void setTrack(uint i); virtual void setTrack(TagLib::uint i);
/*!
* Returns the genre in number.
*
* /note Normally 255 indicates that this tag contains no genre.
*/
TagLib::uint genreNumber() const;
/*!
* Sets the genre in number to \a i.
*
* /note Valid value is from 0 up to 255. Normally 255 indicates that
* this tag contains no genre.
*/
void setGenreNumber(TagLib::uint i);
/*! /*!
* Sets the string handler that decides how the ID3v1 data will be * Sets the string handler that decides how the ID3v1 data will be
* converted to and from binary data. * converted to and from binary data.
* If the parameter \a handler is null, the previous handler is
* released and default ISO-8859-1 handler is restored.
*
* \note The caller is responsible for deleting the previous handler
* as needed after it is released.
* *
* \see StringHandler * \see StringHandler
*/ */

View File

@@ -152,7 +152,7 @@ ByteVector AttachedPictureFrame::renderFields() const
{ {
ByteVector data; ByteVector data;
String::Type encoding = checkEncoding(d->description, d->textEncoding); String::Type encoding = checkTextEncoding(d->description, d->textEncoding);
data.append(char(encoding)); data.append(char(encoding));
data.append(d->mimeType.data(String::Latin1)); data.append(d->mimeType.data(String::Latin1));

View File

@@ -29,6 +29,7 @@
#include <tstringlist.h> #include <tstringlist.h>
#include "commentsframe.h" #include "commentsframe.h"
#include "tpropertymap.h"
using namespace TagLib; using namespace TagLib;
using namespace ID3v2; using namespace ID3v2;
@@ -109,6 +110,19 @@ void CommentsFrame::setTextEncoding(String::Type encoding)
d->textEncoding = encoding; d->textEncoding = encoding;
} }
PropertyMap CommentsFrame::asProperties() const
{
String key = description().upper();
PropertyMap map;
if(key.isEmpty() || key == "COMMENT")
map.insert("COMMENT", text());
else if(key.isNull())
map.unsupportedData().append(L"COMM/" + description());
else
map.insert("COMMENT:" + key, text());
return map;
}
CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static
{ {
ID3v2::FrameList comments = tag->frameList("COMM"); ID3v2::FrameList comments = tag->frameList("COMM");
@@ -144,9 +158,14 @@ void CommentsFrame::parseFields(const ByteVector &data)
ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
if(l.size() == 2) { if(l.size() == 2) {
if(d->textEncoding == String::Latin1) {
d->description = Tag::latin1StringHandler()->parse(l.front());
d->text = Tag::latin1StringHandler()->parse(l.back());
} else {
d->description = String(l.front(), d->textEncoding); d->description = String(l.front(), d->textEncoding);
d->text = String(l.back(), d->textEncoding); d->text = String(l.back(), d->textEncoding);
} }
}
} }
ByteVector CommentsFrame::renderFields() const ByteVector CommentsFrame::renderFields() const
@@ -155,8 +174,8 @@ ByteVector CommentsFrame::renderFields() const
String::Type encoding = d->textEncoding; String::Type encoding = d->textEncoding;
encoding = checkEncoding(d->description, encoding); encoding = checkTextEncoding(d->description, encoding);
encoding = checkEncoding(d->text, encoding); encoding = checkTextEncoding(d->text, encoding);
v.append(char(encoding)); v.append(char(encoding));
v.append(d->language.size() == 3 ? d->language : "XXX"); v.append(d->language.size() == 3 ? d->language : "XXX");

View File

@@ -136,6 +136,17 @@ namespace TagLib {
*/ */
void setTextEncoding(String::Type encoding); void setTextEncoding(String::Type encoding);
/*!
* Parses this frame as PropertyMap with a single key.
* - if description() is empty or "COMMENT", the key will be "COMMENT"
* - if description() is not a valid PropertyMap key, the frame will be
* marked unsupported by an entry "COMM/<description>" in the unsupportedData()
* attribute of the returned map.
* - otherwise, the key will be "COMMENT:<description>"
* - The single value will be the frame's text().
*/
PropertyMap asProperties() const;
/*! /*!
* Comments each have a unique description. This searches for a comment * Comments each have a unique description. This searches for a comment
* frame with the decription \a d and returns a pointer to it. If no * frame with the decription \a d and returns a pointer to it. If no

View File

@@ -0,0 +1,162 @@
/***************************************************************************
copyright : (C) 2012 by Rupert Daniel
email : rupert@cancelmonday.com
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tdebug.h>
#include "ownershipframe.h"
#include <id3v2tag.h>
using namespace TagLib;
using namespace ID3v2;
class OwnershipFrame::OwnershipFramePrivate
{
public:
String pricePaid;
String datePurchased;
String seller;
String::Type textEncoding;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE")
{
d = new OwnershipFramePrivate;
d->textEncoding = encoding;
}
OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data)
{
d = new OwnershipFramePrivate;
setData(data);
}
OwnershipFrame::~OwnershipFrame()
{
delete d;
}
String OwnershipFrame::toString() const
{
return "pricePaid=" + d->pricePaid + " datePurchased=" + d->datePurchased + " seller=" + d->seller;
}
String OwnershipFrame::pricePaid() const
{
return d->pricePaid;
}
void OwnershipFrame::setPricePaid(const String &s)
{
d->pricePaid = s;
}
String OwnershipFrame::datePurchased() const
{
return d->datePurchased;
}
void OwnershipFrame::setDatePurchased(const String &s)
{
d->datePurchased = s;
}
String OwnershipFrame::seller() const
{
return d->seller;
}
void OwnershipFrame::setSeller(const String &s)
{
d->seller = s;
}
String::Type OwnershipFrame::textEncoding() const
{
return d->textEncoding;
}
void OwnershipFrame::setTextEncoding(String::Type encoding)
{
d->textEncoding = encoding;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void OwnershipFrame::parseFields(const ByteVector &data)
{
int pos = 0;
// Get the text encoding
d->textEncoding = String::Type(data[0]);
pos += 1;
// Read the price paid this is a null terminate string
d->pricePaid = readStringField(data, String::Latin1, &pos);
// If we don't have at least 8 bytes left then don't parse the rest of the
// data
if(data.size() - pos < 8) {
return;
}
// Read the date purchased YYYYMMDD
d->datePurchased = String(data.mid(pos, 8));
pos += 8;
// Read the seller
if(d->textEncoding == String::Latin1)
d->seller = Tag::latin1StringHandler()->parse(data.mid(pos));
else
d->seller = String(data.mid(pos), d->textEncoding);
}
ByteVector OwnershipFrame::renderFields() const
{
ByteVector v;
v.append(char(d->textEncoding));
v.append(d->pricePaid.data(String::Latin1));
v.append(textDelimiter(String::Latin1));
v.append(d->datePurchased.data(String::Latin1));
v.append(d->seller.data(d->textEncoding));
return v;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new OwnershipFramePrivate;
parseFields(fieldData(data));
}

View File

@@ -0,0 +1,151 @@
/***************************************************************************
copyright : (C) 2012 by Rupert Daniel
email : rupert@cancelmonday.com
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_OWNERSHIPFRAME_H
#define TAGLIB_OWNERSHIPFRAME_H
#include "id3v2frame.h"
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
//! An implementation of ID3v2 "ownership"
/*!
* This implements the ID3v2 ownership (OWNE frame). It consists of
* a price paid, a date purchased (YYYYMMDD) and the name of the seller.
*/
class TAGLIB_EXPORT OwnershipFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* Construct an empty ownership frame.
*/
explicit OwnershipFrame(String::Type encoding = String::Latin1);
/*!
* Construct a ownership based on the data in \a data.
*/
explicit OwnershipFrame(const ByteVector &data);
/*!
* Destroys this OwnershipFrame instance.
*/
virtual ~OwnershipFrame();
/*!
* Returns the text of this popularimeter.
*
* \see text()
*/
virtual String toString() const;
/*!
* Returns the date purchased.
*
* \see setDatePurchased()
*/
String datePurchased() const;
/*!
* Set the date purchased.
*
* \see datePurchased()
*/
void setDatePurchased(const String &datePurchased);
/*!
* Returns the price paid.
*
* \see setPricePaid()
*/
String pricePaid() const;
/*!
* Set the price paid.
*
* \see pricePaid()
*/
void setPricePaid(const String &pricePaid);
/*!
* Returns the seller.
*
* \see setSeller()
*/
String seller() const;
/*!
* Set the seller.
*
* \see seller()
*/
void setSeller(const String &seller);
/*!
* Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor
* or read from the frame when parsed.
*
* \see setTextEncoding()
* \see render()
*/
String::Type textEncoding() const;
/*!
* Sets the text encoding to be used when rendering this frame to
* \a encoding.
*
* \see textEncoding()
* \see render()
*/
void setTextEncoding(String::Type encoding);
protected:
// Reimplementations.
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
/*!
* The constructor used by the FrameFactory.
*/
OwnershipFrame(const ByteVector &data, Header *h);
OwnershipFrame(const OwnershipFrame &);
OwnershipFrame &operator=(const OwnershipFrame &);
class OwnershipFramePrivate;
OwnershipFramePrivate *d;
};
}
}
#endif

View File

@@ -109,7 +109,7 @@ void PopularimeterFrame::parseFields(const ByteVector &data)
if(pos < size) { if(pos < size) {
d->rating = (unsigned char)(data[pos++]); d->rating = (unsigned char)(data[pos++]);
if(pos < size) { if(pos < size) {
d->counter = data.mid(pos, 4).toUInt(); d->counter = data.toUInt(static_cast<uint>(pos));
} }
} }
} }

View File

@@ -191,7 +191,7 @@ void RelativeVolumeFrame::parseFields(const ByteVector &data)
ChannelData &channel = d->channels[type]; ChannelData &channel = d->channels[type];
channel.volumeAdjustment = data.mid(pos, 2).toShort(); channel.volumeAdjustment = data.toShort(static_cast<uint>(pos));
pos += 2; pos += 2;
channel.peakVolume.bitsRepresentingPeak = data[pos]; channel.peakVolume.bitsRepresentingPeak = data[pos];

View File

@@ -25,8 +25,9 @@
#include <tbytevectorlist.h> #include <tbytevectorlist.h>
#include <id3v2tag.h> #include <id3v2tag.h>
#include "textidentificationframe.h" #include "textidentificationframe.h"
#include "tpropertymap.h"
#include "id3v1genres.h"
using namespace TagLib; using namespace TagLib;
using namespace ID3v2; using namespace ID3v2;
@@ -57,6 +58,32 @@ TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) :
setData(data); setData(data);
} }
TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const PropertyMap &properties) // static
{
TextIdentificationFrame *frame = new TextIdentificationFrame("TIPL");
StringList l;
for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it){
l.append(it->first);
l.append(it->second.toString(",")); // comma-separated list of names
}
frame->setText(l);
return frame;
}
TextIdentificationFrame *TextIdentificationFrame::createTMCLFrame(const PropertyMap &properties) // static
{
TextIdentificationFrame *frame = new TextIdentificationFrame("TMCL");
StringList l;
for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it){
if(!it->first.startsWith(instrumentPrefix)) // should not happen
continue;
l.append(it->first.substr(instrumentPrefix.size()));
l.append(it->second.toString(","));
}
frame->setText(l);
return frame;
}
TextIdentificationFrame::~TextIdentificationFrame() TextIdentificationFrame::~TextIdentificationFrame()
{ {
delete d; delete d;
@@ -92,6 +119,61 @@ void TextIdentificationFrame::setTextEncoding(String::Type encoding)
d->textEncoding = encoding; d->textEncoding = encoding;
} }
// array of allowed TIPL prefixes and their corresponding key value
static const TagLib::uint involvedPeopleSize = 5;
static const char* involvedPeople[][2] = {
{"ARRANGER", "ARRANGER"},
{"ENGINEER", "ENGINEER"},
{"PRODUCER", "PRODUCER"},
{"DJ-MIX", "DJMIXER"},
{"MIX", "MIXER"},
};
const KeyConversionMap &TextIdentificationFrame::involvedPeopleMap() // static
{
static KeyConversionMap m;
if(m.isEmpty())
for(uint i = 0; i < involvedPeopleSize; ++i)
m.insert(involvedPeople[i][1], involvedPeople[i][0]);
return m;
}
PropertyMap TextIdentificationFrame::asProperties() const
{
if(frameID() == "TIPL")
return makeTIPLProperties();
if(frameID() == "TMCL")
return makeTMCLProperties();
PropertyMap map;
String tagName = frameIDToKey(frameID());
if(tagName.isNull()) {
map.unsupportedData().append(frameID());
return map;
}
StringList values = fieldList();
if(tagName == "GENRE") {
// Special case: Support ID3v1-style genre numbers. They are not officially supported in
// ID3v2, however it seems that still a lot of programs use them.
for(StringList::Iterator it = values.begin(); it != values.end(); ++it) {
bool ok = false;
int test = it->toInt(&ok); // test if the genre value is an integer
if(ok)
*it = ID3v1::genre(test);
}
} else if(tagName == "DATE") {
for(StringList::Iterator it = values.begin(); it != values.end(); ++it) {
// ID3v2 specifies ISO8601 timestamps which contain a 'T' as separator between date and time.
// Since this is unusual in other formats, the T is removed.
int tpos = it->find("T");
if(tpos != -1)
(*it)[tpos] = ' ';
}
}
PropertyMap ret;
ret.insert(tagName, values);
return ret;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// TextIdentificationFrame protected members // TextIdentificationFrame protected members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -131,15 +213,17 @@ void TextIdentificationFrame::parseFields(const ByteVector &data)
for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) { for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) {
if(!(*it).isEmpty()) { if(!(*it).isEmpty()) {
String s(*it, d->textEncoding); if(d->textEncoding == String::Latin1)
d->fieldList.append(s); d->fieldList.append(Tag::latin1StringHandler()->parse(*it));
else
d->fieldList.append(String(*it, d->textEncoding));
} }
} }
} }
ByteVector TextIdentificationFrame::renderFields() const ByteVector TextIdentificationFrame::renderFields() const
{ {
String::Type encoding = checkEncoding(d->fieldList, d->textEncoding); String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding);
ByteVector v; ByteVector v;
@@ -170,6 +254,55 @@ TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header
parseFields(fieldData(data)); parseFields(fieldData(data));
} }
PropertyMap TextIdentificationFrame::makeTIPLProperties() const
{
PropertyMap map;
if(fieldList().size() % 2 != 0){
// according to the ID3 spec, TIPL must contain an even number of entries
map.unsupportedData().append(frameID());
return map;
}
StringList l = fieldList();
for(StringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
bool found = false;
for(uint i = 0; i < involvedPeopleSize; ++i)
if(*it == involvedPeople[i][0]) {
map.insert(involvedPeople[i][1], (++it)->split(","));
found = true;
break;
}
if(!found){
// invalid involved role -> mark whole frame as unsupported in order to be consisten with writing
map.clear();
map.unsupportedData().append(frameID());
return map;
}
}
return map;
}
PropertyMap TextIdentificationFrame::makeTMCLProperties() const
{
PropertyMap map;
if(fieldList().size() % 2 != 0){
// according to the ID3 spec, TMCL must contain an even number of entries
map.unsupportedData().append(frameID());
return map;
}
StringList l = fieldList();
for(StringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
String instrument = it->upper();
if(instrument.isNull()) {
// instrument is not a valid key -> frame unsupported
map.clear();
map.unsupportedData().append(frameID());
return map;
}
map.insert(L"PERFORMER:" + instrument, (++it)->split(","));
}
return map;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// UserTextIdentificationFrame public members // UserTextIdentificationFrame public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -191,6 +324,14 @@ UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data)
checkFields(); checkFields();
} }
UserTextIdentificationFrame::UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding) :
TextIdentificationFrame("TXXX", encoding),
d(0)
{
setDescription(description);
setText(values);
}
String UserTextIdentificationFrame::toString() const String UserTextIdentificationFrame::toString() const
{ {
return "[" + description() + "] " + fieldList().toString(); return "[" + description() + "] " + fieldList().toString();
@@ -238,6 +379,17 @@ void UserTextIdentificationFrame::setDescription(const String &s)
TextIdentificationFrame::setText(l); TextIdentificationFrame::setText(l);
} }
PropertyMap UserTextIdentificationFrame::asProperties() const
{
PropertyMap map;
String tagName = txxxToKey(description());
StringList v = fieldList();
for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it)
if(it != v.begin())
map.insert(tagName, *it);
return map;
}
UserTextIdentificationFrame *UserTextIdentificationFrame::find( UserTextIdentificationFrame *UserTextIdentificationFrame::find(
ID3v2::Tag *tag, const String &description) // static ID3v2::Tag *tag, const String &description) // static
{ {

View File

@@ -27,6 +27,7 @@
#define TAGLIB_TEXTIDENTIFICATIONFRAME_H #define TAGLIB_TEXTIDENTIFICATIONFRAME_H
#include "tstringlist.h" #include "tstringlist.h"
#include "tmap.h"
#include "taglib_export.h" #include "taglib_export.h"
#include "id3v2frame.h" #include "id3v2frame.h"
@@ -36,6 +37,7 @@ namespace TagLib {
namespace ID3v2 { namespace ID3v2 {
class Tag; class Tag;
typedef Map<String, String> KeyConversionMap;
//! An ID3v2 text identification frame implementation //! An ID3v2 text identification frame implementation
@@ -123,6 +125,20 @@ namespace TagLib {
*/ */
explicit TextIdentificationFrame(const ByteVector &data); explicit TextIdentificationFrame(const ByteVector &data);
/*!
* This is a special factory method to create a TIPL (involved people list)
* frame from the given \a properties. Will parse key=[list of values] data
* into the TIPL format as specified in the ID3 standard.
*/
static TextIdentificationFrame *createTIPLFrame(const PropertyMap &properties);
/*!
* This is a special factory method to create a TMCL (musician credits list)
* frame from the given \a properties. Will parse key=[list of values] data
* into the TMCL format as specified in the ID3 standard, where key should be
* of the form instrumentPrefix:instrument.
*/
static TextIdentificationFrame *createTMCLFrame(const PropertyMap &properties);
/*! /*!
* Destroys this TextIdentificationFrame instance. * Destroys this TextIdentificationFrame instance.
*/ */
@@ -173,6 +189,14 @@ namespace TagLib {
*/ */
StringList fieldList() const; StringList fieldList() const;
/*!
* Returns a KeyConversionMap mapping a role as it would be used in a PropertyMap
* to the corresponding key used in a TIPL ID3 frame to describe that role.
*/
static const KeyConversionMap &involvedPeopleMap();
PropertyMap asProperties() const;
protected: protected:
// Reimplementations. // Reimplementations.
@@ -188,6 +212,16 @@ namespace TagLib {
TextIdentificationFrame(const TextIdentificationFrame &); TextIdentificationFrame(const TextIdentificationFrame &);
TextIdentificationFrame &operator=(const TextIdentificationFrame &); TextIdentificationFrame &operator=(const TextIdentificationFrame &);
/*!
* Parses the special structure of a TIPL frame
* Only the whitelisted roles "ARRANGER", "ENGINEER", "PRODUCER",
* "DJMIXER" (ID3: "DJ-MIX") and "MIXER" (ID3: "MIX") are allowed.
*/
PropertyMap makeTIPLProperties() const;
/*!
* Parses the special structure of a TMCL frame.
*/
PropertyMap makeTMCLProperties() const;
class TextIdentificationFramePrivate; class TextIdentificationFramePrivate;
TextIdentificationFramePrivate *d; TextIdentificationFramePrivate *d;
}; };
@@ -218,6 +252,12 @@ namespace TagLib {
*/ */
explicit UserTextIdentificationFrame(const ByteVector &data); explicit UserTextIdentificationFrame(const ByteVector &data);
/*!
* Creates a user defined text identification frame with the given \a description
* and \a values.
*/
UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding = String::UTF8);
virtual String toString() const; virtual String toString() const;
/*! /*!
@@ -236,6 +276,21 @@ namespace TagLib {
void setText(const String &text); void setText(const String &text);
void setText(const StringList &fields); void setText(const StringList &fields);
/*!
* A UserTextIdentificationFrame is parsed into a PropertyMap as follows:
* - the key is the frame's description, uppercased
* - if the description contains '::', only the substring after that
* separator is considered as key (compatibility with exfalso)
* - if the above rules don't yield a valid key (e.g. containing non-ASCII
* characters), the returned map will contain an entry "TXXX/<description>"
* in its unsupportedData() list.
* - The values will be copies of the fieldList().
* - If the description() appears as value in fieldList(), it will be omitted
* in the value list, in order to be compatible with TagLib which copies
* the description() into the fieldList().
*/
PropertyMap asProperties() const;
/*! /*!
* Searches for the user defined text frame with the description \a description * Searches for the user defined text frame with the description \a description
* in \a tag. This returns null if no matching frames were found. * in \a tag. This returns null if no matching frames were found.

View File

@@ -24,8 +24,10 @@
***************************************************************************/ ***************************************************************************/
#include <tbytevectorlist.h> #include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tdebug.h> #include <tdebug.h>
#include "id3v2tag.h"
#include "uniquefileidentifierframe.h" #include "uniquefileidentifierframe.h"
using namespace TagLib; using namespace TagLib;
@@ -87,6 +89,34 @@ String UniqueFileIdentifierFrame::toString() const
return String::null; return String::null;
} }
PropertyMap UniqueFileIdentifierFrame::asProperties() const
{
PropertyMap map;
if(d->owner == "http://musicbrainz.org") {
map.insert("MUSICBRAINZ_TRACKID", String(d->identifier));
}
else {
map.unsupportedData().append(frameID() + String("/") + d->owner);
}
return map;
}
UniqueFileIdentifierFrame *UniqueFileIdentifierFrame::findByOwner(const ID3v2::Tag *tag, const String &o) // static
{
ID3v2::FrameList comments = tag->frameList("UFID");
for(ID3v2::FrameList::ConstIterator it = comments.begin();
it != comments.end();
++it)
{
UniqueFileIdentifierFrame *frame = dynamic_cast<UniqueFileIdentifierFrame *>(*it);
if(frame && frame->owner() == o)
return frame;
}
return 0;
}
void UniqueFileIdentifierFrame::parseFields(const ByteVector &data) void UniqueFileIdentifierFrame::parseFields(const ByteVector &data)
{ {
if(data.size() < 1) { if(data.size() < 1) {

View File

@@ -94,13 +94,23 @@ namespace TagLib {
virtual String toString() const; virtual String toString() const;
PropertyMap asProperties() const;
/*!
* UFID frames each have a unique owner. This searches for a UFID
* frame with the owner \a o and returns a pointer to it.
*
* \see owner()
*/
static UniqueFileIdentifierFrame *findByOwner(const Tag *tag, const String &o);
protected: protected:
virtual void parseFields(const ByteVector &data); virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const; virtual ByteVector renderFields() const;
private: private:
UniqueFileIdentifierFrame(const UniqueFileIdentifierFrame &); UniqueFileIdentifierFrame(const UniqueFileIdentifierFrame &);
UniqueFileIdentifierFrame &operator=(UniqueFileIdentifierFrame &); UniqueFileIdentifierFrame &operator=(const UniqueFileIdentifierFrame &);
UniqueFileIdentifierFrame(const ByteVector &data, Header *h); UniqueFileIdentifierFrame(const ByteVector &data, Header *h);

View File

@@ -27,7 +27,9 @@
#include "unsynchronizedlyricsframe.h" #include "unsynchronizedlyricsframe.h"
#include <tbytevectorlist.h> #include <tbytevectorlist.h>
#include <id3v2tag.h>
#include <tdebug.h> #include <tdebug.h>
#include <tpropertymap.h>
using namespace TagLib; using namespace TagLib;
using namespace ID3v2; using namespace ID3v2;
@@ -111,6 +113,30 @@ void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding)
d->textEncoding = encoding; d->textEncoding = encoding;
} }
PropertyMap UnsynchronizedLyricsFrame::asProperties() const
{
PropertyMap map;
String key = description().upper();
if(key.isEmpty() || key.upper() == "LYRICS")
map.insert("LYRICS", text());
else if(key.isNull())
map.unsupportedData().append(L"USLT/" + description());
else
map.insert("LYRICS:" + key, text());
return map;
}
UnsynchronizedLyricsFrame *UnsynchronizedLyricsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static
{
ID3v2::FrameList lyrics = tag->frameList("USLT");
for(ID3v2::FrameList::ConstIterator it = lyrics.begin(); it != lyrics.end(); ++it){
UnsynchronizedLyricsFrame *frame = dynamic_cast<UnsynchronizedLyricsFrame *>(*it);
if(frame && frame->description() == d)
return frame;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// protected members // protected members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -132,9 +158,14 @@ void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data)
ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
if(l.size() == 2) { if(l.size() == 2) {
if(d->textEncoding == String::Latin1) {
d->description = Tag::latin1StringHandler()->parse(l.front());
d->text = Tag::latin1StringHandler()->parse(l.back());
} else {
d->description = String(l.front(), d->textEncoding); d->description = String(l.front(), d->textEncoding);
d->text = String(l.back(), d->textEncoding); d->text = String(l.back(), d->textEncoding);
} }
}
} }
ByteVector UnsynchronizedLyricsFrame::renderFields() const ByteVector UnsynchronizedLyricsFrame::renderFields() const

View File

@@ -134,6 +134,28 @@ namespace TagLib {
*/ */
void setTextEncoding(String::Type encoding); void setTextEncoding(String::Type encoding);
/*! Parses this frame as PropertyMap with a single key.
* - if description() is empty or "LYRICS", the key will be "LYRICS"
* - if description() is not a valid PropertyMap key, the frame will be
* marked unsupported by an entry "USLT/<description>" in the unsupportedData()
* attribute of the returned map.
* - otherwise, the key will be "LYRICS:<description>"
* - The single value will be the frame's text().
* Note that currently the language() field is not supported by the PropertyMap
* interface.
*/
PropertyMap asProperties() const;
/*!
* LyricsFrames each have a unique description. This searches for a lyrics
* frame with the decription \a d and returns a pointer to it. If no
* frame is found that matches the given description null is returned.
*
* \see description()
*/
static UnsynchronizedLyricsFrame *findByDescription(const Tag *tag, const String &d);
protected: protected:
// Reimplementations. // Reimplementations.

View File

@@ -26,8 +26,10 @@
***************************************************************************/ ***************************************************************************/
#include "urllinkframe.h" #include "urllinkframe.h"
#include "id3v2tag.h"
#include <tdebug.h> #include <tdebug.h>
#include <tstringlist.h> #include <tstringlist.h>
#include <tpropertymap.h>
using namespace TagLib; using namespace TagLib;
using namespace ID3v2; using namespace ID3v2;
@@ -78,6 +80,18 @@ String UrlLinkFrame::toString() const
return url(); return url();
} }
PropertyMap UrlLinkFrame::asProperties() const
{
String key = frameIDToKey(frameID());
PropertyMap map;
if(key.isNull())
// unknown W*** frame - this normally shouldn't happen
map.unsupportedData().append(frameID());
else
map.insert(key, url());
return map;
}
void UrlLinkFrame::parseFields(const ByteVector &data) void UrlLinkFrame::parseFields(const ByteVector &data)
{ {
d->url = String(data); d->url = String(data);
@@ -139,6 +153,30 @@ void UserUrlLinkFrame::setDescription(const String &s)
d->description = s; d->description = s;
} }
PropertyMap UserUrlLinkFrame::asProperties() const
{
PropertyMap map;
String key = description().upper();
if(key.isEmpty() || key.upper() == "URL")
map.insert("URL", url());
else if(key.isNull())
map.unsupportedData().append(L"WXXX/" + description());
else
map.insert("URL:" + key, url());
return map;
}
UserUrlLinkFrame *UserUrlLinkFrame::find(ID3v2::Tag *tag, const String &description) // static
{
FrameList l = tag->frameList("WXXX");
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) {
UserUrlLinkFrame *f = dynamic_cast<UserUrlLinkFrame *>(*it);
if(f && f->description() == description)
return f;
}
return 0;
}
void UserUrlLinkFrame::parseFields(const ByteVector &data) void UserUrlLinkFrame::parseFields(const ByteVector &data)
{ {
if(data.size() < 2) { if(data.size() < 2) {
@@ -175,7 +213,7 @@ ByteVector UserUrlLinkFrame::renderFields() const
{ {
ByteVector v; ByteVector v;
String::Type encoding = checkEncoding(d->description, d->textEncoding); String::Type encoding = checkTextEncoding(d->description, d->textEncoding);
v.append(char(encoding)); v.append(char(encoding));
v.append(d->description.data(encoding)); v.append(d->description.data(encoding));

View File

@@ -68,6 +68,7 @@ namespace TagLib {
virtual void setText(const String &s); virtual void setText(const String &s);
virtual String toString() const; virtual String toString() const;
PropertyMap asProperties() const;
protected: protected:
virtual void parseFields(const ByteVector &data); virtual void parseFields(const ByteVector &data);
@@ -150,6 +151,22 @@ namespace TagLib {
*/ */
void setDescription(const String &s); void setDescription(const String &s);
/*!
* Parses the UserUrlLinkFrame as PropertyMap. The description() is taken as key,
* and the URL as single value.
* - if description() is empty, the key will be "URL".
* - otherwise, if description() is not a valid key (e.g. containing non-ASCII
* characters), the returned map will contain an entry "WXXX/<description>"
* in its unsupportedData() list.
*/
PropertyMap asProperties() const;
/*!
* Searches for the user defined url frame with the description \a description
* in \a tag. This returns null if no matching frames were found.
*/
static UserUrlLinkFrame *find(Tag *tag, const String &description);
protected: protected:
virtual void parseFields(const ByteVector &data); virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const; virtual ByteVector renderFields() const;

View File

@@ -23,7 +23,7 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifndef HAVE_ZLIB #ifdef HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif #endif
@@ -36,8 +36,16 @@
#include <tdebug.h> #include <tdebug.h>
#include <tstringlist.h> #include <tstringlist.h>
#include "id3v2tag.h"
#include "id3v2frame.h" #include "id3v2frame.h"
#include "id3v2synchdata.h" #include "id3v2synchdata.h"
#include "tpropertymap.h"
#include "frames/textidentificationframe.h"
#include "frames/urllinkframe.h"
#include "frames/unsynchronizedlyricsframe.h"
#include "frames/commentsframe.h"
#include "frames/uniquefileidentifierframe.h"
#include "frames/unknownframe.h"
using namespace TagLib; using namespace TagLib;
using namespace ID3v2; using namespace ID3v2;
@@ -65,7 +73,7 @@ namespace
return false; return false;
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) { for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) { if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
return false; return false;
} }
} }
@@ -95,10 +103,62 @@ ByteVector Frame::textDelimiter(String::Type t)
return d; return d;
} }
const String Frame::instrumentPrefix("PERFORMER:");
const String Frame::commentPrefix("COMMENT:");
const String Frame::lyricsPrefix("LYRICS:");
const String Frame::urlPrefix("URL:");
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Frame *Frame::createTextualFrame(const String &key, const StringList &values) //static
{
// check if the key is contained in the key<=>frameID mapping
ByteVector frameID = keyToFrameID(key);
if(!frameID.isNull()) {
if(frameID[0] == 'T'){ // text frame
TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8);
frame->setText(values);
return frame;
} else if((frameID[0] == 'W') && (values.size() == 1)){ // URL frame (not WXXX); support only one value
UrlLinkFrame* frame = new UrlLinkFrame(frameID);
frame->setUrl(values.front());
return frame;
}
}
if(key == "MUSICBRAINZ_TRACKID" && values.size() == 1) {
UniqueFileIdentifierFrame *frame = new UniqueFileIdentifierFrame("http://musicbrainz.org", values.front().data(String::UTF8));
return frame;
}
// now we check if it's one of the "special" cases:
// -LYRICS: depending on the number of values, use USLT or TXXX (with description=LYRICS)
if((key == "LYRICS" || key.startsWith(lyricsPrefix)) && values.size() == 1){
UnsynchronizedLyricsFrame *frame = new UnsynchronizedLyricsFrame(String::UTF8);
frame->setDescription(key == "LYRICS" ? key : key.substr(lyricsPrefix.size()));
frame->setText(values.front());
return frame;
}
// -URL: depending on the number of values, use WXXX or TXXX (with description=URL)
if((key == "URL" || key.startsWith(urlPrefix)) && values.size() == 1){
UserUrlLinkFrame *frame = new UserUrlLinkFrame(String::UTF8);
frame->setDescription(key == "URL" ? key : key.substr(urlPrefix.size()));
frame->setUrl(values.front());
return frame;
}
// -COMMENT: depending on the number of values, use COMM or TXXX (with description=COMMENT)
if((key == "COMMENT" || key.startsWith(commentPrefix)) && values.size() == 1){
CommentsFrame *frame = new CommentsFrame(String::UTF8);
if (key != "COMMENT"){
frame->setDescription(key.substr(commentPrefix.size()));
}
frame->setText(values.front());
return frame;
}
// if non of the above cases apply, we use a TXXX frame with the key as description
return new UserTextIdentificationFrame(keyToTXXX(key), values, String::UTF8);
}
Frame::~Frame() Frame::~Frame()
{ {
delete d; delete d;
@@ -221,7 +281,11 @@ String Frame::readStringField(const ByteVector &data, String::Type encoding, int
if(end < *position) if(end < *position)
return String::null; return String::null;
String str = String(data.mid(*position, end - *position), encoding); String str;
if(encoding == String::Latin1)
str = Tag::latin1StringHandler()->parse(data.mid(*position, end - *position));
else
str = String(data.mid(*position, end - *position), encoding);
*position = end + delimiter.size(); *position = end + delimiter.size();
@@ -230,19 +294,241 @@ String Frame::readStringField(const ByteVector &data, String::Type encoding, int
String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding) // static String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding) // static
{ {
return checkEncoding(fields, encoding, 4);
}
String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding, uint version) // static
{
if((encoding == String::UTF8 || encoding == String::UTF16BE) && version != 4)
return String::UTF16;
if(encoding != String::Latin1) if(encoding != String::Latin1)
return encoding; return encoding;
for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) { for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
if(!(*it).isLatin1()) { if(!(*it).isLatin1()) {
if(version == 4) {
debug("Frame::checkEncoding() -- Rendering using UTF8."); debug("Frame::checkEncoding() -- Rendering using UTF8.");
return String::UTF8; return String::UTF8;
} }
else {
debug("Frame::checkEncoding() -- Rendering using UTF16.");
return String::UTF16;
}
}
} }
return String::Latin1; return String::Latin1;
} }
String::Type Frame::checkTextEncoding(const StringList &fields, String::Type encoding) const
{
return checkEncoding(fields, encoding, header()->version());
}
static const TagLib::uint frameTranslationSize = 51;
static const char *frameTranslation[][2] = {
// Text information frames
{ "TALB", "ALBUM"},
{ "TBPM", "BPM" },
{ "TCOM", "COMPOSER" },
{ "TCON", "GENRE" },
{ "TCOP", "COPYRIGHT" },
{ "TDEN", "ENCODINGTIME" },
{ "TDLY", "PLAYLISTDELAY" },
{ "TDOR", "ORIGINALDATE" },
{ "TDRC", "DATE" },
// { "TRDA", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
// { "TDAT", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
// { "TYER", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
// { "TIME", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
{ "TDRL", "RELEASEDATE" },
{ "TDTG", "TAGGINGDATE" },
{ "TENC", "ENCODEDBY" },
{ "TEXT", "LYRICIST" },
{ "TFLT", "FILETYPE" },
//{ "TIPL", "INVOLVEDPEOPLE" }, handled separately
{ "TIT1", "CONTENTGROUP" },
{ "TIT2", "TITLE"},
{ "TIT3", "SUBTITLE" },
{ "TKEY", "INITIALKEY" },
{ "TLAN", "LANGUAGE" },
{ "TLEN", "LENGTH" },
//{ "TMCL", "MUSICIANCREDITS" }, handled separately
{ "TMED", "MEDIA" },
{ "TMOO", "MOOD" },
{ "TOAL", "ORIGINALALBUM" },
{ "TOFN", "ORIGINALFILENAME" },
{ "TOLY", "ORIGINALLYRICIST" },
{ "TOPE", "ORIGINALARTIST" },
{ "TOWN", "OWNER" },
{ "TPE1", "ARTIST"},
{ "TPE2", "ALBUMARTIST" }, // id3's spec says 'PERFORMER', but most programs use 'ALBUMARTIST'
{ "TPE3", "CONDUCTOR" },
{ "TPE4", "REMIXER" }, // could also be ARRANGER
{ "TPOS", "DISCNUMBER" },
{ "TPRO", "PRODUCEDNOTICE" },
{ "TPUB", "LABEL" },
{ "TRCK", "TRACKNUMBER" },
{ "TRSN", "RADIOSTATION" },
{ "TRSO", "RADIOSTATIONOWNER" },
{ "TSOA", "ALBUMSORT" },
{ "TSOP", "ARTISTSORT" },
{ "TSOT", "TITLESORT" },
{ "TSO2", "ALBUMARTISTSORT" }, // non-standard, used by iTunes
{ "TSRC", "ISRC" },
{ "TSSE", "ENCODING" },
// URL frames
{ "WCOP", "COPYRIGHTURL" },
{ "WOAF", "FILEWEBPAGE" },
{ "WOAR", "ARTISTWEBPAGE" },
{ "WOAS", "AUDIOSOURCEWEBPAGE" },
{ "WORS", "RADIOSTATIONWEBPAGE" },
{ "WPAY", "PAYMENTWEBPAGE" },
{ "WPUB", "PUBLISHERWEBPAGE" },
//{ "WXXX", "URL"}, handled specially
// Other frames
{ "COMM", "COMMENT" },
//{ "USLT", "LYRICS" }, handled specially
};
static const TagLib::uint txxxFrameTranslationSize = 8;
static const char *txxxFrameTranslation[][2] = {
{ "MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
{ "MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
{ "Acoustid Id", "ACOUSTID_ID" },
{ "Acoustid Fingerprint", "ACOUSTID_FINGERPRINT" },
{ "MusicIP PUID", "MUSICIP_PUID" },
};
Map<ByteVector, String> &idMap()
{
static Map<ByteVector, String> m;
if(m.isEmpty())
for(size_t i = 0; i < frameTranslationSize; ++i)
m[frameTranslation[i][0]] = frameTranslation[i][1];
return m;
}
Map<String, String> &txxxMap()
{
static Map<String, String> m;
if(m.isEmpty()) {
for(size_t i = 0; i < txxxFrameTranslationSize; ++i) {
String key = String(txxxFrameTranslation[i][0]).upper();
m[key] = txxxFrameTranslation[i][1];
}
}
return m;
}
// list of deprecated frames and their successors
static const TagLib::uint deprecatedFramesSize = 4;
static const char *deprecatedFrames[][2] = {
{"TRDA", "TDRC"}, // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3)
{"TDAT", "TDRC"}, // 2.3 -> 2.4
{"TYER", "TDRC"}, // 2.3 -> 2.4
{"TIME", "TDRC"}, // 2.3 -> 2.4
};
Map<ByteVector,ByteVector> &deprecationMap()
{
static Map<ByteVector,ByteVector> depMap;
if(depMap.isEmpty())
for(TagLib::uint i = 0; i < deprecatedFramesSize; ++i)
depMap[deprecatedFrames[i][0]] = deprecatedFrames[i][1];
return depMap;
}
String Frame::frameIDToKey(const ByteVector &id)
{
Map<ByteVector, String> &m = idMap();
if(m.contains(id))
return m[id];
if(deprecationMap().contains(id))
return m[deprecationMap()[id]];
return String::null;
}
ByteVector Frame::keyToFrameID(const String &s)
{
static Map<String, ByteVector> m;
if(m.isEmpty())
for(size_t i = 0; i < frameTranslationSize; ++i)
m[frameTranslation[i][1]] = frameTranslation[i][0];
if(m.contains(s.upper()))
return m[s];
return ByteVector::null;
}
String Frame::txxxToKey(const String &description)
{
Map<String, String> &m = txxxMap();
String d = description.upper();
if(m.contains(d))
return m[d];
return d;
}
String Frame::keyToTXXX(const String &s)
{
static Map<String, String> m;
if(m.isEmpty())
for(size_t i = 0; i < txxxFrameTranslationSize; ++i)
m[txxxFrameTranslation[i][1]] = txxxFrameTranslation[i][0];
if(m.contains(s.upper()))
return m[s];
return s;
}
PropertyMap Frame::asProperties() const
{
if(dynamic_cast< const UnknownFrame *>(this)) {
PropertyMap m;
m.unsupportedData().append("UNKNOWN/" + frameID());
return m;
}
const ByteVector &id = frameID();
// workaround until this function is virtual
if(id == "TXXX")
return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties();
else if(id[0] == 'T')
return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties();
else if(id == "WXXX")
return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties();
else if(id[0] == 'W')
return dynamic_cast< const UrlLinkFrame* >(this)->asProperties();
else if(id == "COMM")
return dynamic_cast< const CommentsFrame* >(this)->asProperties();
else if(id == "USLT")
return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties();
else if(id == "UFID")
return dynamic_cast< const UniqueFileIdentifierFrame* >(this)->asProperties();
PropertyMap m;
m.unsupportedData().append(id);
return m;
}
void Frame::splitProperties(const PropertyMap &original, PropertyMap &singleFrameProperties,
PropertyMap &tiplProperties, PropertyMap &tmclProperties)
{
singleFrameProperties.clear();
tiplProperties.clear();
tmclProperties.clear();
for(PropertyMap::ConstIterator it = original.begin(); it != original.end(); ++it) {
if(TextIdentificationFrame::involvedPeopleMap().contains(it->first))
tiplProperties.insert(it->first, it->second);
else if(it->first.startsWith(TextIdentificationFrame::instrumentPrefix))
tmclProperties.insert(it->first, it->second);
else
singleFrameProperties.insert(it->first, it->second);
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Frame::Header class // Frame::Header class
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -356,7 +642,7 @@ void Frame::Header::setData(const ByteVector &data, uint version)
return; return;
} }
d->frameSize = data.mid(3, 3).toUInt(); d->frameSize = data.toUInt(3, 3, true);
break; break;
} }
@@ -384,7 +670,7 @@ void Frame::Header::setData(const ByteVector &data, uint version)
// Set the size -- the frame size is the four bytes starting at byte four in // Set the size -- the frame size is the four bytes starting at byte four in
// the frame header (structure 4) // the frame header (structure 4)
d->frameSize = data.mid(4, 4).toUInt(); d->frameSize = data.toUInt(4U);
{ // read the first byte of flags { // read the first byte of flags
std::bitset<8> flags(data[8]); std::bitset<8> flags(data[8]);
@@ -431,7 +717,7 @@ void Frame::Header::setData(const ByteVector &data, uint version)
// iTunes writes v2.4 tags with v2.3-like frame sizes // iTunes writes v2.4 tags with v2.3-like frame sizes
if(d->frameSize > 127) { if(d->frameSize > 127) {
if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) { if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) {
unsigned int uintSize = data.mid(4, 4).toUInt(); unsigned int uintSize = data.toUInt(4U);
if(isValidFrameID(data.mid(uintSize + 10, 4))) { if(isValidFrameID(data.mid(uintSize + 10, 4))) {
d->frameSize = uintSize; d->frameSize = uintSize;
} }
@@ -484,6 +770,11 @@ TagLib::uint Frame::Header::version() const
return d->version; return d->version;
} }
void Frame::Header::setVersion(TagLib::uint version)
{
d->version = version;
}
bool Frame::Header::tagAlterPreservation() const bool Frame::Header::tagAlterPreservation() const
{ {
return d->tagAlterPreservation; return d->tagAlterPreservation;
@@ -538,7 +829,11 @@ ByteVector Frame::Header::render() const
{ {
ByteVector flags(2, char(0)); // just blank for the moment ByteVector flags(2, char(0)); // just blank for the moment
ByteVector v = d->frameID + SynchData::fromUInt(d->frameSize) + flags; ByteVector v = d->frameID +
(d->version == 3
? ByteVector::fromUInt(d->frameSize)
: SynchData::fromUInt(d->frameSize)) +
flags;
return v; return v;
} }

View File

@@ -33,6 +33,7 @@
namespace TagLib { namespace TagLib {
class StringList; class StringList;
class PropertyMap;
namespace ID3v2 { namespace ID3v2 {
@@ -56,6 +57,14 @@ namespace TagLib {
friend class FrameFactory; friend class FrameFactory;
public: public:
/*!
* Creates a textual frame which corresponds to a single key in the PropertyMap
* interface. These are all (User)TextIdentificationFrames except TIPL and TMCL,
* all (User)URLLinkFrames, CommentsFrames, and UnsynchronizedLyricsFrame.
*/
static Frame *createTextualFrame(const String &key, const StringList &values);
/*! /*!
* Destroys this Frame instance. * Destroys this Frame instance.
*/ */
@@ -126,6 +135,28 @@ namespace TagLib {
*/ */
static ByteVector textDelimiter(String::Type t); static ByteVector textDelimiter(String::Type t);
/*!
* The string with which an instrument name is prefixed to build a key in a PropertyMap;
* used to translate PropertyMaps to TMCL frames. In the current implementation, this
* is "PERFORMER:".
*/
static const String instrumentPrefix;
/*!
* The PropertyMap key prefix which triggers the use of a COMM frame instead of a TXXX
* frame for a non-standard key. In the current implementation, this is "COMMENT:".
*/
static const String commentPrefix;
/*!
* The PropertyMap key prefix which triggers the use of a USLT frame instead of a TXXX
* frame for a non-standard key. In the current implementation, this is "LYRICS:".
*/
static const String lyricsPrefix;
/*!
* The PropertyMap key prefix which triggers the use of a WXXX frame instead of a TXX
* frame for a non-standard key. In the current implementation, this is "URL:".
*/
static const String urlPrefix;
protected: protected:
class Header; class Header;
@@ -199,9 +230,76 @@ namespace TagLib {
* Checks a the list of string values to see if they can be used with the * Checks a the list of string values to see if they can be used with the
* specified encoding and returns the recommended encoding. * specified encoding and returns the recommended encoding.
*/ */
// BIC: remove and make non-static
static String::Type checkEncoding(const StringList &fields, static String::Type checkEncoding(const StringList &fields,
String::Type encoding); String::Type encoding);
/*!
* Checks a the list of string values to see if they can be used with the
* specified encoding and returns the recommended encoding. This method
* also checks the ID3v2 version and makes sure the encoding can be used
* in the specified version.
*/
// BIC: remove and make non-static
static String::Type checkEncoding(const StringList &fields,
String::Type encoding, uint version);
/*!
* Checks a the list of string values to see if they can be used with the
* specified encoding and returns the recommended encoding. This method
* also checks the ID3v2 version and makes sure the encoding can be used
* in the version specified by the frame's header.
*/
String::Type checkTextEncoding(const StringList &fields,
String::Type encoding) const;
/*!
* Parses the contents of this frame as PropertyMap. If that fails, the returend
* PropertyMap will be empty, and its unsupportedData() will contain this frame's
* ID.
* BIC: Will be a virtual function in future releases.
*/
PropertyMap asProperties() const;
/*!
* Returns an appropriate ID3 frame ID for the given free-form tag key. This method
* will return ByteVector::null if no specialized translation is found.
*/
static ByteVector keyToFrameID(const String &);
/*!
* Returns a free-form tag name for the given ID3 frame ID. Note that this does not work
* for general frame IDs such as TXXX or WXXX; in such a case String::null is returned.
*/
static String frameIDToKey(const ByteVector &);
/*!
* Returns an appropriate TXXX frame description for the given free-form tag key.
*/
static String keyToTXXX(const String &);
/*!
* Returns a free-form tag name for the given ID3 frame description.
*/
static String txxxToKey(const String &);
/*!
* This helper function splits the PropertyMap \a original into three ProperytMaps
* \a singleFrameProperties, \a tiplProperties, and \a tmclProperties, such that:
* - \a singleFrameProperties contains only of keys which can be represented with
* exactly one ID3 frame per key. In the current implementation
* this is everything except for the fixed "involved people" keys and keys of the
* form "TextIdentificationFrame::instrumentPrefix" + "instrument", which are
* mapped to a TMCL frame.
* - \a tiplProperties will consist of those keys that are present in
* TextIdentificationFrame::involvedPeopleMap()
* - \a tmclProperties contains the "musician credits" keys which should be mapped
* to a TMCL frame
*/
static void splitProperties(const PropertyMap &original, PropertyMap &singleFrameProperties,
PropertyMap &tiplProperties, PropertyMap &tmclProperties);
private: private:
Frame(const Frame &); Frame(const Frame &);
Frame &operator=(const Frame &); Frame &operator=(const Frame &);
@@ -294,11 +392,17 @@ namespace TagLib {
void setFrameSize(uint size); void setFrameSize(uint size);
/*! /*!
* Returns the ID3v2 version of the header (as passed in from the * Returns the ID3v2 version of the header, as passed in from the
* construction of the header). * construction of the header or set via setVersion().
*/ */
uint version() const; uint version() const;
/*!
* Sets the ID3v2 version of the header, changing has impact on the
* correct parsing/rendering of frame data.
*/
void setVersion(uint version);
/*! /*!
* Returns the size of the frame header in bytes. * Returns the size of the frame header in bytes.
* *

View File

@@ -23,7 +23,7 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifndef HAVE_ZLIB #ifdef HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif #endif
@@ -44,6 +44,7 @@
#include "frames/unsynchronizedlyricsframe.h" #include "frames/unsynchronizedlyricsframe.h"
#include "frames/popularimeterframe.h" #include "frames/popularimeterframe.h"
#include "frames/privateframe.h" #include "frames/privateframe.h"
#include "frames/ownershipframe.h"
using namespace TagLib; using namespace TagLib;
using namespace ID3v2; using namespace ID3v2;
@@ -65,7 +66,7 @@ public:
} }
}; };
FrameFactory *FrameFactory::factory = 0; FrameFactory FrameFactory::factory;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
@@ -73,7 +74,6 @@ FrameFactory *FrameFactory::factory = 0;
FrameFactory *FrameFactory::instance() FrameFactory *FrameFactory::instance()
{ {
static FrameFactory factory;
return &factory; return &factory;
} }
@@ -99,7 +99,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
// A quick sanity check -- make sure that the frameID is 4 uppercase Latin1 // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
// characters. Also make sure that there is data in the frame. // characters. Also make sure that there is data in the frame.
if(!frameID.size() == (version < 3 ? 3 : 4) || if(frameID.size() != (version < 3 ? 3 : 4) ||
header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) || header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) ||
header->frameSize() > data.size()) header->frameSize() > data.size())
{ {
@@ -107,8 +107,19 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
return 0; return 0;
} }
#ifndef NO_ITUNES_HACKS
if(version == 3 && frameID.size() == 4 && frameID[3] == '\0') {
// iTunes v2.3 tags store v2.2 frames - convert now
frameID = frameID.mid(0, 3);
header->setFrameID(frameID);
header->setVersion(2);
updateFrame(header);
header->setVersion(3);
}
#endif
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) { for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) { if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
delete header; delete header;
return 0; return 0;
} }
@@ -125,7 +136,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
// TagLib doesn't mess with encrypted frames, so just treat them // TagLib doesn't mess with encrypted frames, so just treat them
// as unknown frames. // as unknown frames.
#if HAVE_ZLIB == 0 #if !defined(HAVE_ZLIB) || HAVE_ZLIB == 0
if(header->compression()) { if(header->compression()) {
debug("Compressed frames are currently not supported."); debug("Compressed frames are currently not supported.");
return new UnknownFrame(data, header); return new UnknownFrame(data, header);
@@ -240,6 +251,14 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
if(frameID == "PRIV") if(frameID == "PRIV")
return new PrivateFrame(data, header); return new PrivateFrame(data, header);
// Ownership (frames 4.22)
if(frameID == "OWNE") {
OwnershipFrame *f = new OwnershipFrame(data, header);
d->setTextEncoding(f);
return f;
}
return new UnknownFrame(data, header); return new UnknownFrame(data, header);
} }
@@ -309,6 +328,7 @@ bool FrameFactory::updateFrame(Frame::Header *header) const
convertFrame("TBP", "TBPM", header); convertFrame("TBP", "TBPM", header);
convertFrame("TCM", "TCOM", header); convertFrame("TCM", "TCOM", header);
convertFrame("TCO", "TCON", header); convertFrame("TCO", "TCON", header);
convertFrame("TCP", "TCMP", header);
convertFrame("TCR", "TCOP", header); convertFrame("TCR", "TCOP", header);
convertFrame("TDY", "TDLY", header); convertFrame("TDY", "TDLY", header);
convertFrame("TEN", "TENC", header); convertFrame("TEN", "TENC", header);
@@ -331,7 +351,12 @@ bool FrameFactory::updateFrame(Frame::Header *header) const
convertFrame("TRC", "TSRC", header); convertFrame("TRC", "TSRC", header);
convertFrame("TRD", "TDRC", header); convertFrame("TRD", "TDRC", header);
convertFrame("TRK", "TRCK", header); convertFrame("TRK", "TRCK", header);
convertFrame("TS2", "TSO2", header);
convertFrame("TSA", "TSOA", header);
convertFrame("TSC", "TSOC", header);
convertFrame("TSP", "TSOP", header);
convertFrame("TSS", "TSSE", header); convertFrame("TSS", "TSSE", header);
convertFrame("TST", "TSOT", header);
convertFrame("TT1", "TIT1", header); convertFrame("TT1", "TIT1", header);
convertFrame("TT2", "TIT2", header); convertFrame("TT2", "TIT2", header);
convertFrame("TT3", "TIT3", header); convertFrame("TT3", "TIT3", header);
@@ -367,6 +392,7 @@ bool FrameFactory::updateFrame(Frame::Header *header) const
convertFrame("TORY", "TDOR", header); convertFrame("TORY", "TDOR", header);
convertFrame("TYER", "TDRC", header); convertFrame("TYER", "TDRC", header);
convertFrame("IPLS", "TIPL", header);
break; break;
} }

View File

@@ -123,8 +123,7 @@ namespace TagLib {
FrameFactory(); FrameFactory();
/*! /*!
* Destroys the frame factory. In most cases this will never be called (as * Destroys the frame factory.
* is typical of singletons).
*/ */
virtual ~FrameFactory(); virtual ~FrameFactory();
@@ -155,7 +154,7 @@ namespace TagLib {
void updateGenre(TextIdentificationFrame *frame) const; void updateGenre(TextIdentificationFrame *frame) const;
static FrameFactory *factory; static FrameFactory factory;
class FrameFactoryPrivate; class FrameFactoryPrivate;
FrameFactoryPrivate *d; FrameFactoryPrivate *d;

View File

@@ -164,7 +164,7 @@ ByteVector Header::render() const
// add the version number -- we always render a 2.4.0 tag regardless of what // add the version number -- we always render a 2.4.0 tag regardless of what
// the tag originally was. // the tag originally was.
v.append(char(4)); v.append(char(majorVersion()));
v.append(char(0)); v.append(char(0));
// Currently we don't actually support writing extended headers, footers or // Currently we don't actually support writing extended headers, footers or

View File

@@ -49,7 +49,14 @@ TagLib::uint SynchData::toUInt(const ByteVector &data)
// Invalid data; assume this was created by some buggy software that just // Invalid data; assume this was created by some buggy software that just
// put normal integers here rather than syncsafe ones, and try it that // put normal integers here rather than syncsafe ones, and try it that
// way. // way.
sum = (data.size() > 4) ? data.mid(0, 4).toUInt() : data.toUInt(); if(data.size() >= 4) {
sum = data.toUInt(0, true);
}
else {
ByteVector tmp(data);
tmp.resize(4);
sum = tmp.toUInt(0, true);
}
} }
return sum; return sum;

View File

@@ -23,19 +23,28 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tfile.h> #include <tfile.h>
#include <tdebug.h>
#include "id3v2tag.h" #include "id3v2tag.h"
#include "id3v2header.h" #include "id3v2header.h"
#include "id3v2extendedheader.h" #include "id3v2extendedheader.h"
#include "id3v2footer.h" #include "id3v2footer.h"
#include "id3v2synchdata.h" #include "id3v2synchdata.h"
#include "tbytevector.h"
#include "id3v1genres.h" #include "id3v1genres.h"
#include "tpropertymap.h"
#include <tdebug.h>
#include "frames/textidentificationframe.h" #include "frames/textidentificationframe.h"
#include "frames/commentsframe.h" #include "frames/commentsframe.h"
#include "frames/urllinkframe.h"
#include "frames/uniquefileidentifierframe.h"
#include "frames/unsynchronizedlyricsframe.h"
#include "frames/unknownframe.h"
using namespace TagLib; using namespace TagLib;
using namespace ID3v2; using namespace ID3v2;
@@ -65,8 +74,30 @@ public:
FrameListMap frameListMap; FrameListMap frameListMap;
FrameList frameList; FrameList frameList;
static const Latin1StringHandler *stringHandler;
}; };
static const Latin1StringHandler defaultStringHandler;
const ID3v2::Latin1StringHandler *ID3v2::Tag::TagPrivate::stringHandler = &defaultStringHandler;
////////////////////////////////////////////////////////////////////////////////
// StringHandler implementation
////////////////////////////////////////////////////////////////////////////////
Latin1StringHandler::Latin1StringHandler()
{
}
Latin1StringHandler::~Latin1StringHandler()
{
}
String Latin1StringHandler::parse(const ByteVector &data) const
{
return String(data, String::Latin1);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -329,7 +360,201 @@ void ID3v2::Tag::removeFrames(const ByteVector &id)
removeFrame(*it, true); removeFrame(*it, true);
} }
PropertyMap ID3v2::Tag::properties() const
{
PropertyMap properties;
for(FrameList::ConstIterator it = frameList().begin(); it != frameList().end(); ++it) {
PropertyMap props = (*it)->asProperties();
properties.merge(props);
}
return properties;
}
void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties)
{
for(StringList::ConstIterator it = properties.begin(); it != properties.end(); ++it){
if(it->startsWith("UNKNOWN/")) {
String frameID = it->substr(String("UNKNOWN/").size());
if(frameID.size() != 4)
continue; // invalid specification
ByteVector id = frameID.data(String::Latin1);
// delete all unknown frames of given type
FrameList l = frameList(id);
for(FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++)
if (dynamic_cast<const UnknownFrame *>(*fit) != 0)
removeFrame(*fit);
}
else if(it->size() == 4){
ByteVector id = it->data(String::Latin1);
removeFrames(id);
}
else {
ByteVector id = it->substr(0,4).data(String::Latin1);
if(it->size() <= 5)
continue; // invalid specification
String description = it->substr(5);
Frame *frame = 0;
if(id == "TXXX")
frame = UserTextIdentificationFrame::find(this, description);
else if(id == "WXXX")
frame = UserUrlLinkFrame::find(this, description);
else if(id == "COMM")
frame = CommentsFrame::findByDescription(this, description);
else if(id == "USLT")
frame = UnsynchronizedLyricsFrame::findByDescription(this, description);
else if(id == "UFID")
frame = UniqueFileIdentifierFrame::findByOwner(this, description);
if(frame)
removeFrame(frame);
}
}
}
PropertyMap ID3v2::Tag::setProperties(const PropertyMap &origProps)
{
FrameList framesToDelete;
// we split up the PropertyMap into the "normal" keys and the "complicated" ones,
// which are those according to TIPL or TMCL frames.
PropertyMap properties;
PropertyMap tiplProperties;
PropertyMap tmclProperties;
Frame::splitProperties(origProps, properties, tiplProperties, tmclProperties);
for(FrameListMap::ConstIterator it = frameListMap().begin(); it != frameListMap().end(); ++it){
for(FrameList::ConstIterator lit = it->second.begin(); lit != it->second.end(); ++lit){
PropertyMap frameProperties = (*lit)->asProperties();
if(it->first == "TIPL") {
if (tiplProperties != frameProperties)
framesToDelete.append(*lit);
else
tiplProperties.erase(frameProperties);
} else if(it->first == "TMCL") {
if (tmclProperties != frameProperties)
framesToDelete.append(*lit);
else
tmclProperties.erase(frameProperties);
} else if(!properties.contains(frameProperties))
framesToDelete.append(*lit);
else
properties.erase(frameProperties);
}
}
for(FrameList::ConstIterator it = framesToDelete.begin(); it != framesToDelete.end(); ++it)
removeFrame(*it);
// now create remaining frames:
// start with the involved people list (TIPL)
if(!tiplProperties.isEmpty())
addFrame(TextIdentificationFrame::createTIPLFrame(tiplProperties));
// proceed with the musician credit list (TMCL)
if(!tmclProperties.isEmpty())
addFrame(TextIdentificationFrame::createTMCLFrame(tmclProperties));
// now create the "one key per frame" frames
for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it)
addFrame(Frame::createTextualFrame(it->first, it->second));
return PropertyMap(); // ID3 implements the complete PropertyMap interface, so an empty map is returned
}
ByteVector ID3v2::Tag::render() const ByteVector ID3v2::Tag::render() const
{
return render(4);
}
void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const
{
const char *unsupportedFrames[] = {
"ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG",
"TMOO", "TPRO", "TSOA", "TSOT", "TSST", "TSOP", 0
};
ID3v2::TextIdentificationFrame *frameTDOR = 0;
ID3v2::TextIdentificationFrame *frameTDRC = 0;
ID3v2::TextIdentificationFrame *frameTIPL = 0;
ID3v2::TextIdentificationFrame *frameTMCL = 0;
for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++) {
ID3v2::Frame *frame = *it;
ByteVector frameID = frame->header()->frameID();
for(int i = 0; unsupportedFrames[i]; i++) {
if(frameID == unsupportedFrames[i]) {
debug("A frame that is not supported in ID3v2.3 \'"
+ String(frameID) + "\' has been discarded");
frame = 0;
break;
}
}
if(frame && frameID == "TDOR") {
frameTDOR = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
frame = 0;
}
if(frame && frameID == "TDRC") {
frameTDRC = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
frame = 0;
}
if(frame && frameID == "TIPL") {
frameTIPL = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
frame = 0;
}
if(frame && frameID == "TMCL") {
frameTMCL = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
frame = 0;
}
if(frame) {
frames->append(frame);
}
}
if(frameTDOR) {
String content = frameTDOR->toString();
if(content.size() >= 4) {
ID3v2::TextIdentificationFrame *frameTORY = new ID3v2::TextIdentificationFrame("TORY", String::Latin1);
frameTORY->setText(content.substr(0, 4));
frames->append(frameTORY);
newFrames->append(frameTORY);
}
}
if(frameTDRC) {
String content = frameTDRC->toString();
if(content.size() >= 4) {
ID3v2::TextIdentificationFrame *frameTYER = new ID3v2::TextIdentificationFrame("TYER", String::Latin1);
frameTYER->setText(content.substr(0, 4));
frames->append(frameTYER);
newFrames->append(frameTYER);
if(content.size() >= 10 && content[4] == '-' && content[7] == '-') {
ID3v2::TextIdentificationFrame *frameTDAT = new ID3v2::TextIdentificationFrame("TDAT", String::Latin1);
frameTDAT->setText(content.substr(8, 2) + content.substr(5, 2));
frames->append(frameTDAT);
newFrames->append(frameTDAT);
if(content.size() >= 16 && content[10] == 'T' && content[13] == ':') {
ID3v2::TextIdentificationFrame *frameTIME = new ID3v2::TextIdentificationFrame("TIME", String::Latin1);
frameTIME->setText(content.substr(11, 2) + content.substr(14, 2));
frames->append(frameTIME);
newFrames->append(frameTIME);
}
}
}
}
if(frameTIPL || frameTMCL) {
ID3v2::TextIdentificationFrame *frameIPLS = new ID3v2::TextIdentificationFrame("IPLS", String::Latin1);
StringList people;
if(frameTMCL) {
StringList v24People = frameTMCL->fieldList();
for(uint i = 0; i + 1 < v24People.size(); i += 2) {
people.append(v24People[i]);
people.append(v24People[i+1]);
}
}
if(frameTIPL) {
StringList v24People = frameTIPL->fieldList();
for(uint i = 0; i + 1 < v24People.size(); i += 2) {
people.append(v24People[i]);
people.append(v24People[i+1]);
}
}
frameIPLS->setText(people);
frames->append(frameIPLS);
newFrames->append(frameIPLS);
}
}
ByteVector ID3v2::Tag::render(int version) const
{ {
// We need to render the "tag data" first so that we have to correct size to // We need to render the "tag data" first so that we have to correct size to
// render in the tag's header. The "tag data" -- everything that is included // render in the tag's header. The "tag data" -- everything that is included
@@ -338,11 +563,28 @@ ByteVector ID3v2::Tag::render() const
ByteVector tagData; ByteVector tagData;
if(version != 3 && version != 4) {
debug("Unknown ID3v2 version, using ID3v2.4");
version = 4;
}
// TODO: Render the extended header. // TODO: Render the extended header.
// Loop through the frames rendering them and adding them to the tagData. // Loop through the frames rendering them and adding them to the tagData.
for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++) { FrameList newFrames;
newFrames.setAutoDelete(true);
FrameList frameList;
if(version == 4) {
frameList = d->frameList;
}
else {
downgradeFrames(&frameList, &newFrames);
}
for(FrameList::Iterator it = frameList.begin(); it != frameList.end(); it++) {
(*it)->header()->setVersion(version);
if((*it)->header()->frameID().size() != 4) { if((*it)->header()->frameID().size() != 4) {
debug("A frame of unsupported or unknown type \'" debug("A frame of unsupported or unknown type \'"
+ String((*it)->header()->frameID()) + "\' has been discarded"); + String((*it)->header()->frameID()) + "\' has been discarded");
@@ -364,13 +606,27 @@ ByteVector ID3v2::Tag::render() const
tagData.append(ByteVector(paddingSize, char(0))); tagData.append(ByteVector(paddingSize, char(0)));
// Set the tag size. // Set the version and data size.
d->header.setMajorVersion(version);
d->header.setTagSize(tagData.size()); d->header.setTagSize(tagData.size());
// TODO: This should eventually include d->footer->render(). // TODO: This should eventually include d->footer->render().
return d->header.render() + tagData; return d->header.render() + tagData;
} }
Latin1StringHandler const *ID3v2::Tag::latin1StringHandler()
{
return TagPrivate::stringHandler;
}
void ID3v2::Tag::setLatin1StringHandler(const Latin1StringHandler *handler)
{
if(handler)
TagPrivate::stringHandler = handler;
else
TagPrivate::stringHandler = &defaultStringHandler;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// protected members // protected members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -432,8 +688,9 @@ void ID3v2::Tag::parse(const ByteVector &origData)
// portion of the frame data. // portion of the frame data.
if(data.at(frameDataPosition) == 0) { if(data.at(frameDataPosition) == 0) {
if(d->header.footerPresent()) if(d->header.footerPresent()) {
debug("Padding *and* a footer found. This is not allowed by the spec."); debug("Padding *and* a footer found. This is not allowed by the spec.");
}
d->paddingSize = frameDataLength - frameDataPosition; d->paddingSize = frameDataLength - frameDataPosition;
return; return;

View File

@@ -57,6 +57,36 @@ namespace TagLib {
typedef List<Frame *> FrameList; typedef List<Frame *> FrameList;
typedef Map<ByteVector, FrameList> FrameListMap; typedef Map<ByteVector, FrameList> FrameListMap;
//! An abstraction for the ISO-8859-1 string to data encoding in ID3v2 tags.
/*!
* ID3v2 tag can store strings in ISO-8859-1 (Latin1), and TagLib only
* supports genuine ISO-8859-1 by default. However, in practice, non
* ISO-8859-1 encodings are often used instead of ISO-8859-1, such as
* Windows-1252 for western languages, Shift_JIS for Japanese and so on.
*
* Here is an option to read such tags by subclassing this class,
* reimplementing parse() and setting your reimplementation as the default
* with ID3v2::Tag::setStringHandler().
*
* \note Writing non-ISO-8859-1 tags is not implemented intentionally.
* Use UTF-16 or UTF-8 instead.
*
* \see ID3v2::Tag::setStringHandler()
*/
class TAGLIB_EXPORT Latin1StringHandler
{
public:
Latin1StringHandler();
virtual ~Latin1StringHandler();
/*!
* Decode a string from \a data. The default implementation assumes that
* \a data is an ISO-8859-1 (Latin1) character array.
*/
virtual String parse(const ByteVector &data) const;
};
//! The main class in the ID3v2 implementation //! The main class in the ID3v2 implementation
/*! /*!
@@ -260,11 +290,91 @@ namespace TagLib {
*/ */
void removeFrames(const ByteVector &id); void removeFrames(const ByteVector &id);
/*!
* Implements the unified property interface -- export function.
* This function does some work to translate the hard-specified ID3v2
* frame types into a free-form string-to-stringlist PropertyMap:
* - if ID3v2 frame ID is known by Frame::frameIDToKey(), the returned
* key is used
* - if the frame ID is "TXXX" (user text frame), the description() is
* used as key
* - if the frame ID is "WXXX" (user url frame),
* - if the description is empty or "URL", the key "URL" is used
* - otherwise, the key "URL:<description>" is used;
* - if the frame ID is "COMM" (comments frame),
* - if the description is empty or "COMMENT", the key "COMMENT"
* is used
* - otherwise, the key "COMMENT:<description>" is used;
* - if the frame ID is "USLT" (unsynchronized lyrics),
* - if the description is empty or "LYRICS", the key "LYRICS" is used
* - otherwise, the key "LYRICS:<description>" is used;
* - if the frame ID is "TIPL" (involved peoples list), and if all the
* roles defined in the frame are known in TextIdentificationFrame::involvedPeopleMap(),
* then "<role>=<name>" will be contained in the returned obejct for each
* - if the frame ID is "TMCL" (musician credit list), then
* "PERFORMER:<instrument>=<name>" will be contained in the returned
* PropertyMap for each defined musician
* In any other case, the unsupportedData() of the returned object will contain
* the frame's ID and, in case of a frame ID which is allowed to appear more than
* once, the description, separated by a "/".
*
*/
PropertyMap properties() const;
/*!
* Removes unsupported frames given by \a properties. The elements of
* \a properties must be taken from properties().unsupportedData(); they
* are of one of the following forms:
* - a four-character frame ID, if the ID3 specification allows only one
* frame with that ID (thus, the frame is uniquely determined)
* - frameID + "/" + description(), when the ID is one of "TXXX", "WXXX",
* "COMM", or "USLT",
* - "UNKNOWN/" + frameID, for frames that could not be parsed by TagLib.
* In that case, *all* unknown frames with the given ID will be removed.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* See the comments in properties().
*/
PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Render the tag back to binary data, suitable to be written to disk. * Render the tag back to binary data, suitable to be written to disk.
*/ */
ByteVector render() const; ByteVector render() const;
/*!
* Render the tag back to binary data, suitable to be written to disk.
*
* The \a version parameter specifies the version of the rendered
* ID3v2 tag. It can be either 4 or 3.
*/
// BIC: combine with the above method
ByteVector render(int version) const;
/*!
* Gets the current string handler that decides how the "Latin-1" data
* will be converted to and from binary data.
*
* \see Latin1StringHandler
*/
static Latin1StringHandler const *latin1StringHandler();
/*!
* Sets the string handler that decides how the "Latin-1" data will be
* converted to and from binary data.
* If the parameter \a handler is null, the previous handler is
* released and default ISO-8859-1 handler is restored.
*
* \note The caller is responsible for deleting the previous handler
* as needed after it is released.
*
* \see Latin1StringHandler
*/
static void setLatin1StringHandler(const Latin1StringHandler *handler);
protected: protected:
/*! /*!
* Reads data from the file specified in the constructor. It does basic * Reads data from the file specified in the constructor. It does basic
@@ -286,6 +396,8 @@ namespace TagLib {
*/ */
void setTextFrame(const ByteVector &id, const String &value); void setTextFrame(const ByteVector &id, const String &value);
void downgradeFrames(FrameList *existingFrames, FrameList *newFrames) const;
private: private:
Tag(const Tag &); Tag(const Tag &);
Tag &operator=(const Tag &); Tag &operator=(const Tag &);

View File

@@ -35,6 +35,7 @@
#include "mpegfile.h" #include "mpegfile.h"
#include "mpegheader.h" #include "mpegheader.h"
#include "tpropertymap.h"
using namespace TagLib; using namespace TagLib;
@@ -113,6 +114,16 @@ MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
MPEG::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle propertiesStyle) :
TagLib::File(stream)
{
d = new FilePrivate(frameFactory);
if(isOpen())
read(readProperties, propertiesStyle);
}
MPEG::File::~File() MPEG::File::~File()
{ {
delete d; delete d;
@@ -123,6 +134,37 @@ TagLib::Tag *MPEG::File::tag() const
return &d->tag; return &d->tag;
} }
PropertyMap MPEG::File::properties() const
{
// once Tag::properties() is virtual, this case distinction could actually be done
// within TagUnion.
if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
if(d->hasAPE)
return d->tag.access<APE::Tag>(APEIndex, false)->properties();
if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->properties();
return PropertyMap();
}
void MPEG::File::removeUnsupportedProperties(const StringList &properties)
{
if(d->hasID3v2)
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(properties);
else if(d->hasAPE)
d->tag.access<APE::Tag>(APEIndex, false)->removeUnsupportedProperties(properties);
else if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(properties);
}
PropertyMap MPEG::File::setProperties(const PropertyMap &properties)
{
if(d->hasID3v1)
// update ID3v1 tag if it exists, but ignore the return value
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
}
MPEG::Properties *MPEG::File::audioProperties() const MPEG::Properties *MPEG::File::audioProperties() const
{ {
return d->properties; return d->properties;
@@ -139,6 +181,16 @@ bool MPEG::File::save(int tags)
} }
bool MPEG::File::save(int tags, bool stripOthers) bool MPEG::File::save(int tags, bool stripOthers)
{
return save(tags, stripOthers, 4);
}
bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version)
{
return save(tags, stripOthers, id3v2Version, true);
}
bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags)
{ {
if(tags == NoTags && stripOthers) if(tags == NoTags && stripOthers)
return strip(AllTags); return strip(AllTags);
@@ -156,14 +208,19 @@ bool MPEG::File::save(int tags, bool stripOthers)
return false; return false;
} }
// Create the tags if we've been asked to. Copy the values from the tag that // Create the tags if we've been asked to.
// does exist into the new tag.
if((tags & ID3v2) && ID3v1Tag()) if (duplicateTags) {
// Copy the values from the tag that does exist into the new tag,
// except if the existing tag is to be stripped.
if((tags & ID3v2) && ID3v1Tag() && !(stripOthers && !(tags & ID3v1)))
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false); Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
if((tags & ID3v1) && d->tag[ID3v2Index]) if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2)))
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false); Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
}
bool success = true; bool success = true;
@@ -174,7 +231,7 @@ bool MPEG::File::save(int tags, bool stripOthers)
if(!d->hasID3v2) if(!d->hasID3v2)
d->ID3v2Location = 0; d->ID3v2Location = 0;
insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize); insert(ID3v2Tag()->render(id3v2Version), d->ID3v2Location, d->ID3v2OriginalSize);
d->hasID3v2 = true; d->hasID3v2 = true;
@@ -224,8 +281,9 @@ bool MPEG::File::save(int tags, bool stripOthers)
else { else {
seek(0, End); seek(0, End);
d->APELocation = tell(); d->APELocation = tell();
APE::Tag *apeTag = d->tag.access<APE::Tag>(APEIndex, false);
d->APEFooterLocation = d->APELocation d->APEFooterLocation = d->APELocation
+ d->tag.access<APE::Tag>(APEIndex, false)->footer()->completeTagSize() + apeTag->footer()->completeTagSize()
- APE::Footer::size(); - APE::Footer::size();
writeBlock(APETag()->render()); writeBlock(APETag()->render());
d->APEOriginalSize = APETag()->footer()->completeTagSize(); d->APEOriginalSize = APETag()->footer()->completeTagSize();
@@ -386,6 +444,21 @@ long MPEG::File::lastFrameOffset()
return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length()); return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length());
} }
bool MPEG::File::hasID3v1Tag() const
{
return d->hasID3v1;
}
bool MPEG::File::hasID3v2Tag() const
{
return d->hasID3v2;
}
bool MPEG::File::hasAPETag() const
{
return d->hasAPE;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -583,9 +656,6 @@ void MPEG::File::findAPE()
bool MPEG::File::secondSynchByte(char byte) bool MPEG::File::secondSynchByte(char byte)
{ {
if(uchar(byte) == 0xff)
return false;
std::bitset<8> b(byte); std::bitset<8> b(byte);
// check to see if the byte matches 111xxxxx // check to see if the byte matches 111xxxxx

View File

@@ -28,6 +28,7 @@
#include "taglib_export.h" #include "taglib_export.h"
#include "tfile.h" #include "tfile.h"
#include "tag.h"
#include "mpegproperties.h" #include "mpegproperties.h"
@@ -70,9 +71,10 @@ namespace TagLib {
}; };
/*! /*!
* Contructs an MPEG file from \a file. If \a readProperties is true the * Constructs an MPEG file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If * file's audio properties will also be read.
* false, \a propertiesStyle is ignored. *
* \note In the current implementation, \a propertiesStyle is ignored.
* *
* \deprecated This constructor will be dropped in favor of the one below * \deprecated This constructor will be dropped in favor of the one below
* in a future version. * in a future version.
@@ -81,16 +83,35 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Contructs an MPEG file from \a file. If \a readProperties is true the * Constructs an MPEG file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If * file's audio properties will also be read.
* false, \a propertiesStyle is ignored. The frames will be created using *
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory. * \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/ */
// BIC: merge with the above constructor // BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory, File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an MPEG file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
*/ */
@@ -115,6 +136,26 @@ namespace TagLib {
*/ */
virtual Tag *tag() const; virtual Tag *tag() const;
/*!
* Implements the reading part of the unified property interface.
* If the file contains more than one tag, only the
* first one (in the order ID3v2, APE, ID3v1) will be converted to the
* PropertyMap.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the writing part of the unified tag dictionary interface.
* In order to avoid problems with deprecated tag formats, this method
* always creates an ID3v2 tag if necessary.
* If an ID3v1 tag exists, it will be updated as well, within the
* limitations of that format.
* The returned PropertyMap refers to the ID3v2 tag only.
*/
PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the MPEG::Properties for this file. If no audio properties * Returns the MPEG::Properties for this file. If no audio properties
* were read then this will return a null pointer. * were read then this will return a null pointer.
@@ -161,42 +202,93 @@ namespace TagLib {
// BIC: combine with the above method // BIC: combine with the above method
bool save(int tags, bool stripOthers); bool save(int tags, bool stripOthers);
/*!
* Save the file. This will attempt to save all of the tag types that are
* specified by OR-ing together TagTypes values. The save() method above
* uses AllTags. This returns true if saving was successful.
*
* If \a stripOthers is true this strips all tags not included in the mask,
* but does not modify them in memory, so later calls to save() which make
* use of these tags will remain valid. This also strips empty tags.
*
* The \a id3v2Version parameter specifies the version of the saved
* ID3v2 tag. It can be either 4 or 3.
*/
// BIC: combine with the above method
bool save(int tags, bool stripOthers, int id3v2Version);
/*!
* Save the file. This will attempt to save all of the tag types that are
* specified by OR-ing together TagTypes values. The save() method above
* uses AllTags. This returns true if saving was successful.
*
* If \a stripOthers is true this strips all tags not included in the mask,
* but does not modify them in memory, so later calls to save() which make
* use of these tags will remain valid. This also strips empty tags.
*
* The \a id3v2Version parameter specifies the version of the saved
* ID3v2 tag. It can be either 4 or 3.
*
* If \a duplicateTags is true and at least one tag -- ID3v1 or ID3v2 --
* exists this will duplicate its content into the other tag.
*/
// BIC: combine with the above method
bool save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags);
/*! /*!
* Returns a pointer to the ID3v2 tag of the file. * Returns a pointer to the ID3v2 tag of the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this may return a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create * if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist. * an ID3v2 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* on disk actually has an ID3v2 tag.
* *
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be * \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasID3v2Tag()
*/ */
ID3v2::Tag *ID3v2Tag(bool create = false); ID3v2::Tag *ID3v2Tag(bool create = false);
/*! /*!
* Returns a pointer to the ID3v1 tag of the file. * Returns a pointer to the ID3v1 tag of the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this may return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create * if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist. * an ID3v1 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
* *
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be * \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasID3v1Tag()
*/ */
ID3v1::Tag *ID3v1Tag(bool create = false); ID3v1::Tag *ID3v1Tag(bool create = false);
/*! /*!
* Returns a pointer to the APE tag of the file. * Returns a pointer to the APE tag of the file.
* *
* If \a create is false (the default) this will return a null pointer * If \a create is false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is true it will create * if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist. * an APE tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
* *
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be * \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is * deleted by the user. It will be deleted when the file (object) is
* destroyed. * destroyed.
*
* \see hasAPETag()
*/ */
APE::Tag *APETag(bool create = false); APE::Tag *APETag(bool create = false);
@@ -252,6 +344,27 @@ namespace TagLib {
*/ */
long lastFrameOffset(); long lastFrameOffset();
/*!
* Returns whether or not the file on disk actually has an ID3v1 tag.
*
* \see ID3v1Tag()
*/
bool hasID3v1Tag() const;
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
*
* \see ID3v2Tag()
*/
bool hasID3v2Tag() const;
/*!
* Returns whether or not the file on disk actually has an APE tag.
*
* \see APETag()
*/
bool hasAPETag() const;
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@@ -28,6 +28,7 @@
#include <tbytevector.h> #include <tbytevector.h>
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include "trefcounter.h"
#include "mpegheader.h" #include "mpegheader.h"

View File

@@ -221,7 +221,7 @@ void MPEG::Properties::read()
double length = timePerFrame * d->xingHeader->totalFrames(); double length = timePerFrame * d->xingHeader->totalFrames();
d->length = int(length); d->length = int(length);
d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 / length / 1000 : 0; d->bitrate = d->length > 0 ? (int)(d->xingHeader->totalSize() * 8 / length / 1000) : 0;
} }
else { else {
// Since there was no valid Xing header found, we hope that we're in a constant // Since there was no valid Xing header found, we hope that we're in a constant

View File

@@ -108,8 +108,8 @@ void MPEG::XingHeader::parse(const ByteVector &data)
return; return;
} }
d->frames = data.mid(8, 4).toUInt(); d->frames = data.toUInt(8U);
d->size = data.mid(12, 4).toUInt(); d->size = data.toUInt(12U);
d->valid = true; d->valid = true;
} }

View File

@@ -26,6 +26,7 @@
#include <tbytevector.h> #include <tbytevector.h>
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include <tpropertymap.h>
#include <xiphcomment.h> #include <xiphcomment.h>
#include "oggflacfile.h" #include "oggflacfile.h"
@@ -72,6 +73,15 @@ Ogg::FLAC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file) Properties::ReadStyle propertiesStyle) : Ogg::File(file)
{ {
d = new FilePrivate; d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
Ogg::FLAC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
@@ -85,6 +95,16 @@ Ogg::XiphComment *Ogg::FLAC::File::tag() const
return d->comment; return d->comment;
} }
PropertyMap Ogg::FLAC::File::properties() const
{
return d->comment->properties();
}
PropertyMap Ogg::FLAC::File::setProperties(const PropertyMap &properties)
{
return d->comment->setProperties(properties);
}
Properties *Ogg::FLAC::File::audioProperties() const Properties *Ogg::FLAC::File::audioProperties() const
{ {
return d->properties; return d->properties;
@@ -117,6 +137,11 @@ bool Ogg::FLAC::File::save()
return Ogg::File::save(); return Ogg::File::save();
} }
bool Ogg::FLAC::File::hasXiphComment() const
{
return d->hasXiphComment;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -221,7 +246,7 @@ void Ogg::FLAC::File::scan()
char blockType = header[0] & 0x7f; char blockType = header[0] & 0x7f;
bool lastBlock = (header[0] & 0x80) != 0; bool lastBlock = (header[0] & 0x80) != 0;
uint length = header.mid(1, 3).toUInt(); uint length = header.toUInt(1, 3, true);
overhead += length; overhead += length;
// Sanity: First block should be the stream_info metadata // Sanity: First block should be the stream_info metadata
@@ -231,7 +256,7 @@ void Ogg::FLAC::File::scan()
return; return;
} }
d->streamInfoData = metadataHeader.mid(4,length); d->streamInfoData = metadataHeader.mid(4, length);
// Search through the remaining metadata // Search through the remaining metadata
@@ -244,7 +269,7 @@ void Ogg::FLAC::File::scan()
header = metadataHeader.mid(0, 4); header = metadataHeader.mid(0, 4);
blockType = header[0] & 0x7f; blockType = header[0] & 0x7f;
lastBlock = (header[0] & 0x80) != 0; lastBlock = (header[0] & 0x80) != 0;
length = header.mid(1, 3).toUInt(); length = header.toUInt(1, 3, true);
overhead += length; overhead += length;
if(blockType == 1) { if(blockType == 1) {
@@ -256,9 +281,9 @@ void Ogg::FLAC::File::scan()
d->hasXiphComment = true; d->hasXiphComment = true;
d->commentPacket = ipacket; d->commentPacket = ipacket;
} }
else if(blockType > 5) else if(blockType > 5) {
debug("Ogg::FLAC::File::scan() -- Unknown metadata block"); debug("Ogg::FLAC::File::scan() -- Unknown metadata block");
}
} }
// End of metadata, now comes the datastream // End of metadata, now comes the datastream

View File

@@ -64,13 +64,26 @@ namespace TagLib {
{ {
public: public:
/*! /*!
* Contructs an Ogg/FLAC file from \a file. If \a readProperties is true * Constructs an Ogg/FLAC file from \a file. If \a readProperties is true
* the file's audio properties will also be read using \a propertiesStyle. * the file's audio properties will also be read.
* If false, \a propertiesStyle is ignored. *
* \note In the current implementation, \a propertiesStyle is ignored.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an Ogg/FLAC file from \a stream. If \a readProperties is true
* the file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
*/ */
@@ -78,6 +91,16 @@ namespace TagLib {
/*! /*!
* Returns the Tag for this file. This will always be a XiphComment. * Returns the Tag for this file. This will always be a XiphComment.
*
* \note This always returns a valid pointer regardless of whether or not
* the file on disk has a XiphComment. Use hasXiphComment() to check if
* the file on disk actually has a XiphComment.
*
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasXiphComment()
*/ */
virtual XiphComment *tag() const; virtual XiphComment *tag() const;
@@ -87,6 +110,20 @@ namespace TagLib {
*/ */
virtual Properties *audioProperties() const; virtual Properties *audioProperties() const;
/*!
* Implements the unified property interface -- export function.
* This forwards directly to XiphComment::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified tag dictionary interface -- import function.
* Like properties(), this is a forwarder to the file's XiphComment.
*/
PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Save the file. This will primarily save and update the XiphComment. * Save the file. This will primarily save and update the XiphComment.
* Returns true if the save is successful. * Returns true if the save is successful.
@@ -99,6 +136,13 @@ namespace TagLib {
*/ */
long streamLength(); long streamLength();
/*!
* Returns whether or not the file on disk actually has a XiphComment.
*
* \see tag()
*/
bool hasXiphComment() const;
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@@ -213,6 +213,11 @@ Ogg::File::File(FileName file) : TagLib::File(file)
d = new FilePrivate; d = new FilePrivate;
} }
Ogg::File::File(IOStream *stream) : TagLib::File(stream)
{
d = new FilePrivate;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -82,9 +82,7 @@ namespace TagLib {
protected: protected:
/*! /*!
* Contructs an Ogg file from \a file. If \a readProperties is true the * Constructs an Ogg file from \a file.
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* *
* \note This constructor is protected since Ogg::File shouldn't be * \note This constructor is protected since Ogg::File shouldn't be
* instantiated directly but rather should be used through the codec * instantiated directly but rather should be used through the codec
@@ -92,6 +90,18 @@ namespace TagLib {
*/ */
File(FileName file); File(FileName file);
/*!
* Constructs an Ogg file from \a stream.
*
* \note This constructor is protected since Ogg::File shouldn't be
* instantiated directly but rather should be used through the codec
* specific subclasses.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@@ -255,9 +255,9 @@ void Ogg::PageHeader::read()
d->firstPageOfStream = flags.test(1); d->firstPageOfStream = flags.test(1);
d->lastPageOfStream = flags.test(2); d->lastPageOfStream = flags.test(2);
d->absoluteGranularPosition = data.mid(6, 8).toLongLong(false); d->absoluteGranularPosition = data.toLongLong(6, false);
d->streamSerialNumber = data.mid(14, 4).toUInt(false); d->streamSerialNumber = data.toUInt(14, false);
d->pageSequenceNumber = data.mid(18, 4).toUInt(false); d->pageSequenceNumber = data.toUInt(18, false);
// Byte number 27 is the number of page segments, which is the only variable // Byte number 27 is the number of page segments, which is the only variable
// length portion of the page header. After reading the number of page // length portion of the page header. After reading the number of page

View File

@@ -31,6 +31,7 @@
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include <tpropertymap.h>
#include "speexfile.h" #include "speexfile.h"
@@ -62,6 +63,15 @@ Speex::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file) Properties::ReadStyle propertiesStyle) : Ogg::File(file)
{ {
d = new FilePrivate; d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
Speex::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
@@ -75,6 +85,16 @@ Ogg::XiphComment *Speex::File::tag() const
return d->comment; return d->comment;
} }
PropertyMap Speex::File::properties() const
{
return d->comment->properties();
}
PropertyMap Speex::File::setProperties(const PropertyMap &properties)
{
return d->comment->setProperties(properties);
}
Speex::Properties *Speex::File::audioProperties() const Speex::Properties *Speex::File::audioProperties() const
{ {
return d->properties; return d->properties;

View File

@@ -56,13 +56,26 @@ namespace TagLib {
{ {
public: public:
/*! /*!
* Contructs a Speex file from \a file. If \a readProperties is true the * Constructs a Speex file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If * file's audio properties will also be read.
* false, \a propertiesStyle is ignored. *
* \note In the current implementation, \a propertiesStyle is ignored.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs a Speex file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
*/ */
@@ -75,12 +88,26 @@ namespace TagLib {
*/ */
virtual Ogg::XiphComment *tag() const; virtual Ogg::XiphComment *tag() const;
/*!
* Implements the unified property interface -- export function.
* This forwards directly to XiphComment::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified tag dictionary interface -- import function.
* Like properties(), this is a forwarder to the file's XiphComment.
*/
PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the Speex::Properties for this file. If no audio properties * Returns the Speex::Properties for this file. If no audio properties
* were read then this will return a null pointer. * were read then this will return a null pointer.
*/ */
virtual Properties *audioProperties() const; virtual Properties *audioProperties() const;
virtual bool save(); virtual bool save();
private: private:

View File

@@ -113,32 +113,32 @@ void Speex::Properties::read()
ByteVector data = d->file->packet(0); ByteVector data = d->file->packet(0);
int pos = 28; uint pos = 28;
// speex_version_id; /**< Version for Speex (for checking compatibility) */ // speex_version_id; /**< Version for Speex (for checking compatibility) */
d->speexVersion = data.mid(pos, 4).toUInt(false); d->speexVersion = data.toUInt(pos, false);
pos += 4; pos += 4;
// header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */ // header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */
pos += 4; pos += 4;
// rate; /**< Sampling rate used */ // rate; /**< Sampling rate used */
d->sampleRate = data.mid(pos, 4).toUInt(false); d->sampleRate = data.toUInt(pos, false);
pos += 4; pos += 4;
// mode; /**< Mode used (0 for narrowband, 1 for wideband) */ // mode; /**< Mode used (0 for narrowband, 1 for wideband) */
d->mode = data.mid(pos, 4).toUInt(false); d->mode = data.toUInt(pos, false);
pos += 4; pos += 4;
// mode_bitstream_version; /**< Version ID of the bit-stream */ // mode_bitstream_version; /**< Version ID of the bit-stream */
pos += 4; pos += 4;
// nb_channels; /**< Number of channels encoded */ // nb_channels; /**< Number of channels encoded */
d->channels = data.mid(pos, 4).toUInt(false); d->channels = data.toUInt(pos, false);
pos += 4; pos += 4;
// bitrate; /**< Bit-rate used */ // bitrate; /**< Bit-rate used */
d->bitrate = data.mid(pos, 4).toUInt(false); d->bitrate = data.toUInt(pos, false);
pos += 4; pos += 4;
// frame_size; /**< Size of frames */ // frame_size; /**< Size of frames */
@@ -146,7 +146,7 @@ void Speex::Properties::read()
pos += 4; pos += 4;
// vbr; /**< 1 for a VBR encoding, 0 otherwise */ // vbr; /**< 1 for a VBR encoding, 0 otherwise */
d->vbr = data.mid(pos, 4).toUInt(false) == 1; d->vbr = data.toUInt(pos, false) == 1;
pos += 4; pos += 4;
// frames_per_packet; /**< Number of frames stored per Ogg packet */ // frames_per_packet; /**< Number of frames stored per Ogg packet */

View File

@@ -27,9 +27,11 @@
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include <tpropertymap.h>
#include "vorbisfile.h" #include "vorbisfile.h"
using namespace TagLib; using namespace TagLib;
class Vorbis::File::FilePrivate class Vorbis::File::FilePrivate
@@ -65,6 +67,15 @@ Vorbis::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file) Properties::ReadStyle propertiesStyle) : Ogg::File(file)
{ {
d = new FilePrivate; d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
Vorbis::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
@@ -78,6 +89,16 @@ Ogg::XiphComment *Vorbis::File::tag() const
return d->comment; return d->comment;
} }
PropertyMap Vorbis::File::properties() const
{
return d->comment->properties();
}
PropertyMap Vorbis::File::setProperties(const PropertyMap &properties)
{
return d->comment->setProperties(properties);
}
Vorbis::Properties *Vorbis::File::audioProperties() const Vorbis::Properties *Vorbis::File::audioProperties() const
{ {
return d->properties; return d->properties;

View File

@@ -63,13 +63,26 @@ namespace TagLib {
{ {
public: public:
/*! /*!
* Contructs a Vorbis file from \a file. If \a readProperties is true the * Constructs a Vorbis file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If * file's audio properties will also be read.
* false, \a propertiesStyle is ignored. *
* \note In the current implementation, \a propertiesStyle is ignored.
*/ */
File(FileName file, bool readProperties = true, File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average); Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs a Vorbis file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*! /*!
* Destroys this instance of the File. * Destroys this instance of the File.
*/ */
@@ -82,6 +95,19 @@ namespace TagLib {
*/ */
virtual Ogg::XiphComment *tag() const; virtual Ogg::XiphComment *tag() const;
/*!
* Implements the unified property interface -- export function.
* This forwards directly to XiphComment::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified tag dictionary interface -- import function.
* Like properties(), this is a forwarder to the file's XiphComment.
*/
PropertyMap setProperties(const PropertyMap &);
/*! /*!
* Returns the Vorbis::Properties for this file. If no audio properties * Returns the Vorbis::Properties for this file. If no audio properties
* were read then this will return a null pointer. * were read then this will return a null pointer.

View File

@@ -133,7 +133,7 @@ void Vorbis::Properties::read()
ByteVector data = d->file->packet(0); ByteVector data = d->file->packet(0);
int pos = 0; uint pos = 0;
if(data.mid(pos, 7) != vorbisSetupHeaderID) { if(data.mid(pos, 7) != vorbisSetupHeaderID) {
debug("Vorbis::Properties::read() -- invalid Vorbis identification header"); debug("Vorbis::Properties::read() -- invalid Vorbis identification header");
@@ -142,22 +142,22 @@ void Vorbis::Properties::read()
pos += 7; pos += 7;
d->vorbisVersion = data.mid(pos, 4).toUInt(false); d->vorbisVersion = data.toUInt(pos, false);
pos += 4; pos += 4;
d->channels = uchar(data[pos]); d->channels = uchar(data[pos]);
pos += 1; pos += 1;
d->sampleRate = data.mid(pos, 4).toUInt(false); d->sampleRate = data.toUInt(pos, false);
pos += 4; pos += 4;
d->bitrateMaximum = data.mid(pos, 4).toUInt(false); d->bitrateMaximum = data.toUInt(pos, false);
pos += 4; pos += 4;
d->bitrateNominal = data.mid(pos, 4).toUInt(false); d->bitrateNominal = data.toUInt(pos, false);
pos += 4; pos += 4;
d->bitrateMinimum = data.mid(pos, 4).toUInt(false); d->bitrateMinimum = data.toUInt(pos, false);
// TODO: Later this should be only the "fast" mode. // TODO: Later this should be only the "fast" mode.
d->bitrate = d->bitrateNominal; d->bitrate = d->bitrateNominal;
@@ -173,7 +173,7 @@ void Vorbis::Properties::read()
long long end = last->absoluteGranularPosition(); long long end = last->absoluteGranularPosition();
if(start >= 0 && end >= 0 && d->sampleRate > 0) if(start >= 0 && end >= 0 && d->sampleRate > 0)
d->length = (end - start) / (long long) d->sampleRate; d->length = (int)((end - start) / (long long) d->sampleRate);
else else
debug("Vorbis::Properties::read() -- Either the PCM values for the start or " debug("Vorbis::Properties::read() -- Either the PCM values for the start or "
"end of this file was incorrect or the sample rate is zero."); "end of this file was incorrect or the sample rate is zero.");

View File

@@ -27,6 +27,7 @@
#include <tdebug.h> #include <tdebug.h>
#include <xiphcomment.h> #include <xiphcomment.h>
#include <tpropertymap.h>
using namespace TagLib; using namespace TagLib;
@@ -188,6 +189,58 @@ const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const
return d->fieldListMap; return d->fieldListMap;
} }
PropertyMap Ogg::XiphComment::properties() const
{
return d->fieldListMap;
}
PropertyMap Ogg::XiphComment::setProperties(const PropertyMap &properties)
{
// check which keys are to be deleted
StringList toRemove;
for(FieldListMap::ConstIterator it = d->fieldListMap.begin(); it != d->fieldListMap.end(); ++it)
if (!properties.contains(it->first))
toRemove.append(it->first);
for(StringList::ConstIterator it = toRemove.begin(); it != toRemove.end(); ++it)
removeField(*it);
// now go through keys in \a properties and check that the values match those in the xiph comment
PropertyMap invalid;
PropertyMap::ConstIterator it = properties.begin();
for(; it != properties.end(); ++it)
{
if(!checkKey(it->first))
invalid.insert(it->first, it->second);
else if(!d->fieldListMap.contains(it->first) || !(it->second == d->fieldListMap[it->first])) {
const StringList &sl = it->second;
if(sl.size() == 0)
// zero size string list -> remove the tag with all values
removeField(it->first);
else {
// replace all strings in the list for the tag
StringList::ConstIterator valueIterator = sl.begin();
addField(it->first, *valueIterator, true);
++valueIterator;
for(; valueIterator != sl.end(); ++valueIterator)
addField(it->first, *valueIterator, false);
}
}
}
return invalid;
}
bool Ogg::XiphComment::checkKey(const String &key)
{
if(key.size() < 1)
return false;
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
// forbid non-printable, non-ascii, '=' (#61) and '~' (#126)
if (*it < 32 || *it >= 128 || *it == 61 || *it == 126)
return false;
return true;
}
String Ogg::XiphComment::vendorID() const String Ogg::XiphComment::vendorID() const
{ {
return d->vendorID; return d->vendorID;
@@ -285,9 +338,9 @@ void Ogg::XiphComment::parse(const ByteVector &data)
// The first thing in the comment data is the vendor ID length, followed by a // The first thing in the comment data is the vendor ID length, followed by a
// UTF8 string with the vendor ID. // UTF8 string with the vendor ID.
int pos = 0; uint pos = 0;
uint vendorLength = data.mid(0, 4).toUInt(false); const uint vendorLength = data.toUInt(0, false);
pos += 4; pos += 4;
d->vendorID = String(data.mid(pos, vendorLength), String::UTF8); d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
@@ -295,7 +348,7 @@ void Ogg::XiphComment::parse(const ByteVector &data)
// Next the number of fields in the comment vector. // Next the number of fields in the comment vector.
uint commentFields = data.mid(pos, 4).toUInt(false); const uint commentFields = data.toUInt(pos, false);
pos += 4; pos += 4;
if(commentFields > (data.size() - 8) / 4) { if(commentFields > (data.size() - 8) / 4) {
@@ -307,7 +360,7 @@ void Ogg::XiphComment::parse(const ByteVector &data)
// Each comment field is in the format "KEY=value" in a UTF8 string and has // Each comment field is in the format "KEY=value" in a UTF8 string and has
// 4 bytes before the text starts that gives the length. // 4 bytes before the text starts that gives the length.
uint commentLength = data.mid(pos, 4).toUInt(false); const uint commentLength = data.toUInt(pos, false);
pos += 4; pos += 4;
String comment = String(data.mid(pos, commentLength), String::UTF8); String comment = String(data.mid(pos, commentLength), String::UTF8);

View File

@@ -140,6 +140,29 @@ namespace TagLib {
*/ */
const FieldListMap &fieldListMap() const; const FieldListMap &fieldListMap() const;
/*!
* Implements the unified property interface -- export function.
* The result is a one-to-one match of the Xiph comment, since it is
* completely compatible with the property interface (in fact, a Xiph
* comment is nothing more than a map from tag names to list of values,
* as is the dict interface).
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* The tags from the given map will be stored one-to-one in the file,
* except for invalid keys (less than one character, non-ASCII, or
* containing '=' or '~') in which case the according values will
* be contained in the returned PropertyMap.
*/
PropertyMap setProperties(const PropertyMap&);
/*!
* Check if the given String is a valid Xiph comment key.
*/
static bool checkKey(const String&);
/*! /*!
* Returns the vendor ID of the Ogg Vorbis encoder. libvorbis 1.0 as the * Returns the vendor ID of the Ogg Vorbis encoder. libvorbis 1.0 as the
* most common case always returns "Xiph.Org libVorbis I 20020717". * most common case always returns "Xiph.Org libVorbis I 20020717".

View File

@@ -1,117 +0,0 @@
/***************************************************************************
copyright : (C) 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include "aifffile.h"
using namespace TagLib;
class RIFF::AIFF::File::FilePrivate
{
public:
FilePrivate() :
properties(0),
tag(0),
tagChunkID("ID3 ")
{
}
~FilePrivate()
{
delete properties;
delete tag;
}
Properties *properties;
ID3v2::Tag *tag;
ByteVector tagChunkID;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
RIFF::AIFF::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : RIFF::File(file, BigEndian)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
RIFF::AIFF::File::~File()
{
delete d;
}
ID3v2::Tag *RIFF::AIFF::File::tag() const
{
return d->tag;
}
RIFF::AIFF::Properties *RIFF::AIFF::File::audioProperties() const
{
return d->properties;
}
bool RIFF::AIFF::File::save()
{
if(readOnly()) {
debug("RIFF::AIFF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("RIFF::AIFF::File::save() -- Trying to save invalid file.");
return false;
}
setChunkData(d->tagChunkID, d->tag->render());
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void RIFF::AIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
for(uint i = 0; i < chunkCount(); i++) {
if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") {
d->tagChunkID = chunkName(i);
d->tag = new ID3v2::Tag(this, chunkOffset(i));
}
else if(chunkName(i) == "COMM" && readProperties)
d->properties = new Properties(chunkData(i), propertiesStyle);
}
if(!d->tag)
d->tag = new ID3v2::Tag;
}

View File

@@ -1,102 +0,0 @@
/***************************************************************************
copyright : (C) 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_AIFFFILE_H
#define TAGLIB_AIFFFILE_H
#include "rifffile.h"
#include "id3v2tag.h"
#include "aiffproperties.h"
namespace TagLib {
namespace RIFF {
//! An implementation of AIFF metadata
/*!
* This is implementation of AIFF metadata.
*
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
* chunk as well as properties from the file.
*/
namespace AIFF {
//! An implementation of TagLib::File with AIFF specific methods
/*!
* This implements and provides an interface for AIFF files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to AIFF files.
*/
class TAGLIB_EXPORT File : public TagLib::RIFF::File
{
public:
/*!
* Contructs an AIFF 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,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the Tag for this file.
*/
virtual ID3v2::Tag *tag() const;
/*!
* Returns the AIFF::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
/*!
* Saves the file.
*/
virtual bool save();
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
class FilePrivate;
FilePrivate *d;
};
}
}
}
#endif

View File

@@ -1,153 +0,0 @@
/***************************************************************************
copyright : (C) 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstring.h>
#include <tdebug.h>
#include <cmath>
// ldexp is a c99 function, which might not be defined in <cmath>
// so we pull in math.h too and hope it does the right (wrong) thing
// wrt. c99 functions in C++
#include <math.h>
#include "aiffproperties.h"
////////////////////////////////////////////////////////////////////////////////
// nasty 80-bit float helpers
////////////////////////////////////////////////////////////////////////////////
#define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0)
static double ConvertFromIeeeExtended(unsigned char *bytes)
{
double f;
int expon;
unsigned long hiMant, loMant;
expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) |
((unsigned long)(bytes[3] & 0xFF) << 16) |
((unsigned long)(bytes[4] & 0xFF) << 8) |
((unsigned long)(bytes[5] & 0xFF));
loMant = ((unsigned long)(bytes[6] & 0xFF) << 24) |
((unsigned long)(bytes[7] & 0xFF) << 16) |
((unsigned long)(bytes[8] & 0xFF) << 8) |
((unsigned long)(bytes[9] & 0xFF));
if (expon == 0 && hiMant == 0 && loMant == 0)
f = 0;
else {
if(expon == 0x7FFF) /* Infinity or NaN */
f = HUGE_VAL;
else {
expon -= 16383;
f = ldexp(UnsignedToFloat(hiMant), expon -= 31);
f += ldexp(UnsignedToFloat(loMant), expon -= 32);
}
}
if(bytes[0] & 0x80)
return -f;
else
return f;
}
using namespace TagLib;
class RIFF::AIFF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
sampleWidth(0)
{
}
int length;
int bitrate;
int sampleRate;
int channels;
int sampleWidth;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
RIFF::AIFF::Properties::Properties(const ByteVector &data, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate;
read(data);
}
RIFF::AIFF::Properties::~Properties()
{
delete d;
}
int RIFF::AIFF::Properties::length() const
{
return d->length;
}
int RIFF::AIFF::Properties::bitrate() const
{
return d->bitrate;
}
int RIFF::AIFF::Properties::sampleRate() const
{
return d->sampleRate;
}
int RIFF::AIFF::Properties::channels() const
{
return d->channels;
}
int RIFF::AIFF::Properties::sampleWidth() const
{
return d->sampleWidth;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void RIFF::AIFF::Properties::read(const ByteVector &data)
{
d->channels = data.mid(0, 2).toShort();
uint sampleFrames = data.mid(2, 4).toUInt();
d->sampleWidth = data.mid(6, 2).toShort();
double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<unsigned char *>(data.mid(8, 10).data()));
d->sampleRate = sampleRate;
d->bitrate = (sampleRate * d->sampleWidth * d->channels) / 1000.0;
d->length = d->sampleRate > 0 ? sampleFrames / d->sampleRate : 0;
}

View File

@@ -1,82 +0,0 @@
/***************************************************************************
copyright : (C) 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_AIFFPROPERTIES_H
#define TAGLIB_AIFFPROPERTIES_H
#include "audioproperties.h"
namespace TagLib {
namespace RIFF {
namespace AIFF {
class File;
//! An implementation of audio property reading for AIFF
/*!
* This reads the data from an AIFF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of AIFF::Properties with the data read from the
* ByteVector \a data.
*/
Properties(const ByteVector &data, ReadStyle style);
/*!
* Destroys this AIFF::Properties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
int sampleWidth() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read(const ByteVector &data);
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
}
#endif

View File

@@ -1,274 +0,0 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tdebug.h>
#include <tstring.h>
#include "rifffile.h"
#include <vector>
using namespace TagLib;
struct Chunk
{
ByteVector name;
TagLib::uint offset;
TagLib::uint size;
char padding;
};
class RIFF::File::FilePrivate
{
public:
FilePrivate() :
endianness(BigEndian),
size(0)
{
}
Endianness endianness;
ByteVector type;
TagLib::uint size;
ByteVector format;
std::vector<Chunk> chunks;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
RIFF::File::~File()
{
delete d;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
RIFF::File::File(FileName file, Endianness endianness) : TagLib::File(file)
{
d = new FilePrivate;
d->endianness = endianness;
if(isOpen())
read();
}
TagLib::uint RIFF::File::riffSize() const
{
return d->size;
}
TagLib::uint RIFF::File::chunkCount() const
{
return d->chunks.size();
}
TagLib::uint RIFF::File::chunkDataSize(uint i) const
{
return d->chunks[i].size;
}
TagLib::uint RIFF::File::chunkOffset(uint i) const
{
return d->chunks[i].offset;
}
TagLib::uint RIFF::File::chunkPadding(uint i) const
{
return d->chunks[i].padding;
}
ByteVector RIFF::File::chunkName(uint i) const
{
if(i >= chunkCount())
return ByteVector::null;
return d->chunks[i].name;
}
ByteVector RIFF::File::chunkData(uint i)
{
if(i >= chunkCount())
return ByteVector::null;
// Offset for the first subchunk's data
long begin = 12 + 8;
for(uint it = 0; it < i; it++)
begin += 8 + d->chunks[it].size + d->chunks[it].padding;
seek(begin);
return readBlock(d->chunks[i].size);
}
void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
{
if(d->chunks.size() == 0) {
debug("RIFF::File::setChunkData - No valid chunks found.");
return;
}
for(uint i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == name) {
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
// Now update the specific chunk
writeChunk(name, data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8);
d->chunks[i].size = data.size();
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
// Now update the internal offsets
for(i++; i < d->chunks.size(); i++)
d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding;
return;
}
}
// Couldn't find an existing chunk, so let's create a new one.
uint i = d->chunks.size() - 1;
ulong offset = d->chunks[i].offset + d->chunks[i].size;
// First we update the global size
d->size += (offset & 1) + data.size() + 8;
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
// Now add the chunk to the file
writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0);
// And update our internal structure
if (offset & 1) {
d->chunks[i].padding = 1;
offset++;
}
Chunk chunk;
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 8;
chunk.padding = (data.size() & 0x01) ? 1 : 0;
d->chunks.push_back(chunk);
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
static bool isValidChunkID(const ByteVector &name)
{
if(name.size() != 4) {
return false;
}
for(int i = 0; i < 4; i++) {
if(name[i] < 32 || name[i] > 127) {
return false;
}
}
return true;
}
void RIFF::File::read()
{
bool bigEndian = (d->endianness == BigEndian);
d->type = readBlock(4);
d->size = readBlock(4).toUInt(bigEndian);
d->format = readBlock(4);
// + 8: chunk header at least, fix for additional junk bytes
while(tell() + 8 <= length()) {
ByteVector chunkName = readBlock(4);
uint chunkSize = readBlock(4).toUInt(bigEndian);
if(!isValidChunkID(chunkName)) {
debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
setValid(false);
break;
}
if(tell() + chunkSize > uint(length())) {
debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)");
setValid(false);
break;
}
Chunk chunk;
chunk.name = chunkName;
chunk.size = chunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// check padding
chunk.padding = 0;
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 0)) {
// not well formed, re-seek
seek(uPosNotPadded, Beginning);
}
else {
chunk.padding = 1;
}
}
d->chunks.push_back(chunk);
}
}
void RIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
ulong offset, ulong replace, uint leadingPadding)
{
ByteVector combined;
if(leadingPadding) {
combined.append(ByteVector(leadingPadding, '\x00'));
}
combined.append(name);
combined.append(ByteVector::fromUInt(data.size(), d->endianness == BigEndian));
combined.append(data);
if((data.size() & 0x01) != 0) {
combined.append('\x00');
}
insert(combined, offset, replace);
}

View File

@@ -1,121 +0,0 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_RIFFFILE_H
#define TAGLIB_RIFFFILE_H
#include "taglib_export.h"
#include "tfile.h"
namespace TagLib {
//! An implementation of TagLib::File with RIFF specific methods
namespace RIFF {
//! An RIFF file class with some useful methods specific to RIFF
/*!
* This implements the generic TagLib::File API and additionally provides
* access to properties that are distinct to RIFF files, notably access
* to the different ID3 tags.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* Destroys this instance of the File.
*/
virtual ~File();
protected:
enum Endianness { BigEndian, LittleEndian };
File(FileName file, Endianness endianness);
/*!
* \return The size of the main RIFF chunk.
*/
uint riffSize() const;
/*!
* \return The number of chunks in the file.
*/
uint chunkCount() const;
/*!
* \return The offset within the file for the selected chunk number.
*/
uint chunkOffset(uint i) const;
/*!
* \return The size of the chunk data.
*/
uint chunkDataSize(uint i) const;
/*!
* \return The size of the padding after the chunk (can be either 0 or 1).
*/
uint chunkPadding(uint i) const;
/*!
* \return The name of the specified chunk, for instance, "COMM" or "ID3 "
*/
ByteVector chunkName(uint i) const;
/*!
* Reads the chunk data from the file and returns it.
*
* \note This \e will move the read pointer for the file.
*/
ByteVector chunkData(uint i);
/*!
* Sets the data for the chunk \a name to \a data. If a chunk with the
* given name already exists it will be overwritten, otherwise it will be
* created after the existing chunks.
*
* \warning This will update the file immediately.
*/
void setChunkData(const ByteVector &name, const ByteVector &data);
private:
File(const File &);
File &operator=(const File &);
void read();
void writeChunk(const ByteVector &name, const ByteVector &data,
ulong offset, ulong replace = 0,
uint leadingPadding = 0);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -1,124 +0,0 @@
/***************************************************************************
copyright : (C) 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include "wavfile.h"
using namespace TagLib;
class RIFF::WAV::File::FilePrivate
{
public:
FilePrivate() :
properties(0),
tag(0),
tagChunkID("ID3 ")
{
}
~FilePrivate()
{
delete properties;
delete tag;
}
Properties *properties;
ID3v2::Tag *tag;
ByteVector tagChunkID;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
RIFF::WAV::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : RIFF::File(file, LittleEndian)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
RIFF::WAV::File::~File()
{
delete d;
}
ID3v2::Tag *RIFF::WAV::File::tag() const
{
return d->tag;
}
RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const
{
return d->properties;
}
bool RIFF::WAV::File::save()
{
if(readOnly()) {
debug("RIFF::WAV::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("RIFF::WAV::File::save() -- Trying to save invalid file.");
return false;
}
setChunkData(d->tagChunkID, d->tag->render());
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
ByteVector formatData;
uint streamLength = 0;
for(uint i = 0; i < chunkCount(); i++) {
if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") {
d->tagChunkID = chunkName(i);
d->tag = new ID3v2::Tag(this, chunkOffset(i));
}
else if(chunkName(i) == "fmt " && readProperties)
formatData = chunkData(i);
else if(chunkName(i) == "data" && readProperties)
streamLength = chunkDataSize(i);
}
if(!formatData.isEmpty())
d->properties = new Properties(formatData, streamLength, propertiesStyle);
if(!d->tag)
d->tag = new ID3v2::Tag;
}

View File

@@ -1,102 +0,0 @@
/***************************************************************************
copyright : (C) 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_WAVFILE_H
#define TAGLIB_WAVFILE_H
#include "rifffile.h"
#include "id3v2tag.h"
#include "wavproperties.h"
namespace TagLib {
namespace RIFF {
//! An implementation of WAV metadata
/*!
* This is implementation of WAV metadata.
*
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
* chunk as well as properties from the file.
*/
namespace WAV {
//! An implementation of TagLib::File with WAV specific methods
/*!
* This implements and provides an interface for WAV files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to WAV files.
*/
class TAGLIB_EXPORT File : public TagLib::RIFF::File
{
public:
/*!
* Contructs an WAV 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,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the Tag for this file.
*/
virtual ID3v2::Tag *tag() const;
/*!
* Returns the WAV::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
/*!
* Saves the file.
*/
virtual bool save();
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
class FilePrivate;
FilePrivate *d;
};
}
}
}
#endif

View File

@@ -1,120 +0,0 @@
/***************************************************************************
copyright : (C) 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "wavproperties.h"
#include <tstring.h>
#include <tdebug.h>
#include <cmath>
#include <math.h>
using namespace TagLib;
class RIFF::WAV::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(uint streamLength = 0) :
format(0),
length(0),
bitrate(0),
sampleRate(0),
channels(0),
sampleWidth(0),
streamLength(streamLength)
{
}
short format;
int length;
int bitrate;
int sampleRate;
int channels;
int sampleWidth;
uint streamLength;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
RIFF::WAV::Properties::Properties(const ByteVector &data, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate();
read(data);
}
RIFF::WAV::Properties::Properties(const ByteVector &data, uint streamLength, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(streamLength);
read(data);
}
RIFF::WAV::Properties::~Properties()
{
delete d;
}
int RIFF::WAV::Properties::length() const
{
return d->length;
}
int RIFF::WAV::Properties::bitrate() const
{
return d->bitrate;
}
int RIFF::WAV::Properties::sampleRate() const
{
return d->sampleRate;
}
int RIFF::WAV::Properties::channels() const
{
return d->channels;
}
int RIFF::WAV::Properties::sampleWidth() const
{
return d->sampleWidth;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void RIFF::WAV::Properties::read(const ByteVector &data)
{
d->format = data.mid(0, 2).toShort(false);
d->channels = data.mid(2, 2).toShort(false);
d->sampleRate = data.mid(4, 4).toUInt(false);
d->sampleWidth = data.mid(14, 2).toShort(false);
uint byteRate = data.mid(8, 4).toUInt(false);
d->bitrate = byteRate * 8 / 1000;
d->length = byteRate > 0 ? d->streamLength / byteRate : 0;
}

View File

@@ -1,91 +0,0 @@
/***************************************************************************
copyright : (C) 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* 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 *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_WAVPROPERTIES_H
#define TAGLIB_WAVPROPERTIES_H
#include "taglib.h"
#include "audioproperties.h"
namespace TagLib {
class ByteVector;
namespace RIFF {
namespace WAV {
class File;
//! An implementation of audio property reading for WAV
/*!
* This reads the data from an WAV stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of WAV::Properties with the data read from the
* ByteVector \a data.
*/
Properties(const ByteVector &data, ReadStyle style);
/*!
* Create an instance of WAV::Properties with the data read from the
* ByteVector \a data and the length calculated using \a streamLength.
*/
Properties(const ByteVector &data, uint streamLength, ReadStyle style);
/*!
* Destroys this WAV::Properties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
int sampleWidth() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read(const ByteVector &data);
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
}
#endif

View File

@@ -24,6 +24,8 @@
***************************************************************************/ ***************************************************************************/
#include "tag.h" #include "tag.h"
#include "tstringlist.h"
#include "tpropertymap.h"
using namespace TagLib; using namespace TagLib;
@@ -53,6 +55,101 @@ bool Tag::isEmpty() const
track() == 0); track() == 0);
} }
PropertyMap Tag::properties() const
{
PropertyMap map;
if(!(title().isNull()))
map["TITLE"].append(title());
if(!(artist().isNull()))
map["ARTIST"].append(artist());
if(!(album().isNull()))
map["ALBUM"].append(album());
if(!(comment().isNull()))
map["COMMENT"].append(comment());
if(!(genre().isNull()))
map["GENRE"].append(genre());
if(!(year() == 0))
map["DATE"].append(String::number(year()));
if(!(track() == 0))
map["TRACKNUMBER"].append(String::number(track()));
return map;
}
void Tag::removeUnsupportedProperties(const StringList&)
{
}
PropertyMap Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap properties(origProps);
properties.removeEmpty();
StringList oneValueSet;
// can this be simplified by using some preprocessor defines / function pointers?
if(properties.contains("TITLE")) {
setTitle(properties["TITLE"].front());
oneValueSet.append("TITLE");
} else
setTitle(String::null);
if(properties.contains("ARTIST")) {
setArtist(properties["ARTIST"].front());
oneValueSet.append("ARTIST");
} else
setArtist(String::null);
if(properties.contains("ALBUM")) {
setAlbum(properties["ALBUM"].front());
oneValueSet.append("ALBUM");
} else
setAlbum(String::null);
if(properties.contains("COMMENT")) {
setComment(properties["COMMENT"].front());
oneValueSet.append("COMMENT");
} else
setComment(String::null);
if(properties.contains("GENRE")) {
setGenre(properties["GENRE"].front());
oneValueSet.append("GENRE");
} else
setGenre(String::null);
if(properties.contains("DATE")) {
bool ok;
int date = properties["DATE"].front().toInt(&ok);
if(ok) {
setYear(date);
oneValueSet.append("DATE");
} else
setYear(0);
}
else
setYear(0);
if(properties.contains("TRACKNUMBER")) {
bool ok;
int track = properties["TRACKNUMBER"].front().toInt(&ok);
if(ok) {
setTrack(track);
oneValueSet.append("TRACKNUMBER");
} else
setTrack(0);
}
else
setYear(0);
// for each tag that has been set above, remove the first entry in the corresponding
// value list. The others will be returned as unsupported by this format.
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
if(properties[*it].size() == 1)
properties.erase(*it);
else
properties[*it].erase( properties[*it].begin() );
}
return properties;
}
void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static
{ {
if(overwrite) { if(overwrite) {

Some files were not shown because too many files have changed in this diff Show More