mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
NowPlaying: Update TagLib to 1.9.1
This commit is contained in:
@@ -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.
|
||||||
**
|
**
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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,7 +90,16 @@ 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;
|
||||||
read(readProperties, propertiesStyle);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
APE::File::~File()
|
APE::File::~File()
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 &);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -114,32 +138,59 @@ ByteVector APE::Item::value() const
|
|||||||
|
|
||||||
void APE::Item::setKey(const String &key)
|
void APE::Item::setKey(const String &key)
|
||||||
{
|
{
|
||||||
d->key = key;
|
d->key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APE::Item::setValue(const String &value)
|
void APE::Item::setValue(const String &value)
|
||||||
{
|
{
|
||||||
d->text = value;
|
d->type = Text;
|
||||||
|
d->text = value;
|
||||||
|
d->value.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APE::Item::setValues(const StringList &value)
|
void APE::Item::setValues(const StringList &value)
|
||||||
{
|
{
|
||||||
d->text = value;
|
d->type = Text;
|
||||||
|
d->text = value;
|
||||||
|
d->value.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APE::Item::appendValue(const String &value)
|
void APE::Item::appendValue(const String &value)
|
||||||
{
|
{
|
||||||
d->text.append(value);
|
d->type = Text;
|
||||||
|
d->text.append(value);
|
||||||
|
d->value.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APE::Item::appendValues(const StringList &values)
|
void APE::Item::appendValues(const StringList &values)
|
||||||
{
|
{
|
||||||
d->text.append(values);
|
d->type = Text;
|
||||||
|
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
|
||||||
|
|||||||
@@ -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()
|
||||||
*/
|
*/
|
||||||
@@ -138,19 +152,20 @@ namespace TagLib {
|
|||||||
int size() const;
|
int size() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* 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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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,17 +293,31 @@ 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)) {
|
||||||
d->itemListMap[key.upper()].appendValue(value);
|
// Text items may contain more than one value
|
||||||
|
if(APE::Item::Text == d->itemListMap.begin()->second.type())
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
d->itemListMap.insert(key.upper(), item);
|
if(!key.isEmpty())
|
||||||
|
d->itemListMap.insert(key.upper(), item);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool APE::Tag::isEmpty() const
|
bool APE::Tag::isEmpty() const
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ namespace TagLib
|
|||||||
/*!
|
/*!
|
||||||
* Constructs an attribute with \a key and a BytesType \a value.
|
* Constructs an attribute with \a key and a BytesType \a value.
|
||||||
*/
|
*/
|
||||||
Attribute(const ByteVector &value);
|
Attribute(const ByteVector &value);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Constructs an attribute with \a key and a Picture \a value.
|
* Constructs an attribute with \a key and a Picture \a value.
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -367,11 +366,20 @@ ByteVector ASF::File::HeaderExtensionObject::render(ASF::File *file)
|
|||||||
// public members
|
// public members
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle)
|
ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||||
: TagLib::File(file)
|
: TagLib::File(file)
|
||||||
{
|
{
|
||||||
d = new FilePrivate;
|
d = new FilePrivate;
|
||||||
read(readProperties, propertiesStyle);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASF::File::~File()
|
ASF::File::~File()
|
||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -208,8 +208,8 @@ namespace TagLib
|
|||||||
friend class Attribute;
|
friend class Attribute;
|
||||||
#endif
|
#endif
|
||||||
private:
|
private:
|
||||||
struct PicturePriavte;
|
class PicturePrivate;
|
||||||
PicturePriavte *d;
|
PicturePrivate *d;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -57,7 +52,7 @@ ASF::Properties::Properties() : AudioProperties(AudioProperties::Average)
|
|||||||
ASF::Properties::~Properties()
|
ASF::Properties::~Properties()
|
||||||
{
|
{
|
||||||
if(d)
|
if(d)
|
||||||
delete d;
|
delete d;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ASF::Properties::length() const
|
int ASF::Properties::length() const
|
||||||
@@ -78,7 +73,12 @@ int ASF::Properties::sampleRate() const
|
|||||||
int ASF::Properties::channels() const
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
@@ -71,4 +73,4 @@ namespace TagLib {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -120,17 +120,17 @@ namespace TagLib {
|
|||||||
virtual void setComment(const String &s);
|
virtual void setComment(const String &s);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Sets the rating to \a s.
|
* Sets the rating to \a s.
|
||||||
*/
|
*/
|
||||||
virtual void setRating(const String &s);
|
virtual void setRating(const String &s);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Sets the copyright to \a s.
|
* Sets the copyright to \a s.
|
||||||
*/
|
*/
|
||||||
virtual void setCopyright(const String &s);
|
virtual void setCopyright(const String &s);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Sets the genre to \a s.
|
* Sets the genre to \a s.
|
||||||
*/
|
*/
|
||||||
virtual void setGenre(const String &s);
|
virtual void setGenre(const String &s);
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
#define WITH_ASF 1
|
|
||||||
#define WITH_MP4 1
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||||
email : wheeler@kde.org
|
email : wheeler@kde.org
|
||||||
|
|
||||||
copyright : (C) 2010 by Alex Novichkov
|
copyright : (C) 2010 by Alex Novichkov
|
||||||
email : novichko@atnet.ru
|
email : novichko@atnet.ru
|
||||||
(added APE file support)
|
(added APE file support)
|
||||||
@@ -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 == "TTA")
|
// if(ext == "OPUS")
|
||||||
// return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
|
// return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||||
#ifdef TAGLIB_WITH_MP4
|
// if(ext == "TTA")
|
||||||
if(ext == "M4A" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
|
// return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||||
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
|
// if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
|
||||||
#endif
|
// return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||||
#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;
|
||||||
|
|||||||
@@ -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,7 +109,8 @@ FLAC::File::File(FileName file, bool readProperties,
|
|||||||
TagLib::File(file)
|
TagLib::File(file)
|
||||||
{
|
{
|
||||||
d = new FilePrivate;
|
d = new FilePrivate;
|
||||||
read(readProperties, propertiesStyle);
|
if(isOpen())
|
||||||
|
read(readProperties, propertiesStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||||
@@ -116,7 +119,18 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
|||||||
{
|
{
|
||||||
d = new FilePrivate;
|
d = new FilePrivate;
|
||||||
d->ID3v2FrameFactory = frameFactory;
|
d->ID3v2FrameFactory = frameFactory;
|
||||||
read(readProperties, propertiesStyle);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
FLAC::File::~File()
|
FLAC::File::~File()
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 &);
|
||||||
|
|||||||
@@ -23,17 +23,13 @@
|
|||||||
* 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"
|
||||||
|
|
||||||
using namespace TagLib;
|
using namespace TagLib;
|
||||||
|
|
||||||
class FLAC::MetadataBlock::MetadataBlockPrivate
|
class FLAC::MetadataBlock::MetadataBlockPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MetadataBlockPrivate() {}
|
MetadataBlockPrivate() {}
|
||||||
|
|||||||
@@ -23,17 +23,13 @@
|
|||||||
* 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"
|
||||||
|
|
||||||
using namespace TagLib;
|
using namespace TagLib;
|
||||||
|
|
||||||
class FLAC::Picture::PicturePrivate
|
class FLAC::Picture::PicturePrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PicturePrivate() :
|
PicturePrivate() :
|
||||||
@@ -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.");
|
||||||
@@ -117,7 +113,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
|||||||
}
|
}
|
||||||
d->data = data.mid(pos, dataLength);
|
d->data = data.mid(pos, dataLength);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteVector FLAC::Picture::render() const
|
ByteVector FLAC::Picture::render() const
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -77,9 +77,14 @@ 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.
|
||||||
*/
|
*/
|
||||||
ByteVector signature() const;
|
ByteVector signature() const;
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -34,7 +30,7 @@
|
|||||||
|
|
||||||
using namespace TagLib;
|
using namespace TagLib;
|
||||||
|
|
||||||
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
|
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UnknownMetadataBlockPrivate() : code(0) {}
|
UnknownMetadataBlockPrivate() : code(0) {}
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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,7 +94,16 @@ 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;
|
||||||
read(readProperties, propertiesStyle);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
MPC::File::~File()
|
MPC::File::~File()
|
||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 &);
|
||||||
|
|||||||
@@ -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;
|
||||||
return;
|
|
||||||
|
|
||||||
d->version = d->data[3] & 15;
|
while(!readSH && !readRG) {
|
||||||
|
ByteVector packetType = file->readBlock(2);
|
||||||
|
uint packetSizeLength = 0;
|
||||||
|
unsigned long packetSize = readSize(file, packetSizeLength);
|
||||||
|
unsigned long dataSize = packetSize - 2 - packetSizeLength;
|
||||||
|
|
||||||
unsigned int frames;
|
if(packetType == "SH") {
|
||||||
|
// Stream Header
|
||||||
|
// http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket
|
||||||
|
ByteVector data = file->readBlock(dataSize);
|
||||||
|
readSH = true;
|
||||||
|
|
||||||
if(d->version >= 7) {
|
TagLib::uint pos = 4;
|
||||||
frames = d->data.mid(4, 4).toUInt(false);
|
d->version = data[pos];
|
||||||
|
pos += 1;
|
||||||
|
d->sampleFrames = readSize(data.mid(pos), pos);
|
||||||
|
ulong begSilence = readSize(data.mid(pos), pos);
|
||||||
|
|
||||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(d->data.mid(8, 4).toUInt(false)));
|
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;
|
||||||
|
|
||||||
|
d->totalFrames = data.toUInt(4, false);
|
||||||
|
|
||||||
|
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(8, 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
else
|
||||||
TagPrivate::stringHandler = handler;
|
TagPrivate::stringHandler = &defaultStringHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ void AttachedPictureFrame::parseFields(const ByteVector &data)
|
|||||||
int pos = 1;
|
int pos = 1;
|
||||||
|
|
||||||
d->mimeType = readStringField(data, String::Latin1, &pos);
|
d->mimeType = readStringField(data, String::Latin1, &pos);
|
||||||
/* Now we need at least two more bytes available */
|
/* Now we need at least two more bytes available */
|
||||||
if (uint(pos) + 1 >= data.size()) {
|
if (uint(pos) + 1 >= data.size()) {
|
||||||
debug("Truncated picture frame.");
|
debug("Truncated picture frame.");
|
||||||
return;
|
return;
|
||||||
@@ -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));
|
||||||
|
|||||||
@@ -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,8 +158,13 @@ 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) {
|
||||||
d->description = String(l.front(), d->textEncoding);
|
if(d->textEncoding == String::Latin1) {
|
||||||
d->text = String(l.back(), d->textEncoding);
|
d->description = Tag::latin1StringHandler()->parse(l.front());
|
||||||
|
d->text = Tag::latin1StringHandler()->parse(l.back());
|
||||||
|
} else {
|
||||||
|
d->description = String(l.front(), d->textEncoding);
|
||||||
|
d->text = String(l.back(), d->textEncoding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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");
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ void PrivateFrame::parseFields(const ByteVector &data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Owner identifier is assumed to be Latin1
|
// Owner identifier is assumed to be Latin1
|
||||||
|
|
||||||
const int byteAlign = 1;
|
const int byteAlign = 1;
|
||||||
const int endOfOwner = data.find(textDelimiter(String::Latin1), 0, byteAlign);
|
const int endOfOwner = data.find(textDelimiter(String::Latin1), 0, byteAlign);
|
||||||
|
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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,8 +158,13 @@ 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) {
|
||||||
d->description = String(l.front(), d->textEncoding);
|
if(d->textEncoding == String::Latin1) {
|
||||||
d->text = String(l.back(), d->textEncoding);
|
d->description = Tag::latin1StringHandler()->parse(l.front());
|
||||||
|
d->text = Tag::latin1StringHandler()->parse(l.back());
|
||||||
|
} else {
|
||||||
|
d->description = String(l.front(), d->textEncoding);
|
||||||
|
d->text = String(l.back(), d->textEncoding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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()) {
|
||||||
debug("Frame::checkEncoding() -- Rendering using UTF8.");
|
if(version == 4) {
|
||||||
return String::UTF8;
|
debug("Frame::checkEncoding() -- Rendering using 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -184,13 +195,13 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
|
|||||||
|
|
||||||
// ID3v2.2 Attached Picture
|
// ID3v2.2 Attached Picture
|
||||||
|
|
||||||
if(frameID == "PIC") {
|
if(frameID == "PIC") {
|
||||||
AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header);
|
AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header);
|
||||||
d->setTextEncoding(f);
|
d->setTextEncoding(f);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relative Volume Adjustment (frames 4.11)
|
// Relative Volume Adjustment (frames 4.11)
|
||||||
|
|
||||||
if(frameID == "RVA2")
|
if(frameID == "RVA2")
|
||||||
return new RelativeVolumeFrame(data, header);
|
return new RelativeVolumeFrame(data, header);
|
||||||
@@ -239,6 +250,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -324,12 +355,206 @@ void ID3v2::Tag::removeFrame(Frame *frame, bool del)
|
|||||||
|
|
||||||
void ID3v2::Tag::removeFrames(const ByteVector &id)
|
void ID3v2::Tag::removeFrames(const ByteVector &id)
|
||||||
{
|
{
|
||||||
FrameList l = d->frameListMap[id];
|
FrameList l = d->frameListMap[id];
|
||||||
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
|
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
|
||||||
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;
|
||||||
|
|||||||
@@ -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 &);
|
||||||
|
|||||||
@@ -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) {
|
||||||
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
|
|
||||||
|
|
||||||
if((tags & ID3v1) && d->tag[ID3v2Index])
|
// Copy the values from the tag that does exist into the new tag,
|
||||||
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
|
// except if the existing tag is to be stripped.
|
||||||
|
|
||||||
|
if((tags & ID3v2) && ID3v1Tag() && !(stripOthers && !(tags & ID3v1)))
|
||||||
|
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
|
||||||
|
|
||||||
|
if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2)))
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -186,7 +243,7 @@ bool MPEG::File::save(int tags, bool stripOthers)
|
|||||||
// APE tag location has changed, update if it exists
|
// APE tag location has changed, update if it exists
|
||||||
|
|
||||||
if(APETag())
|
if(APETag())
|
||||||
findAPE();
|
findAPE();
|
||||||
}
|
}
|
||||||
else if(stripOthers)
|
else if(stripOthers)
|
||||||
success = strip(ID3v2, false) && success;
|
success = strip(ID3v2, false) && success;
|
||||||
@@ -224,9 +281,10 @@ bool MPEG::File::save(int tags, bool stripOthers)
|
|||||||
else {
|
else {
|
||||||
seek(0, End);
|
seek(0, End);
|
||||||
d->APELocation = tell();
|
d->APELocation = tell();
|
||||||
d->APEFooterLocation = d->APELocation
|
APE::Tag *apeTag = d->tag.access<APE::Tag>(APEIndex, false);
|
||||||
+ d->tag.access<APE::Tag>(APEIndex, false)->footer()->completeTagSize()
|
d->APEFooterLocation = d->APELocation
|
||||||
- APE::Footer::size();
|
+ apeTag->footer()->completeTagSize()
|
||||||
|
- APE::Footer::size();
|
||||||
writeBlock(APETag()->render());
|
writeBlock(APETag()->render());
|
||||||
d->APEOriginalSize = APETag()->footer()->completeTagSize();
|
d->APEOriginalSize = APETag()->footer()->completeTagSize();
|
||||||
d->hasAPE = true;
|
d->hasAPE = true;
|
||||||
@@ -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
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -572,7 +645,7 @@ void MPEG::File::findAPE()
|
|||||||
seek(d->APEFooterLocation);
|
seek(d->APEFooterLocation);
|
||||||
APE::Footer footer(readBlock(APE::Footer::size()));
|
APE::Footer footer(readBlock(APE::Footer::size()));
|
||||||
d->APELocation = d->APEFooterLocation - footer.completeTagSize()
|
d->APELocation = d->APEFooterLocation - footer.completeTagSize()
|
||||||
+ APE::Footer::size();
|
+ APE::Footer::size();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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 &);
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,7 +73,16 @@ 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;
|
||||||
read(readProperties, propertiesStyle);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ogg::FLAC::File::~File()
|
Ogg::FLAC::File::~File()
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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 &);
|
||||||
|
|||||||
@@ -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
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -276,8 +281,8 @@ void Ogg::File::writePageGroup(const List<int> &thePageGroup)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
// pages in the pageGroup and packets must be equivalent
|
// pages in the pageGroup and packets must be equivalent
|
||||||
// (originalSize and size of packets would not work together),
|
// (originalSize and size of packets would not work together),
|
||||||
// therefore we sometimes have to add pages to the group
|
// therefore we sometimes have to add pages to the group
|
||||||
List<int> pageGroup(thePageGroup);
|
List<int> pageGroup(thePageGroup);
|
||||||
while (!d->pages[pageGroup.back()]->header()->lastPacketCompleted()) {
|
while (!d->pages[pageGroup.back()]->header()->lastPacketCompleted()) {
|
||||||
@@ -336,7 +341,7 @@ void Ogg::File::writePageGroup(const List<int> &thePageGroup)
|
|||||||
|
|
||||||
if (pages.back()->header()->pageSequenceNumber() != pageGroup.back()) {
|
if (pages.back()->header()->pageSequenceNumber() != pageGroup.back()) {
|
||||||
|
|
||||||
// TODO: change the internal data structure so that we don't need to hold the
|
// TODO: change the internal data structure so that we don't need to hold the
|
||||||
// complete file in memory (is unavoidable at the moment)
|
// complete file in memory (is unavoidable at the moment)
|
||||||
|
|
||||||
// read the complete stream
|
// read the complete stream
|
||||||
|
|||||||
@@ -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 &);
|
||||||
|
|||||||
@@ -116,9 +116,9 @@ Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const
|
|||||||
flags = ContainsPacketFlags(flags | CompletePacket);
|
flags = ContainsPacketFlags(flags | CompletePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Or if there is more than one page and the page is
|
// Or if there is more than one page and the page is
|
||||||
// (a) the first page and it's complete or
|
// (a) the first page and it's complete or
|
||||||
// (b) the last page and it's complete or
|
// (b) the last page and it's complete or
|
||||||
// (c) a page in the middle.
|
// (c) a page in the middle.
|
||||||
else if(packetCount() > 1 &&
|
else if(packetCount() > 1 &&
|
||||||
((flags & BeginsWithPacket && !d->header.firstPacketContinued()) ||
|
((flags & BeginsWithPacket && !d->header.firstPacketContinued()) ||
|
||||||
@@ -266,7 +266,7 @@ List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued,
|
Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued,
|
||||||
lastPacketInList ? lastPacketCompleted : true,
|
lastPacketInList ? lastPacketCompleted : true,
|
||||||
isVeryLastPacket);
|
isVeryLastPacket);
|
||||||
pageIndex++;
|
pageIndex++;
|
||||||
|
|
||||||
|
|||||||
@@ -70,9 +70,9 @@ namespace TagLib {
|
|||||||
*/
|
*/
|
||||||
const PageHeader *header() const;
|
const PageHeader *header() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Returns a copy of the page with \a sequenceNumber set as sequence number.
|
* Returns a copy of the page with \a sequenceNumber set as sequence number.
|
||||||
*
|
*
|
||||||
* \see header()
|
* \see header()
|
||||||
* \see PageHeader::setPageSequenceNumber()
|
* \see PageHeader::setPageSequenceNumber()
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,7 +63,16 @@ 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;
|
||||||
read(readProperties, propertiesStyle);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
Speex::File::~File()
|
Speex::File::~File()
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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,7 +67,16 @@ 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;
|
||||||
read(readProperties, propertiesStyle);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vorbis::File::~File()
|
Vorbis::File::~File()
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.");
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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".
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user