4 Commits

Author SHA1 Message Date
Lukáš Lalinský
36d9c94f1f Add unit tests 2011-07-14 14:13:24 +02:00
Lukáš Lalinský
b53c08c067 Merge remote branch 'HessiJames/master' 2011-07-14 13:40:33 +02:00
Daniel Faust
0ea8e44df7 String to number conversion moved to new function - long readNumber(ByteVector vector) 2011-07-07 17:57:22 +02:00
Daniel Faust
27332c35ac Find APE tags even if there's a Lyrics3v2 tag present
http://bugs.kde.org/show_bug.cgi?id=254223
2011-07-05 17:24:59 +02:00
409 changed files with 13885 additions and 42838 deletions

View File

@@ -1,18 +0,0 @@
--suffix=none
--style=kr
--indent=spaces=2
--indent-col1-comments
--min-conditional-indent=0
--attach-extern-c
--attach-namespaces
--indent-namespaces
--pad-oper
--unpad-paren
--align-pointer=name
--align-reference=name
--max-instatement-indent=40
--break-closing-brackets
--remove-brackets
--convert-tabs
--max-code-length=100
--break-after-logical

View File

@@ -1,21 +0,0 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Non-specified newlines with a newline ending every file
[*]
insert_final_newline = true
# 2 space indentation
[*.{h,cpp,tcc,cmake,yml}]
indent_style = space
indent_size = 2
# Trim trailing whitespaces
[*.{h,cpp,tcc,cmake,yml}]
trim_trailing_whitespace = true
# UTF-8 without BOM
[*]
charset = utf-8

View File

@@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@@ -1,88 +0,0 @@
name: Build
on: [push, pull_request, workflow_dispatch]
env:
BUILD_TYPE: Release
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest, windows-2025]
include:
- os: windows-latest
cmake_extra_args: '-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"'
- os: windows-2025
cmake_extra_args: '-G "MinGW Makefiles" -DCPPUNIT_INCLUDE_DIR="$env:GITHUB_WORKSPACE/pkg/usr/local/include" -DCPPUNIT_LIBRARIES="$($env:GITHUB_WORKSPACE -replace "\\", "/")/pkg/usr/local/lib/libcppunit.dll.a" -DCPPUNIT_INSTALLED_VERSION="1.15.1"'
# The windows-2025 runner is used for MinGW.
name: ${{ matrix.os == 'windows-2025' && 'mingw' || matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Ubuntu
run: sudo apt install -y libcppunit-dev libutfcpp-dev zlib1g-dev
if: matrix.os == 'ubuntu-latest'
- name: Set up macOS
run: |
brew update
brew install cppunit utf8cpp
if: matrix.os == 'macos-latest'
- name: Set up Windows
run: vcpkg install cppunit utfcpp zlib --triplet x64-windows
if: matrix.os == 'windows-latest'
- name: Set up MinGW
shell: bash
run: |
# Fetch utf8cpp, build cppunit
git submodule update --init
# Fails with fatal error: cppunit/config-auto.h: No such file or directory
# vcpkg install cppunit utfcpp zlib --triplet x64-mingw-dynamic
# Probably not working with CMake and MinGW, so we have to build it ourselves.
curl -sL "https://dev-www.libreoffice.org/src/cppunit-1.15.1.tar.gz" | tar xz
mkdir -p build_cppunit
(cd build_cppunit && MAKE=mingw32-make ../cppunit-1.15.1/configure \
--enable-shared=yes --enable-static=no \
--disable-dependency-tracking --disable-doxygen)
find build_cppunit -name Makefile -exec sed -i 's/\($[({]SHELL[)}]\)/"\1"/' {} \;
sed -i 's/^\(SUBDIRS.*\) examples\(.*\)$/\1\2/' build_cppunit/Makefile
(cd build_cppunit && mingw32-make -j$(nproc) install DESTDIR=$(cd .. && pwd)/pkg)
if: matrix.os == 'windows-2025'
- name: Configure
run: >
cmake -B${{github.workspace}}/build
-DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
${{ matrix.cmake_extra_args }}
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
- name: Test
working-directory: ${{github.workspace}}/build
run: ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
if: matrix.os != 'windows-latest' && matrix.os != 'windows-2025'
- name: Test Windows
working-directory: ${{github.workspace}}/build
run: |
$env:Path += ";$PWD\taglib\Release;$PWD\bindings\c\Release"
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\cppunit_x64-windows\bin"
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\utfcpp_x64-windows\bin"
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\zlib_x64-windows\bin"
ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
if: matrix.os == 'windows-latest'
- name: Test MinGW
working-directory: ${{github.workspace}}/build
run: |
$env:Path += ";$PWD/taglib;$PWD/bindings/c;${{github.workspace}}/pkg/usr/local/bin"
ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
if: matrix.os == 'windows-2025'

17
.gitignore vendored
View File

@@ -1,9 +1,6 @@
cmake_install.cmake
cmake_uninstall.cmake
Makefile
CTestTestfile.cmake
CMakeFiles/
CMakeLists.txt.user*
*.so
*.so.*
*.dylib
@@ -13,17 +10,14 @@ CMakeLists.txt.user*
*.suo
*.user
.*
!.github/workflows/
*~
/CMakeCache.txt
/Doxyfile
/config.h
/taglib.pc
/tests/test_runner
/tests/Testing
/taglib/libtag.a
/taglib_config.h
/taglib-config
/bindings/c/libtag_c.a
/bindings/c/taglib_c.pc
/bindings/c/Debug
/bindings/c/MinSizeRel
@@ -45,12 +39,3 @@ CMakeLists.txt.user*
/taglib/tag.dir/Release
/ALL_BUILD.dir
/ZERO_CHECK.dir
taglib.h.stamp
taglib.xcodeproj
CMakeScripts
/.clang-format
/compile_commands.json
/build/
.clangd
.cache
.idea

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "3rdparty/utfcpp"]
path = 3rdparty/utfcpp
url = https://github.com/nemtrif/utfcpp.git

1
3rdparty/utfcpp vendored

Submodule 3rdparty/utfcpp deleted from df857efc5b

View File

@@ -2,10 +2,6 @@ Scott Wheeler <wheeler@kde.org>
Author, maintainer
Lukas Lalinsky <lalinsky@gmail.com>
Implementation of multiple new file formats, many bug fixes, maintainer
Tsuda Kageyu <tsuda.kageyu@gmail.com>
A lot of bug fixes and performance improvements, maintainer.
Stephen F. Booth <me@sbooth.org>
DSF metadata implementation, bug fixes, maintainer.
Ismael Orenstein <orenstein@kde.org>
Xing header implementation
Allan Sandfeld Jensen <kde@carewolf.org>
@@ -14,10 +10,6 @@ Teemu Tervo <teemu.tervo@gmx.net>
Numerous bug reports and fixes
Mathias Panzenböck <grosser.meister.morti@gmx.net>
Mod, S3M, IT and XM metadata implementations
Damien Plisson <damien78@audirvana.com>
DSDIFF metadata implementation
Urs Fleisch <ufleisch@users.sourceforge.net>
Bug fixes, maintainer.
Please send all patches and questions to taglib-devel@kde.org rather than to
individual developers!

View File

@@ -1,543 +0,0 @@
TagLib 2.1.1 (June 30, 2025)
============================
* Map ID3v2.3 IPLS frames to both ID3v2.4 TIPL and TMCL to have a consistent
behavior when using MusicBrainz tags with the property map interface.
* Fix missing include for `wchar_t` when using C bindings with MinGW.
TagLib 2.1 (May 31, 2025)
=========================
* Support for Shorten (SHN) files.
* Compile time configuration of supported formats: WITH_APE, WITH_ASF, ...
* Compile time configuration of data and temporary directories for unit tests:
TESTS_DIR and TESTS_TMPDIR.
* C bindings: Added taglib_file_new_wchar() and taglib_file_new_type_wchar().
* Preserve unicode encoding when downgrading to ID3v2.3.
* Do not store FLAC metadata blocks which are too large.
* Fix segfaults with String and ByteVector nullptr arguments.
TagLib 2.0.2 (Aug 24, 2024)
===========================
* Fix parsing of ID3v2.2 frames.
* Tolerate MP4 files with unknown atom types as generated by Android tools.
* Support setting properties with arbitrary names in MP4 tags.
* Windows: Fix "-p" option in tagwriter example.
* Support building with older utfcpp versions.
TagLib 2.0.1 (Apr 9, 2024)
==========================
* Fix aborting when _GLIBCXX_ASSERTIONS are enabled.
* Fall back to utf8cpp header detection in the case that its CMake
configuration is removed.
* Improve compatibility with the SWIG interface compiler.
* Build system fixes for testing without bindings, Emscripten and Illumos.
* C bindings: Fix setting UTF-8 encoded property values.
* Windows: Fix opening long paths.
TagLib 2.0 (Jan 24, 2024)
=========================
* New major version, binary incompatible, but mostly source-compatible
with the latest 1.x release if no deprecated features are used.
Simple applications should build without changes, more complex
applications (e.g. extending classes of TagLib) will have to be adapted.
* Requires a C++17 compiler and uses features of C++17.
* Major code cleanup, fixed warnings issued by compilers and static analyzers.
* Made methods virtual which should have been virtual but could not be
changed to keep binary compatibility, remove related workarounds.
* Removed deprecated functions:
- APE::Item::Item(const String &, const String &)
- APE::Item::toStringList(): Use values()
- APE::Item::value(): Use binaryData()
- ASF::Properties::setLength()
- ByteVector::checksum()
- ByteVector::isNull(): Use isEmpty()
- ByteVector::null
- FLAC::File::setID3v2FrameFactory()
- FLAC::File::streamInfoData()
- FLAC::File::streamLength()
- FLAC::Properties::Properties(File *, ReadStyle)
- FLAC::Properties::sampleWidth(): Use bitsPerSample()
- File::isReadable(): Use system functions
- File::isWritable(): Use system functions
- FileName::str()
- FileRef::create(): Use constructor
- MP4::Tag::itemListMap(): Use itemMap()
- MPC::File::remove(): Use strip()
- MPC::Properties::Properties(const ByteVector &, long, ReadStyle)
- MPEG::File::save(int, ...): Use overload
- MPEG::File::setID3v2FrameFactory(): Use constructor
- MPEG::ID3v2::Frame::Header::Header(const ByteVector &, bool)
- MPEG::ID3v2::Frame::Header::frameAlterPreservation(): Use
fileAlterPreservation()
- MPEG::ID3v2::Frame::Header::setData(const ByteVector &, bool)
- MPEG::ID3v2::Frame::Header::size(unsigned int): Use size()
- MPEG::ID3v2::Frame::Header::unsycronisation(): use unsynchronisation()
- MPEG::ID3v2::Frame::checkEncoding(const StringList &, String::Type): Use
checkTextEncoding(const StringList &, String::Type)
- MPEG::ID3v2::Frame::headerSize(): Use Header::size()
- MPEG::ID3v2::Frame::headerSize(unsigned int): Use
Header::size(unsigned int)
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, bool)
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, unsigned int):
Use createFrame(const ByteVector &, const Header *)
- MPEG::ID3v2::RelativeVolumeFrame::channelType()
- MPEG::ID3v2::RelativeVolumeFrame::peakVolume(): Use peakVolume(ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::setChannelType()
- MPEG::ID3v2::RelativeVolumeFrame::setPeakVolume(const PeakVolume &): Use
setPeakVolume(const PeakVolume &, ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustment(float): Use
setVolumeAdjustment(float, ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustmentIndex(short): Use
setVolumeAdjustmentIndex(short, ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustment(): Use
volumeAdjustment(ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustmentIndex(): Use
volumeAdjustmentIndex(ChannelType)
- MPEG::ID3v2::Tag::footer()
- MPEG::ID3v2::Tag::render(int): Use render(Version)
- MPEG::XingHeader::xingHeaderOffset()
- Ogg::Page::getCopyWithNewPageSequenceNumber()
- Ogg::XiphComment::removeField(): Use removeFields()
- PropertyMap::unsupportedData(): Returns now const reference, use
addUnsupportedData() to add keys
- RIFF::AIFF::Properties::Properties(const ByteVector &, ReadStyle)
- RIFF::AIFF::Properties::Properties(const ByteVector &, int, ReadStyle)
- RIFF::AIFF::Properties::sampleWidth(): Use bitsPerSample()
- RIFF::WAV::File::save(TagTypes, bool, int): Use
save(TagTypes, StripTags, Version)
- RIFF::WAV::File::tag(): Returns now a TagUnion, use ID3v2Tag() to get an
ID3v2::Tag
- String::isNull(): Use isEmpty()
- String::null
- TrueAudio::File::setID3v2FrameFactory(): Use constructor
- WavPack::Properties::Properties(const ByteVector &, long, ReadStyle)
* Made methods const: Frame::Header::size(), Frame::headerSize(),
MP4::Atom::findall(), MP4::Atoms::find(), MP4::Atoms::path().
* Made classes non-virtual: APE::Footer, APE::Item, ASF::Attribute,
ASF::Picture, MP4::CoverArt, MP4::Item, ID3v2::ExtendedHeader, ID3v2::Footer,
ID3v2::Header, MPEG::Header, MPEG::XingHeader, Ogg::Page, Ogg::PageHeader.
* Removed type definitions in TagLib namespace: wchar, uchar, ushort, uint,
ulong, ulonglong, wstring: Use the standard types.
* Removed include file taglib_config.h and its defines TAGLIB_WITH_ASF,
TAGLIB_WITH_MP4: They were always 1 since version 1.8.
* Behavioral changes:
- The basic tag methods (e.g. genre()) separate multiple values with " / "
instead of " ".
- The stream operator for String uses UTF-8 instead of ISO-8859-1 encoding.
- MP4 property ORIGINALDATE is mapped to "----:com.apple.iTunes:ORIGINALDATE"
instead of "----:com.apple.iTunes:originaldate".
- MP4 property ENCODEDBY is mapped to "©enc" instead of "©too", which is now
mapped to ENCODING.
* Unified interface for complex properties like pictures.
* Simplified the unified properties interface by providing its methods on
FileRef.
* C bindings: Support for properties (taglib_property_...) and complex
properties like cover art (taglib_complex_property_...), memory I/O streams.
* Support for Direct Stream Digital (DSD) stream files (DSF) and interchange
file format (DSDIFF, DFF), ADTS (AAC) files.
* The runtime version can be queried.
* Additional utility functions ByteVector::fromUShort(),
ByteVector::fromULongLong(), ByteVector::toULongLong(),
ByteVector::toULongLong(), List::sort().
* Fixed List::setAutoDelete() affecting implicitly shared copies.
* Build system: Direct support for CMake, find_package(TagLib) exports target
TagLib::tag.
* Build system: Fixed PackageConfig to support both relative and absolute paths.
* Build system: utf8cpp is no longer included, it can be provided via a system
package or a Git submodule.
* ASF: Support additional properties ARTISTWEBPAGE, ENCODING, ENCODINGTIME,
FILEWEBPAGE, INITIALKEY, ORIGINALALBUM, ORIGINALARTIST, ORIGINALFILENAME,
ORIGINALLYRICIST.
* ID3v2: Fixed extensibility of FrameFactory, use it also for WAV and AIFF
files.
* MP4: Support additional properties OWNER, RELEASEDATE.
* MP4: Introduced ItemFactory allowing clients to support new atom types.
* MP4: Detect duration from mvhd atom if not present in mdhd atom.
* MP4: Fixed type of hdvd atom to be integer instead of boolean.
* MP4: Tolerate trailing garbage in M4A files.
* MPC: Fixed content check in presence of an ID3v2 tag.
* MPEG: Do not scan full file for ID3v2 tag when ReadStyle Fast is used.
* RIFF: Support properties ALBUM, ARRANGER, ARTIST, ARTISTWEBPAGE, BPM,
COMMENT, COMPOSER, COPYRIGHT, DATE, DISCSUBTITLE, ENCODEDBY, ENCODING,
ENCODINGTIME, GENRE, ISRC, LABEL, LANGUAGE, LYRICIST, MEDIA, PERFORMER,
RELEASECOUNTRY, REMIXER, TITLE, TRACKNUMBER.
* WAV: Fixed crash with files having the "id3 " chunk as the only valid chunk.
* Windows: Fixed support for files larger than 2GB.
TagLib 1.13.1 (Jul 1, 2023)
===========================
* Fixed parsing of TXXX frames without description.
* Detect MP4 atoms with invalid length or type.
* Do not miss ID3v2 frames when an extended header is present.
* Use property "DISCSUBTITLE" for ID3v2 "TSST" frame.
* Build system improvements: Use absolute path for macOS dylib install name,
support --define-prefix when using pkg-config, fixed minimum required
CppUnit version.
* Code clean up using clang-tidy.
TagLib 1.13 (Oct 27, 2022)
==========================
* Added interface StreamTypeResolver to support streams which cannot be
fopen()'ed, e.g. network files.
* Added MP4::File::strip() to remove meta atom from MP4 file.
* Added Map::value() to look up without creating entry.
* Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame,
use property "WORK" for ASF "WM/ContentGroupDescription",
use property "COMPILATION" for ID3v2 "TCMP" frame.
* Build system improvements: option WITH_ZLIB, BUILD_TESTING instead of
BUILD_TESTS, GNUInstallDirs, FeatureSummary, tests with BUILD_SHARED_LIBS,
cross compilation with Buildroot, systems without HAVE_GCC_ATOMIC, Clang.
* Fixed heap-buffer-overflows when handling ASF, APE, FLAC, ID3v2, MP4, MPC
tags.
* Fixed detection of invalid file by extension when correct type can be
detected by contents.
* Fixed unnecessary creation of map entries in APE and FLAC tags if looked up
tag does not exist.
* Fixed parsing of MP4 non-full meta atoms.
* Fixed potential ID3v1 false positive in the presence of an APE tag.
* Fixed ID3v2 version handling for frames embedded in CHAP or CTOC frames.
* Fixed parsing of multiple strings with a single BOM in ID3v2.4.0.
* Fixed several smaller issues reported by clang-tidy.
TagLib 1.12 (Feb 16, 2021)
==========================
* Added support for WinRT.
* Added support for Linux on POWER.
* Added support for classical music tags of iTunes 12.5.
* Added support for file descriptor to FileStream.
* Added support for 'cmID', 'purl', 'egid' MP4 atoms.
* Added support for 'GRP1' ID3v2 frame.
* Added support for extensible WAV subformat.
* Enabled FileRef to detect file types based on the stream content.
* Dropped support for Windows 9x and NT 4.0 or older.
* Check for mandatory header objects in ASF files.
* More tolerant handling of RIFF padding, WAV files, broken MPEG streams.
* Improved calculation of Ogg, Opus, Speex, WAV, MP4 bitrates.
* Improved Windows compatibility by storing FLAC picture after comments.
* Fixed numerical genres in ID3v2.3.0 'TCON' frames.
* Fixed consistency of API removing MP4 items when empty values are set.
* Fixed consistency of API preferring COMM frames with no description.
* Fixed OOB read on invalid Ogg FLAC files (CVE-2018-11439).
* Fixed handling of empty MPEG files.
* Fixed parsing MP4 mdhd timescale.
* Fixed reading MP4 atoms with zero length.
* Fixed reading FLAC files with zero-sized seektables.
* Fixed handling of lowercase field names in Vorbis Comments.
* Fixed handling of 'rate' atoms in MP4 files.
* Fixed handling of invalid UTF-8 sequences.
* Fixed possible file corruptions when saving Ogg files.
* Fixed handling of non-audio blocks, sampling rates, DSD audio in WavPack files.
* TableOfContentsFrame::toString() improved.
* UserTextIdentificationFrame::toString() improved.
* Marked FileRef::create() deprecated.
* Marked MPEG::File::save() with boolean parameters deprecated,
provide overloads with enum parameters.
* Several smaller bug fixes and performance improvements.
TagLib 1.11.1 (Oct 24, 2016)
============================
* Fixed binary incompatible change in TagLib::String.
* Fixed reading ID3v2 CTOC frames with a lot of entries.
* Fixed seeking ByteVectorStream from the end.
TagLib 1.11 (Apr 29, 2016)
==========================
1.11:
* Fixed reading APE items with long keys.
* Fixed reading ID3v2 SYLT frames when description is empty.
1.11 BETA 2:
* Better handling of PCM WAV files with a 'fact' chunk.
* Better handling of corrupted APE tags.
* Efficient decoding of unsynchronized ID3v2 frames.
* Fixed text encoding when saving certain frames in ID3v2.3 tags.
* Fixed updating the size of RIFF files when removing chunks.
* Several smaller bug fixes and performance improvements.
1.11 BETA:
* New API for creating FileRef from IOStream.
* Added support for ID3v2 PCST and WFED frames.
* Added support for pictures in XiphComment.
* Added String::clear().
* Added FLAC::File::strip() for removing non-standard tags.
* Added alternative functions to XiphComment::removeField().
* Added BUILD_BINDINGS build option.
* Added ENABLE_CCACHE build option.
* Replaced ENABLE_STATIC build option with BUILD_SHARED_LIBS.
* Better handling of duplicate ID3v2 tags in all kinds of files.
* Better handling of duplicate tag chunks in WAV files.
* Better handling of duplicate tag chunks in AIFF files.
* Better handling of duplicate Vorbis comment blocks in FLAC files.
* Better handling of broken MPEG audio frames.
* Fixed crash when calling File::properties() after strip().
* Fixed crash when parsing certain MPEG files.
* Fixed crash when saving Ogg files.
* Fixed possible file corruptions when saving ASF files.
* Fixed possible file corruptions when saving FLAC files.
* Fixed possible file corruptions when saving MP4 files.
* Fixed possible file corruptions when saving MPEG files.
* Fixed possible file corruptions when saving APE files.
* Fixed possible file corruptions when saving Musepack files.
* Fixed possible file corruptions when saving WavPack files.
* Fixed updating the comment field of Vorbis comments.
* Fixed reading date and time in ID3v2.3 tags.
* Marked ByteVector::null and ByteVector::isNull() deprecated.
* Marked String::null and String::isNull() deprecated.
* Marked XiphComment::removeField() deprecated.
* Marked Ogg::Page::getCopyWithNewPageSequenceNumber() deprecated. It returns null.
* Marked custom integer types deprecated.
* Many smaller bug fixes and performance improvements.
TagLib 1.10 (Nov 11, 2015)
==========================
1.10:
* Added new options to the tagwriter example.
* Fixed self-assignment operator in some types.
* Fixed extraction of MP4 tag keys with an empty list.
1.10 BETA:
* New API for the audio length in milliseconds.
* Added support for ID3v2 ETCO and SYLT frames.
* Added support for album artist in PropertyMap API of MP4 files.
* Added support for embedded frames in ID3v2 CHAP and CTOC frames.
* Added support for AIFF-C files.
* Better handling of duplicate ID3v2 tags in MPEG files.
* Allowed generating taglib.pc on Windows.
* Added ZLIB_SOURCE build option.
* Fixed backwards-incompatible change in TagLib::String when constructing UTF16 strings.
* Fixed crash when parsing certain FLAC files.
* Fixed crash when encoding empty strings.
* Fixed saving of certain XM files on OS X.
* Changed Xiph and APE generic getters to return space-concatenated values.
* Fixed possible file corruptions when removing tags from WAV files.
* Added support for MP4 files with 64-bit atoms in certain 64-bit environments.
* Prevented ID3v2 padding from being too large.
* Fixed crash when parsing corrupted APE files.
* Fixed crash when parsing corrupted WAV files.
* Fixed crash when parsing corrupted Ogg FLAC files.
* Fixed crash when parsing corrupted MPEG files.
* Fixed saving empty tags in WAV files.
* Fixed crash when parsing corrupted Musepack files.
* Fixed possible memory leaks when parsing AIFF and WAV files.
* Fixed crash when parsing corrupted MP4 files.
* Stopped writing empty ID3v2 frames.
* Fixed possible file corruptions when saving WMA files.
* Added TagLib::MP4::Tag::isEmpty().
* Added accessors to manipulate MP4 tags.
* Fixed crash when parsing corrupted WavPack files.
* Fixed seeking MPEG frames.
* Fixed reading FLAC files with zero-sized padding blocks.
* Added support for reading the encoder information of WMA files.
* Added support for reading the codec of WAV files.
* Added support for multi channel WavPack files.
* Added support for reading the nominal bitrate of Ogg Speex files.
* Added support for VBR headers in MPEG files.
* Marked FLAC::File::streamInfoData() deprecated. It returns an empty ByteVector.
* Marked FLAC::File::streamLength() deprecated. It returns zero.
* Fixed possible file corruptions when adding an ID3v1 tag to FLAC files.
* Many smaller bug fixes and performance improvements.
TagLib 1.9.1 (Oct 8, 2013)
==========================
* Fixed binary incompatible change in TagLib::Map and TagLib::List.
* Fixed constructing String from ByteVector.
* Fixed compilation on MSVC with the /Zc:wchar_t- option.
* Fixed detecting of RIFF files with invalid chunk sizes.
* Added TagLib::MP4::Properties::codec().
TagLib 1.9 (Oct 6, 2013)
========================
* Added support for the Ogg Opus file format.
* Added support for INFO tags in WAV files.
* Changed FileStream to use Windows file API.
* Included taglib-config.cmd script for Windows.
* New ID3v1::Tag methods for working directly with genre numbers.
* New MPEG::File methods for checking which tags are saved in the file.
* Added support for the PropertyMap API to ASF and MP4 files.
* Added MusicBrainz identifiers to the PropertyMap API.
* Allowed reading of MP4 cover art without an explicitly specified format.
* Better parsing of corrupted FLAC files.
* Fixed saving of PropertyMap comments without description into ID3v2 tags.
* Fixed crash when parsing certain XM files.
* Fixed compilation of unit test with clang.
* Better handling of files that can't be open or have read-only permissions.
* Improved atomic reference counting.
* New hookable API for debug messages.
* More complete Windows install instructions.
* Many smaller bug fixes and performance improvements.
TagLib 1.8 (Sep 6, 2012)
========================
1.8:
* Added support for OWNE ID3 frames.
* Changed key validation in the new PropertyMap API.
* ID3v1::Tag::setStringHandler will no longer delete the previous handler,
the caller is responsible for this.
* File objects will also no longer delete the passed IOStream objects. It
should be done in the caller code after the File object is no longer
used.
* Added ID3v2::Tag::setLatin1StringHandler for custom handling of
latin1-encoded text in ID3v2 frames.
* Fixed validation of ID3v2 frame IDs (IDs with '0' were ignored).
1.8 BETA:
* New API for accessing tags by name.
* New abstract I/O stream layer to allow custom I/O handlers.
* Support for writing ID3v2.3 tags.
* Support for various module file formats (MOD, S3M, IT, XM).
* Support for MP4 and ASF is now enabled by default.
* Started using atomic int operations for reference counting.
* Added methods for checking if WMA and MP4 files are DRM-protected.
* Added taglib_free to the C bindings.
* New method to allow removing pictures from FLAC files.
* Support for reading audio properties from ALAC and Musepack SV8 files.
* Added replay-gain information to Musepack audio properties.
* Support for APEv2 binary tags.
* Many AudioProperties subclasses now provide information about the total number of samples.
* Various small bug fixes.
TagLib 1.7.2 (Apr 20, 2012)
===========================
* Fixed division by zero while parsing corrupted MP4 files (CVE-2012-2396).
* Fixed compilation on Haiku.
TagLib 1.7.1 (Mar 17, 2012)
===========================
* Improved parsing of corrupted WMA, RIFF and OGG files.
* Fixed a memory leak in the WMA parser.
* Fixed a memory leak in the FLAC parser.
* Fixed a possible division by zero in the APE parser.
* Added detection of TTA2 files.
* Fixed saving of multiple identically named tags to Vorbis Comments.
TagLib 1.7 (Mar 11, 2011)
=========================
1.7:
* Fixed memory leaks in the FLAC file format parser.
* Fixed bitrate calculation for WAV files.
1.7 RC1:
* Support for reading/writing tags from Monkey's Audio files. (BUG:210404)
* Support for reading/writing embedded pictures from WMA files.
* Support for reading/writing embedded pictures from FLAC files (BUG:218696).
* Implemented APE::Tag::isEmpty() to check for all APE tags, not just the
basic ones.
* Added reading of WAV audio length. (BUG:116033)
* Exposed FLAC MD5 signature of the uncompressed audio stream via
FLAC::Properties::signature(). (BUG:160172)
* Added function ByteVector::toHex() for hex-encoding of byte vectors.
* WavPack reader now tries to get the audio length by finding the final
block, if the header doesn't have the information. (BUG:258016)
* Fixed a memory leak in the ID3v2.2 PIC frame parser. (BUG:257007)
* Fixed writing of RIFF files with even chunk sizes. (BUG:243954)
* Fixed compilation on MSVC 2010.
* Removed support for building using autoconf/automake.
* API docs can be now built using "make docs".
TagLib 1.6.3 (Apr 17, 2010)
===========================
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
* New method `int String::toInt(bool *ok)` which can return whether the
conversion to a number was successful.
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
compressed frames). (BUG:231075)
TagLib 1.6.2 (Apr 9, 2010)
==========================
* Read Vorbis Comments from the first FLAC metadata block, if there are
multiple ones. (BUG:211089)
* Fixed a memory leak in FileRef's OGA format detection.
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
(BUG:218526)
* More strict check if something is a valid MP4 file. (BUG:216819)
* Correctly save MP4 int-pair atoms with flags set to 0.
* Fixed compilation of the test runner on Windows.
* Store ASF attributes larger than 64k in the metadata library object.
* Ignore trailing non-data atoms when parsing MP4 covr atoms.
* Don't upgrade ID3v2.2 frame TDA to TDRC. (BUG:228968)
TagLib 1.6.1 (Oct 31, 2009)
===========================
* Better detection of the audio codec of .oga files in FileRef.
* Fixed saving of Vorbis comments to Ogg FLAC files. TagLib tried to
include the Vorbis framing bit, which is only correct for Ogg Vorbis.
* Public symbols now have explicitly set visibility to "default" on GCC.
* Added missing exports for static ID3v1 functions.
* Fixed a typo in taglib_c.pc
* Fixed a failing test on ppc64.
* Support for binary 'covr' atom in MP4 files. TagLib 1.6 treated them
as text atoms, which corrupted them in some cases.
* Fixed ID3v1-style genre to string conversion in MP4 files.
TagLib 1.6 (Sep 13, 2009)
=========================
1.6:
* New CMake option to build a static version - ENABLE_STATIC.
* Added support for disabling dllimport/dllexport on Windows using the
TAGLIB_STATIC macro.
* Support for parsing the obsolete 'gnre' MP4 atom.
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determine if
TagLib was built with MP4/ASF support.
1.6 RC1:
* Split Ogg packets larger than 64k into multiple pages. (BUG:171957)
* TagLib can now use FLAC padding block. (BUG:107659)
* ID3v2.2 frames are now not incorrectly saved. (BUG:176373)
* Support for ID3v2.2 PIC frames. (BUG:167786)
* Fixed a bug in ByteVectorList::split().
* XiphComment::year() now falls back to YEAR if DATE doesn't exist
and XiphComment::year() falls back to TRACKNUM if TRACKNUMBER doesn't
exist. (BUG:144396)
* Improved ID3v2.3 genre parsing. (BUG:188578)
* Better checking of corrupted ID3v2 APIC data. (BUG:168382)
* Bitrate calculating using the Xing header now uses floating point
numbers. (BUG:172556)
* New TagLib::String method rfind().
* Added support for MP4 file format with iTunes-style metadata [optional].
* Added support for ASF (WMA) file format [optional].
* Fixed crash when saving a Locator APEv2 tag. (BUG:169810)
* Fixed a possible crash in the non-const version of String::operator[]
and in String::operator+=. (BUG:169389)
* Added support for PRIV ID3v2 frames.
* Empty ID3v2 genres are no longer treated as numeric ID3v1 genres.
* Added support for the POPM (rating/play count) ID3v2 frame.
* Generic RIFF file format support:
* Support for AIFF files with ID3v2 tags.
* Support for WAV files with ID3v2 tags.
* Fixed crash on handling unsupported ID3v2 frames, e.g. on encrypted
frames. (BUG:161721)
* Fixed overflow while calculating bitrate of FLAC files with a very
high bitrate.

View File

@@ -1,250 +1,94 @@
cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
project(taglib)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
cmake_minimum_required(VERSION 2.6.0 FATAL_ERROR)
include(CTest)
include(FeatureSummary)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
if(APPLE)
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
if(BUILD_FRAMEWORK)
set(BUILD_SHARED_LIBS ON)
#set(CMAKE_MACOSX_RPATH 1)
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
endif()
endif()
option(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
option(ENABLE_CCACHE "Use ccache when building libtag" OFF)
if(ENABLE_CCACHE)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif()
option(ENABLE_STATIC "Make static version of libtag" OFF)
if(ENABLE_STATIC)
add_definitions(-DTAGLIB_STATIC)
set(BUILD_SHARED_LIBS OFF)
else()
set(BUILD_SHARED_LIBS ON)
endif()
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
option(BUILD_EXAMPLES "Build the examples" OFF)
option(BUILD_BINDINGS "Build the bindings" ON)
option(BUILD_TESTS "Build the test suite" OFF)
option(BUILD_EXAMPLES "Build the examples" OFF)
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
option(PLATFORM_WINRT "Enable WinRT support" OFF)
if(PLATFORM_WINRT)
add_definitions(-DPLATFORM_WINRT)
endif()
set(TAGLIB_INSTALL_SUFFIX "" CACHE STRING
"Suffix added to installed files (include directory, libraries, .pc)")
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
add_definitions(-DHAVE_CONFIG_H)
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/" CACHE STRING
"Tests directory, is path to unit test data when 'data' is appended")
set(TESTS_TMPDIR "" CACHE STRING
"Directory for temporary files created during unit tests, system tmpdir is used if undefined")
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
if(CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
## the following are directories where stuff will be installed to
set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries" FORCE)
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)" FORCE)
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE)
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix" FORCE)
if(APPLE)
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
endif()
if(MSVC)
if(ENABLE_STATIC_RUNTIME)
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endforeach(flag_var)
endif()
endif()
set(TAGLIB_LIB_MAJOR_VERSION "1")
set(TAGLIB_LIB_MINOR_VERSION "7")
set(TAGLIB_LIB_PATCH_VERSION "0")
# Read version information from file taglib/toolkit/taglib.h into variables
# TAGLIB_LIB_MAJOR_VERSION, TAGLIB_LIB_MINOR_VERSION, TAGLIB_LIB_PATCH_VERSION.
foreach(version_part MAJOR MINOR PATCH)
set(version_var_name "TAGLIB_${version_part}_VERSION")
file(STRINGS taglib/toolkit/taglib.h version_line
REGEX "^#define +${version_var_name}")
if(NOT version_line)
message(FATAL_ERROR "${version_var_name} not found in taglib.h")
endif()
string(REGEX MATCH "${version_var_name} +([^ ]+)" result ${version_line})
set(TAGLIB_LIB_${version_part}_VERSION ${CMAKE_MATCH_1})
endforeach(version_part)
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
# Only used to force cmake rerun when taglib.h changes.
configure_file(taglib/toolkit/taglib.h ${CMAKE_CURRENT_BINARY_DIR}/taglib.h.stamp)
# 1. If the library source code has changed at all since the last update, then increment revision.
# 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
# 3. If any interfaces have been added since the last public release, then increment age.
# 4. If any interfaces have been removed since the last public release, then set age to 0.
set(TAGLIB_SOVERSION_CURRENT 11)
set(TAGLIB_SOVERSION_REVISION 0)
set(TAGLIB_SOVERSION_AGE 10)
if("${TAGLIB_LIB_PATCH_VERSION}" EQUAL "0")
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}")
else()
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
endif()
# Major version: increase it if you break ABI compatibility.
# Minor version: increase it if you add ABI compatible features.
# Patch version: increase it for bug fix releases.
set(TAGLIB_SOVERSION_MAJOR 2)
set(TAGLIB_SOVERSION_MINOR 1)
set(TAGLIB_SOVERSION_PATCH 1)
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
include(ConfigureChecks.cmake)
option(WITH_APE "Build with APE, MPC, WavPack" ON)
option(WITH_ASF "Build with ASF" ON)
option(WITH_DSF "Build with DSF" ON)
option(WITH_MOD "Build with Tracker modules" ON)
option(WITH_MP4 "Build with MP4" ON)
option(WITH_RIFF "Build with AIFF, RIFF, WAV" ON)
option(WITH_SHORTEN "Build with Shorten" ON)
option(WITH_TRUEAUDIO "Build with TrueAudio" ON)
option(WITH_VORBIS "Build with Vorbis, FLAC, Ogg, Opus" ON)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib-config )
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/taglib-config DESTINATION ${BIN_INSTALL_DIR})
# Determine whether zlib is installed.
option(WITH_ZLIB "Build with ZLIB" ON)
if(WITH_ZLIB)
find_package("ZLIB")
set(HAVE_ZLIB ${ZLIB_FOUND})
if(ZLIB_FOUND)
set(ZLIB_LIBRARIES_FLAGS -lz)
if(NOT BUILD_SHARED_LIBS)
# When linking TagLib statically, zlib has to be linked explicitly.
set(ZLIB_INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
endif()
endif()
endif()
if(NOT WIN32)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" @ONLY)
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${CMAKE_INSTALL_BINDIR}"
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config")
endif()
if(WIN32)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmd.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${CMAKE_INSTALL_BINDIR}"
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config.cmd")
endif()
if(NOT BUILD_FRAMEWORK)
if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
else()
set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
endif()
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR})
else()
set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}.pc")
if(NOT WIN32 AND NOT BUILD_FRAMEWORK)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib.pc )
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
configure_file(config-taglib.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
if(TRACE_IN_RELEASE)
set(TRACE_IN_RELEASE TRUE)
endif()
find_package(utf8cpp QUIET)
if(utf8cpp_FOUND)
message(STATUS "Using utfcpp ${utf8cpp_VERSION} from ${utf8cpp_CONFIG}")
else()
find_path(utf8cpp_INCLUDE_DIR NAMES utf8.h PATH_SUFFIXES utf8cpp
DOC "utf8cpp include directory")
mark_as_advanced(utf8cpp_INCLUDE_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(utf8cpp REQUIRED_VARS utf8cpp_INCLUDE_DIR)
if(utf8cpp_FOUND)
set(utf8cpp_INCLUDE_DIRS "${utf8cpp_INCLUDE_DIR}")
if(NOT TARGET utf8::cpp)
add_library(utf8::cpp INTERFACE IMPORTED)
set_target_properties(utf8::cpp PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${utf8cpp_INCLUDE_DIR}")
endif()
message(STATUS "Using utfcpp from ${utf8cpp_INCLUDE_DIR}")
else()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/utfcpp/CMakeLists.txt)
add_subdirectory("3rdparty/utfcpp")
message(STATUS "Using utfcpp from ${utf8cpp_SOURCE_DIR}")
else()
message(FATAL_ERROR
"utfcpp not found. Either install package (probably utfcpp, utf8cpp, or libutfcpp-dev) "
"or fetch the git submodule using\n"
"git submodule update --init")
endif()
endif()
endif()
if(WITH_APE)
set(TAGLIB_WITH_APE TRUE)
endif()
if(WITH_ASF)
set(TAGLIB_WITH_ASF TRUE)
endif()
if(WITH_DSF)
set(TAGLIB_WITH_DSF TRUE)
endif()
if(WITH_MOD)
set(TAGLIB_WITH_MOD TRUE)
set(TAGLIB_WITH_ASF TRUE)
endif()
if(WITH_MP4)
set(TAGLIB_WITH_MP4 TRUE)
endif()
if(WITH_RIFF)
set(TAGLIB_WITH_RIFF TRUE)
endif()
if(WITH_SHORTEN)
set(TAGLIB_WITH_SHORTEN TRUE)
endif()
if(WITH_TRUEAUDIO)
set(TAGLIB_WITH_TRUEAUDIO TRUE)
endif()
if(WITH_VORBIS)
set(TAGLIB_WITH_VORBIS TRUE)
set(TAGLIB_WITH_MP4 TRUE)
endif()
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
configure_file(taglib/taglib_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h)
add_subdirectory(taglib)
add_subdirectory(taglib)
add_subdirectory(bindings)
add_subdirectory(tests)
add_subdirectory(examples)
if(BUILD_BINDINGS)
add_subdirectory(bindings)
endif()
if(BUILD_TESTING)
find_package(CppUnit)
if(CppUnit_FOUND)
add_subdirectory(tests)
else()
message(WARNING "BUILD_TESTING requested, but CppUnit not found, skipping tests.")
endif()
endif()
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
file(COPY doc/taglib.png DESTINATION doc)
add_custom_target(docs doxygen)
# uninstall target
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
if(NOT TARGET uninstall)
add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
endif()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")

View File

@@ -1,109 +1,23 @@
include(CheckIncludeFile)
include(CheckIncludeFiles)
include(CheckSymbolExists)
include(CheckFunctionExists)
include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckCXXSourceCompiles)
# Check if the size of numeric types are suitable.
check_type_size("short" SIZEOF_SHORT)
if(NOT ${SIZEOF_SHORT} EQUAL 2)
message(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
# check for libz using the cmake supplied FindZLIB.cmake
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
else()
set(HAVE_ZLIB 0)
endif()
check_type_size("int" SIZEOF_INT)
if(NOT ${SIZEOF_INT} EQUAL 4)
message(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
find_package(CppUnit)
if(NOT CppUnit_FOUND AND BUILD_TESTS)
message(STATUS "CppUnit not found, disabling tests.")
set(BUILD_TESTS OFF)
endif()
check_type_size("long long" SIZEOF_LONGLONG)
if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
endif()
check_type_size("wchar_t" SIZEOF_WCHAR_T)
if(NOT ${SIZEOF_WCHAR_T} GREATER 1)
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
endif()
check_type_size("float" SIZEOF_FLOAT)
if(NOT ${SIZEOF_FLOAT} EQUAL 4)
message(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
endif()
check_type_size("double" SIZEOF_DOUBLE)
if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
endif()
# Determine which kind of byte swap functions your compiler supports.
check_cxx_source_compiles("
int main() {
__builtin_bswap16(0);
__builtin_bswap32(0);
__builtin_bswap64(0);
return 0;
}
" HAVE_GCC_BYTESWAP)
if(NOT HAVE_GCC_BYTESWAP)
check_cxx_source_compiles("
#include <byteswap.h>
int main() {
__bswap_16(0);
__bswap_32(0);
__bswap_64(0);
return 0;
}
" HAVE_GLIBC_BYTESWAP)
if(NOT HAVE_GLIBC_BYTESWAP)
check_cxx_source_compiles("
#include <cstdlib>
int main() {
_byteswap_ushort(0);
_byteswap_ulong(0);
_byteswap_uint64(0);
return 0;
}
" HAVE_MSC_BYTESWAP)
if(NOT HAVE_MSC_BYTESWAP)
check_cxx_source_compiles("
#include <libkern/OSByteOrder.h>
int main() {
OSSwapInt16(0);
OSSwapInt32(0);
OSSwapInt64(0);
return 0;
}
" HAVE_MAC_BYTESWAP)
if(NOT HAVE_MAC_BYTESWAP)
check_cxx_source_compiles("
#include <sys/endian.h>
int main() {
swap16(0);
swap32(0);
swap64(0);
return 0;
}
" HAVE_OPENBSD_BYTESWAP)
endif()
endif()
endif()
endif()
# Determine whether your compiler supports ISO _strdup.
check_cxx_source_compiles("
#include <cstring>
int main() {
_strdup(0);
return 0;
}
" HAVE_ISO_STRDUP)
# Detect WinRT mode
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
set(PLATFORM_WINRT 1)
endif()

View File

@@ -1,78 +1,37 @@
# Doxyfile 1.9.1
# Doxyfile 1.3.4
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = TagLib
PROJECT_NUMBER = ${TAGLIB_LIB_VERSION_STRING}
PROJECT_BRIEF =
PROJECT_LOGO = @CMAKE_SOURCE_DIR@/doc/taglib.svg
OUTPUT_DIRECTORY = doc
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
USE_WINDOWS_ENCODING = NO
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
STRIP_FROM_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
JAVADOC_BANNER = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
PYTHON_DOCSTRING = YES
DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
DISTRIBUTE_GROUP_DOC = NO
TAB_SIZE = 4
ALIASES =
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
OPTIMIZE_OUTPUT_SLICE = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
EXTRACT_PRIV_VIRTUAL = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = NO
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
RESOLVE_UNNAMED_PARAMS = YES
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
@@ -80,264 +39,172 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = YES
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
SHOW_INCLUDE_FILES = YES
SHOW_GROUPED_MEMB_INC = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = NO
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
GENERATE_DEPRECATEDLIST= NO
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_FILES = YES
SHOW_NAMESPACES = YES
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = NO
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_AS_ERROR = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = @CMAKE_SOURCE_DIR@/taglib
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.h \
*.hh \
*.H \
*.dox
*.H
RECURSIVE = YES
EXCLUDE =
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXCLUDE_PATTERNS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
IMAGE_PATH =
INPUT_FILTER =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# Configuration options related to source browsing
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
REFERENCES_LINK_SOURCE = YES
SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
CLANG_ASSISTED_PARSING = NO
CLANG_ADD_INC_PATHS = YES
CLANG_OPTIONS =
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
IGNORE_PREFIX =
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER = @CMAKE_SOURCE_DIR@/doc/api-header.html
HTML_FOOTER = @CMAKE_SOURCE_DIR@/doc/api-footer.html
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
HTML_STYLESHEET = @CMAKE_SOURCE_DIR@/doc/taglib-api.css
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
DISABLE_INDEX = NO
GENERATE_TREEVIEW = NO
DISABLE_INDEX = YES
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
FORMULA_MACROFILE =
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = NO
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
LATEX_MAKEINDEX_CMD = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = letter
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
LATEX_EXTRA_STYLESHEET =
LATEX_EXTRA_FILES =
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# Configuration options related to the man page output
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_SUBDIR =
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
XML_NS_MEMB_FILE_SCOPE = NO
XML_SCHEMA =
XML_DTD =
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = DO_NOT_DOCUMENT \
DOXYGEN
EXPAND_AS_DEFINED =
DOXYGEN \
WITH_MP4 \
WITH_ASF
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
DIA_PATH =
CLASS_DIAGRAMS = YES
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
DOT_UML_DETAILS = NO
DOT_WRAP_THRESHOLD = 17
TEMPLATE_RELATIONS = YES
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = svg
INTERACTIVE_SVG = NO
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_CFG_FILE =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 100
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
MAX_DOT_GRAPH_WIDTH = 1024
MAX_DOT_GRAPH_HEIGHT = 1024
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO

43
INSTALL Normal file
View File

@@ -0,0 +1,43 @@
TagLib Installation
===================
TagLib uses the CMake build system. As a user, you will most likely want to
build TagLib in release mode and install it into a system-wide location.
This can be done using the following commands:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_RELEASE_TYPE=Release .
make
sudo make install
In order to build the included examples, use the BUILD_EXAMPLES option:
cmake -DBUILD_EXAMPLES=ON [...]
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
running CMake.
Mac OS X Framework
------------------
On Mac OS X, you might want to build a framework that can be easily integrated
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
TagLib as a framework. For example, the following command can be used to build
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
cmake -DCMAKE_BUILD_TYPE=Release \
-DBUILD_FRAMEWORK=ON \
-DCMAKE_C_COMPILER=/usr/bin/gcc-4.0 \
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
-DCMAKE_OSX_ARCHITECTURES="ppc;i368;x86_64"
Unit Tests
----------
If you want to run the test suite to make sure TagLib works properly on your
system, you need to have cppunit installed. The test suite has a custom target
in the build system, so you can run the tests using make:
make check

View File

@@ -1,518 +0,0 @@
# TagLib Installation
TagLib uses the CMake build system. As a user, you will most likely want to
build TagLib in release mode and install it into a system-wide location.
This can be done using the following commands:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release .
make
sudo make install
In order to build the included examples, use the `BUILD_EXAMPLES` option:
cmake -DBUILD_EXAMPLES=ON [...]
If you want to build TagLib without ZLib, you can use
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DWITH_ZLIB=OFF .
make
sudo make install
See [cmake(1)](https://cmake.org/cmake/help/latest/manual/cmake.1.html) for
generic help on running CMake.
## Build Options
These are the most important build options. For details, have a look into the
CMakeLists.txt file.
| Option | Description |
|-------------------------|----------------------------------------------------|
| `BUILD_SHARED_LIBS` | Build shared libraries |
| `CMAKE_BUILD_TYPE` | Debug, Release, RelWithDebInfo, MinSizeRel |
| `BUILD_EXAMPLES` | Build examples |
| `BUILD_BINDINGS` | Build C bindings |
| `BUILD_TESTING` | Build unit tests |
| `TRACE_IN_RELEASE` | Enable debug output in release builds |
| `WITH_ZLIB` | Whether to build with ZLib (default ON) |
| `ZLIB_ROOT` | Where to find ZLib's root directory |
| `ZLIB_INCLUDE_DIR` | Where to find ZLib's include directory |
| `ZLIB_LIBRARY` | Where to find ZLib's library |
| `CMAKE_INSTALL_PREFIX` | Where to install Taglib |
| `TAGLIB_INSTALL_SUFFIX` | Suffix added to installed libraries, includes, ... |
| `ENABLE_STATIC_RUNTIME` | Link with MSVC runtime statically |
| `BUILD_FRAMEWORK` | Build a macOS framework |
| `TESTS_DIR` | Where to find unit test data (with data appended) |
| `TESTS_TMPDIR` | Where to create temporary files in unit tests |
If you want to install TagLib 2 alongside TagLib 1, you can use
`-DTAGLIB_INSTALL_SUFFIX=-2` and make sure that `BUILD_EXAMPLES` is not `ON`
for both versions. The installed files will then include bin/taglib-2-config,
include/taglib-2, cmake/taglib-2, pkgconfig/taglib-2.pc,
pkgconfig/taglib_c-2.pc and the libraries have a suffix "-2".
### Compile Time Configuration of Supported Formats
To reduce the size of the library, it is possible to switch off supported file
formats. By default, all formats are enabled. Support for MPEG files (MP3, AAC)
and ID3 tags cannot be disabled. The following CMake options are available:
| Option | Description |
|-------------------------|----------------------------------------------------|
| `WITH_APE` | Build with APE, MPC, WavPack (default ON) |
| `WITH_ASF` | Build with ASF (default ON) |
| `WITH_DSF` | Build with DSF (default ON) |
| `WITH_MOD` | Build with Tracker modules (default ON) |
| `WITH_MP4` | Build with MP4 (default ON) |
| `WITH_RIFF` | Build with AIFF, RIFF, WAV (default ON) |
| `WITH_SHORTEN` | Build with Shorten (default ON) |
| `WITH_TRUEAUDIO` | Build with TrueAudio (default ON) |
| `WITH_VORBIS` | Build with Vorbis, FLAC, Ogg, Opus (default ON) |
Note that disabling formats will remove exported symbols from the library and
thus break binary compatibility. These options should therefore only be used
if the library is built specifically for a certain project. The public header
files still contain the full API, if you use TagLib with a reduced set of
formats, you can include taglib_config.h and use its definitions (prefixed with
`TAGLIB_`, e.g. `TAGLIB_WITH_APE`), as it is done in examples/framelist.cpp.
## Dependencies
A required dependency is [utf8cpp](https://github.com/nemtrif/utfcpp). You can
install the corresponding package (libutfcpp-dev on Ubuntu, utf8cpp in Homebrew,
utfcpp in vcpkg) or fetch the Git submodule with `git submodule update --init`.
Optional dependencies are
- [zlib](https://www.zlib.net/): You can disable it with `-DWITH_ZLIB=OFF`,
build and install it from the sources or use a package (zlib1g-dev on Ubuntu,
zlib in vcpkg). It is needed for compressed ID3v2 frames.
- [CppUnit](https://wiki.documentfoundation.org/Cppunit): Is required for unit
tests, which are disabled by default. If you enable them with
`-DBUILD_TESTING=ON`, you can build and install it from the sources or use a
package (libcppunit-dev on Ubuntu, cppunit in Homebrew, cppunit in vcpkg).
If the unit tests are enabled, you can run them with your build tool
(`make check`, `ninja check`) or via CMake
`cmake --build /path/to/build-dir --target check`.
## UNIX (Including Linux, BSD and macOS)
#### Building TagLib
On Linux, you can install the dependencies using the package manager of your
distribution. On macOS with Homebrew, you can use `brew install cppunit utf8cpp`
to install the dependencies.
```
# Adapt these environment variables to your directories
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
TAGLIB_DST_DIR=$HOME/projects/taglib/src/build
cd $TAGLIB_SRC_DIR
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build $TAGLIB_DST_DIR --config Release
cmake --build $TAGLIB_DST_DIR --config Release --target check
# Install to ~/pkg folder
cmake --install $TAGLIB_DST_DIR --config Release --prefix $HOME/pkg --strip
# Run example from installed package
LD_LIBRARY_PATH=$HOME/pkg/lib $HOME/pkg/bin/tagreader /path/to/audio-file
```
#### Building a Project Using TagLib
As an example for an external application using TagLib, we create a folder
taglib-client and copy the file tagreader.cpp from the examples folder
of the TagLib sources into it. We then add this simple CMakeLists.txt.
```
cmake_minimum_required(VERSION 3.5.0)
project(taglib-client)
set(CMAKE_CXX_STANDARD 17)
find_package(ZLIB)
find_package(TagLib 2.0.0 REQUIRED)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader TagLib::tag)
```
To build into a folder `build` inside this directory, just run
```
# Set this to the path where TagLib is installed
TAGLIB_PREFIX=$HOME/pkg
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX
cmake --build build --config Release
build/tagreader /path/to/audio-file
```
If TagLib is installed in a standard location (e.g. /usr, /usr/local), its CMake
configuration is found without setting CMAKE_INSTALL_PREFIX (or alternatives
like CMAKE_PREFIX_PATH, CMAKE_SYSTEM_PREFIX_PATH).
To use the C-bindings with tagreader_c.c, you can change the last two lines in
CMakeLists.txt to
```
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c TagLib::tag_c)
```
#### Building a Project Using pkg-config
Before version 2.0, TagLib did not export CMake configuration, therefore
`pkg-config` could be used. This is still possible.
Note, however, that `pkg-config` makes it more difficult to use packages which
are not installed in a standard location. You can point `pkg-config` to search
in non-standard locations for .pc-files, but they still contain the install
locations defined when building TagLib. Since we did not give a
`CMAKE_INSTALL_PREFIX` in the example above, the default `/usr/local/` is used.
```
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --libs --cflags taglib
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
```
The following examples use the same build example with additional CMake
parameters affecting the installation location.
- Using the default prefix `-DCMAKE_INSTALL_PREFIX=/usr/local`:
```
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
```
- Using an absolute prefix `-DCMAKE_INSTALL_PREFIX=/usr`:
```
-I/usr/include/taglib -ltag -lz
```
- Using absolute lib and include directories
`-DCMAKE_INSTALL_LIBDIR=/abs-lib -DCMAKE_INSTALL_INCLUDEDIR=/abs-include -DCMAKE_INSTALL_PREFIX=/usr`:
```
-I/abs-include -I/abs-include/taglib -L/abs-lib -ltag -lz
```
- Using relative lib and include directories
`-DCMAKE_INSTALL_LIBDIR=rel-lib -DCMAKE_INSTALL_INCLUDEDIR=rel-include -DCMAKE_INSTALL_PREFIX=/usr`:
```
-I/usr/rel-include -I/usr/rel-include/taglib -L/usr/rel-lib -ltag -lz
```
This is the output of
```
PKG_CONFIG_PATH=$HOME/pkg/rel-lib/pkgconfig pkg-config --libs --cflags taglib
```
You could add `--define-prefix` to the `pkg-config` arguments to have the
effective location `$HOME/pkg/` instead of `/usr/` in the output.
Therefore, the correct paths for our example package would be given when using
the `--define-prefix` feature.
```
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --define-prefix --libs --cflags taglib
```
You can use pkg-config from CMake, however, relocation with `--define-prefix`
is not supported.
```
cmake_minimum_required(VERSION 3.6.0)
project(taglib-client)
find_package(PkgConfig)
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader PkgConfig::TAGLIB)
```
#### Framework on macOS
On macOS, you might want to build a framework that can be easily integrated
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
TagLib as a framework. For example, the following command can be used to build
a framework with macOS 10.10 as the deployment target:
mkdir build; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=OFF \
-DBUILD_FRAMEWORK=ON \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
make
For a 10.10 static library, use:
mkdir build; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
make
After `make`, and `make install`, add `libtag.` to your XCode project, and add
the include folder to the project's User Header Search Paths.
## Windows
### Using Visual Studio Build Tools
For this example, we assume that you have the Visual Studio Build Tools 2022
installed. Additionally, you need [cmake](https://cmake.org/), which can be
installed for example using [scoop](https://scoop.sh/) with
`scoop install cmake`.
#### Building TagLib (MSVC)
You can build and install the dependencies
[utf8cpp](https://github.com/nemtrif/utfcpp) and optionally
[zlib](https://www.zlib.net/) and
[CppUnit](https://wiki.documentfoundation.org/Cppunit) yourself, but it is
probably easier using a package manager such as [vcpkg](https://vcpkg.io/),
which can be installed using `scoop install vcpkg` and then install the
dependencies using `vcpkg install utfcpp zlib cppunit`.
Now you can build TagLib from PowerShell.
```
# Adapt these environment variables to your directories
$env:TAGLIB_SRC_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/taglib"
$env:TAGLIB_DST_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/msvs_vcpkg_build"
cd $env:TAGLIB_SRC_DIR
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON `
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-G "Visual Studio 17 2022"
cmake --build $env:TAGLIB_DST_DIR --config Release
# Add directories containing DLL dependencies to path
$env:Path += -join(";$env:TAGLIB_DST_DIR\taglib\Release;",
"$env:TAGLIB_DST_DIR\bindings\c\Release;",
"$env:VCPKG_ROOT\packages\cppunit_x64-windows\bin;",
"$env:VCPKG_ROOT\packages\utfcpp_x64-windows\bin;",
"$env:VCPKG_ROOT\packages\zlib_x64-windows\bin")
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
# Install to \pkg folder on current drive
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg --strip
# Static library
$env:TAGLIB_DST_DIR = "C:/Users/fle/projects/taglib/src/msvs_vcpkg_static_build"
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=OFF -DVISIBILITY_HIDDEN=ON `
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-G "Visual Studio 17 2022"
cmake --build $env:TAGLIB_DST_DIR --config Release
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
# Install to \pkg_static folder on current drive
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg_static --strip
```
Including `ENABLE_STATIC_RUNTIME=ON` indicates you want TagLib built using the
static runtime library, rather than the DLL form of the runtime.
#### Building a Project Using TagLib (MSVC)
As an example for an external application using TagLib, we create a folder
taglib-client and copy the file tagreader.cpp from the examples folder
of the TagLib sources into it. We then add this simple CMakeLists.txt.
```
cmake_minimum_required(VERSION 3.5.0)
project(taglib-client)
set(CMAKE_CXX_STANDARD 17)
find_package(ZLIB)
find_package(TagLib 2.0.0 REQUIRED)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader TagLib::tag)
```
To build into a folder build inside this directory, just run
```
# Set this to the path where TagLib is installed
$env:TAGLIB_PREFIX = "/pkg"
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$env:TAGLIB_PREFIX" `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-G "Visual Studio 17 2022"
cmake --build build --config Release
# Add directories containing DLL dependencies to path
$env:Path += ";$env:TAGLIB_PREFIX\bin;$env:VCPKG_ROOT\packages\zlib_x64-windows\bin"
build\Release\tagreader /path/to/audio-file
```
To use the C-bindings with tagreader_c.c, you can change the last two lines in
CMakeLists.txt to
```
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c TagLib::tag_c)
```
If you link against a static TagLib, you have to adapt the installation path
(e.g. "/pkg_static") and add the following line to CMakeLists.txt
```
target_compile_definitions(tagreader_c PRIVATE TAGLIB_STATIC)
```
### Using MSYS2
#### Building TagLib (MSYS2)
To build TagLib using Clang from MSYS, install [msys2](https://www.msys2.org/),
then start the MSYS CLANG64 shell and install the dependencies as they are
specified in the [MSYS recipe for TagLib](
https://packages.msys2.org/package/mingw-w64-clang-x86_64-taglib?repo=clang64).
```
pacman -Suy
pacman -S mingw-w64-clang-x86_64-gcc-libs mingw-w64-clang-x86_64-zlib \
mingw-w64-clang-x86_64-cc mingw-w64-clang-x86_64-cmake \
mingw-w64-clang-x86_64-cppunit mingw-w64-clang-x86_64-ninja
```
Then you can build TagLib from the MSYS CLANG64 Bash.
```
# Adapt these environment variables to your directories
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
TAGLIB_DST_DIR=$HOME/projects/taglib/src/msys_build
cd $TAGLIB_SRC_DIR
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
-DCMAKE_BUILD_TYPE=Release -G Ninja
cmake --build $TAGLIB_DST_DIR --config Release
PATH=$PATH:/clang64/bin:$TAGLIB_DST_DIR/taglib:$TAGLIB_DST_DIR/bindings/c \
cmake --build $TAGLIB_DST_DIR --config Release --target check
# Install to /pkg_msys folder inside MSYS root (e.g. C:/msys64/pkg_msys/)
cmake --install $TAGLIB_DST_DIR --config Release --prefix /pkg_msys --strip
```
#### Building a Project Using TagLib (MSYS2)
As an example for an external application using TagLib, we create a folder
taglib-client and copy the file tagreader.cpp from the examples folder
of the TagLib sources into it. We then add this simple CMakeLists.txt.
```
cmake_minimum_required(VERSION 3.5.0)
project(taglib-client)
set(CMAKE_CXX_STANDARD 17)
find_package(ZLIB)
find_package(TagLib 2.0.0 REQUIRED)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader TagLib::tag)
```
To build into a folder build_msys inside this directory, just run
```
# Set this to the path where TagLib is installed
TAGLIB_PREFIX=/pkg_msys
cmake -B build_msys -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G Ninja
cmake --build build_msys --config Release
PATH=$PATH:$TAGLIB_PREFIX/bin
build_msys/tagreader /path/to/audio-file
```
To use the C-bindings with tagreader_c.c, you can change the last two lines in
CMakeLists.txt to
```
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c TagLib::tag_c)
```
### Using MinGW
The instructions for MSYS2 can also be used to build with MinGW. You could use
Git Bash together with the MinGW provided by Qt.
```
# Adapt these environment variables to your directories
PATH=$PATH:/c/Qt/Tools/mingw1120_64/bin
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
TAGLIB_DST_DIR=$HOME/projects/taglib/src/mingw_build
cd $TAGLIB_SRC_DIR
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
-DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DWITH_ZLIB=OFF \
-DCMAKE_BUILD_TYPE=Release -G 'MinGW Makefiles'
cmake --build $TAGLIB_DST_DIR --config Release
PATH=$PATH:$TAGLIB_DST_DIR/taglib \
$TAGLIB_DST_DIR/examples/tagreader /path/to/audio-file
# Install to C:\pkg_mingw
cmake --install $TAGLIB_DST_DIR --config Release --prefix /c/pkg_mingw --strip
```
The installed package can then be used by other projects.
```
TAGLIB_PREFIX=/c/pkg_mingw
cmake -B build_mingw -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G 'MinGW Makefiles'
cmake --build build_mingw --config Release
PATH=$PATH:$TAGLIB_PREFIX/bin
build_mingw/tagreader /path/to/audio-file
```
## Android
### Using vcpkg
The bash script below can be used to build TagLib for Android using vcpkg.
It must be started in the parent folder of the taglib source folder and will
build in a folder _android_build_ and install into _android_pkg_.
The package and the unit tests are then transferred to an Android device
and the unit tests are run on the device.
Note that `TESTS_TMPDIR` is set because there is no system-wide temporary folder
on Android. `TESTS_DIR` is set to run the tests on the target.
```
# You may have to adapt the NDK and vcpkg paths and the ABI/triplet.
export ANDROID_NDK_HOME=$HOME/Development/android-sdk/ndk/23.1.7779620
export VCPKG_ROOT=$HOME/Development/vcpkg
PATH=$PATH:$VCPKG_ROOT
# armeabi-v7a/arm-android or arm64-v8a/arm64-android or x86/x86-android or x86_64/x64-android
android_abi=armeabi-v7a
vcpkg_target_triplet=arm-android
vcpkg_toolchain_file=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
android_toolchain_file=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake
vcpkg install --triplet $vcpkg_target_triplet utfcpp zlib cppunit
cmake -B android_build -S taglib \
-DCMAKE_TOOLCHAIN_FILE=$vcpkg_toolchain_file \
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$android_toolchain_file \
-DVCPKG_TARGET_TRIPLET=$vcpkg_target_triplet \
-DANDROID_ABI=$android_abi \
-GNinja -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release \
-DVISIBILITY_HIDDEN=ON -DENABLE_CCACHE=ON -DBUILD_EXAMPLES=ON -DBUILD_TESTING=ON \
-DTESTS_DIR=/data/local/tmp/tests/ -DTESTS_TMPDIR=/data/local/tmp
cmake --build android_build --config Release
cmake --install android_build --config Release --prefix android_pkg --strip
cp -a android_build/tests/test_runner android_pkg/bin/
if hash adb 2>/dev/null; then
adb push android_pkg /data/local/tmp/
adb push android_build/tests/test_runner /data/local/tmp/tests/test_runner
adb push taglib/tests/data /data/local/tmp/tests/
adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/tests/test_runner"
# You could also try an example binary:
# adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/android_pkg/bin/tagreader '/sdcard/Music/Some Album/A Track.mp3'"
fi
```

117
NEWS Normal file
View File

@@ -0,0 +1,117 @@
TagLib 1.8 (In Development)
===========================
* Support for writing ID3v2.3 tags.
* Added methods for checking if WMA and MP4 files are DRM-protected.
* Started using atomic int operations for reference counting.
* Find APE tags even if there's a Lyrics3v2 tag present (BUG:254223).
TagLib 1.7 (Mar 11, 2011)
=========================
1.7:
* Fixed memory leaks in the FLAC file format parser.
* Fixed bitrate calculation for WAV files.
1.7 RC1:
* Support for reading/writing tags from Monkey's Audio files. (BUG:210404)
* Support for reading/writing embedded pictures from WMA files.
* Support for reading/writing embedded pictures from FLAC files (BUG:218696).
* Implemented APE::Tag::isEmpty() to check for all APE tags, not just the
basic ones.
* Added reading of WAV audio length. (BUG:116033)
* Exposed FLAC MD5 signature of the uncompressed audio stream via
FLAC::Properties::signature(). (BUG:160172)
* Added function ByteVector::toHex() for hex-encoding of byte vectors.
* WavPack reader now tries to get the audio length by finding the final
block, if the header doesn't have the information. (BUG:258016)
* Fixed a memory leak in the ID3v2.2 PIC frame parser. (BUG:257007)
* Fixed writing of RIFF files with even chunk sizes. (BUG:243954)
* Fixed compilation on MSVC 2010.
* Removed support for building using autoconf/automake.
* API docs can be now built using "make docs".
TagLib 1.6.3 (Apr 17, 2010)
===========================
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
* New method `int String::toInt(bool *ok)` which can return whether the
conversion to a number was successfull.
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
compressed frames). (BUG:231075)
TagLib 1.6.2 (Apr 9, 2010)
==========================
* Read Vorbis Comments from the first FLAC metadata block, if there are
multipe ones. (BUG:211089)
* Fixed a memory leak in FileRef's OGA format detection.
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
(BUG:218526)
* More strict check if something is a valid MP4 file. (BUG:216819)
* Correctly save MP4 int-pair atoms with flags set to 0.
* Fixed compilation of the test runner on Windows.
* Store ASF attributes larger than 64k in the metadata library object.
* Ignore trailing non-data atoms when parsing MP4 covr atoms.
* Don't upgrade ID3v2.2 frame TDA to TDRC. (BUG:228968)
TagLib 1.6.1 (Oct 31, 2009)
===========================
* Better detection of the audio codec of .oga files in FileRef.
* Fixed saving of Vorbis comments to Ogg FLAC files. TagLib tried to
include the Vorbis framing bit, which is only correct for Ogg Vorbis.
* Public symbols now have explicitly set visibility to "default" on GCC.
* Added missing exports for static ID3v1 functions.
* Fixed a typo in taglib_c.pc
* Fixed a failing test on ppc64.
* Support for binary 'covr' atom in MP4 files. TagLib 1.6 treated them
as text atoms, which corrupted them in some cases.
* Fixed ID3v1-style genre to string conversion in MP4 files.
TagLib 1.6 (Sep 13, 2009)
=========================
1.6:
* New CMake option to build a static version - ENABLE_STATIC.
* Added support for disabling dllimport/dllexport on Windows using the
TAGLIB_STATIC macro.
* Support for parsing the obsolete 'gnre' MP4 atom.
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determin if
TagLib was built with MP4/ASF support.
1.6 RC1:
* Split Ogg packets larger than 64k into multiple pages. (BUG:171957)
* TagLib can now use FLAC padding block. (BUG:107659)
* ID3v2.2 frames are now not incorrectly saved. (BUG:176373)
* Support for ID3v2.2 PIC frames. (BUG:167786)
* Fixed a bug in ByteVectorList::split().
* XiphComment::year() now falls back to YEAR if DATE doesn't exist
and XiphComment::year() falls back to TRACKNUM if TRACKNUMBER doesn't
exist. (BUG:144396)
* Improved ID3v2.3 genre parsing. (BUG:188578)
* Better checking of corrupted ID3v2 APIC data. (BUG:168382)
* Bitrate calculating using the Xing header now uses floating point
numbers. (BUG:172556)
* New TagLib::String method rfind().
* Added support for MP4 file format with iTunes-style metadata [optional].
* Added support for ASF (WMA) file format [optional].
* Fixed crash when saving a Locator APEv2 tag. (BUG:169810)
* Fixed a possible crash in the non-const version of String::operator[]
and in String::operator+=. (BUG:169389)
* Added support for PRIV ID3v2 frames.
* Empty ID3v2 genres are no longer treated as numeric ID3v1 genres.
* Added support for the POPM (rating/playcount) ID3v2 frame.
* Generic RIFF file format support:
* Support for AIFF files with ID3v2 tags.
* Support for WAV files with ID3v2 tags.
* Fixed crash on handling unsupported ID3v2 frames, e.g. on encrypted
frames. (BUG:161721)
* Fixed overflow while calculating bitrate of FLAC files with a very
high bitrate.

View File

@@ -1,24 +0,0 @@
# TagLib
[![Build Status](../../actions/workflows/build.yml/badge.svg)](../../actions)
### TagLib Audio Metadata Library
https://taglib.org/
TagLib is a library for reading and editing the metadata of several
popular audio formats. Currently, it supports both ID3v1 and ID3v2
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE, ASF,
DSF, DFF and AAC files.
TagLib is distributed under the [GNU Lesser General Public License][]
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
it may be used in proprietary applications, but if changes are made to
TagLib they must be contributed back to the project. Please review the
licenses if you are considering using TagLib in your project.
[Ogg Vorbis]: https://xiph.org/vorbis/
[FLAC]: https://xiph.org/flac/
[GNU Lesser General Public License]: https://www.gnu.org/licenses/lgpl.html
[Mozilla Public License]: https://www.mozilla.org/MPL/MPL-1.1.html

View File

@@ -1 +1 @@
add_subdirectory(c)
add_subdirectory(c)

View File

@@ -2,4 +2,5 @@ There are a few other people that have done bindings externally that I have
been made aware of. I have not personally reviewed these bindings, but I'm
listing them here so that those who find them useful are able to find them:
https://taglib.org/#language-bindings
http://developer.kde.org/~wheeler/taglib.html#bindings

View File

@@ -1,86 +1,27 @@
set(tag_c_HDR_DIRS
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/toolkit
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/vorbis
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/flac
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/flac
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2/frames
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
)
if(WITH_ASF)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf
)
endif()
if(WITH_VORBIS)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/vorbis
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/flac
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/flac
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus
)
endif()
if(WITH_APE)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ape
)
endif()
if(WITH_MP4)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4
)
endif()
if(WITH_TRUEAUDIO)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
)
endif()
if(WITH_RIFF)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/aiff
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/wav
)
endif()
if(WITH_MOD)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/it
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mod
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/s3m
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/xm
)
endif()
if(WITH_DSF)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff
)
endif()
if(WITH_SHORTEN)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/shorten
)
endif()
include_directories(${tag_c_HDR_DIRS})
set(tag_c_HDRS tag_c.h)
add_library(tag_c tag_c.cpp ${tag_c_HDRS})
target_include_directories(tag_c INTERFACE
$<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}>
)
target_link_libraries(tag_c PRIVATE tag)
set_target_properties(tag_c PROPERTIES
PUBLIC_HEADER "${tag_c_HDRS}"
DEFINE_SYMBOL MAKE_TAGLIB_LIB
)
if(VISIBILITY_HIDDEN)
set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden)
endif()
target_link_libraries(tag_c tag)
set_target_properties(tag_c PROPERTIES PUBLIC_HEADER "${tag_c_HDRS}")
if(BUILD_FRAMEWORK)
set_target_properties(tag_c PROPERTIES FRAMEWORK TRUE)
endif()
@@ -97,7 +38,7 @@ if(HAVE_CRUN_LIB)
# the only game in town, the three available STLs -- Cstd,
# stlport4 and stdcxx -- make this a mess. The KDE-Solaris
# team supports stdcxx (Apache RogueWave stdcxx 4.1.3).
# According to http://bugs.kde.org/show_bug.cgi?id=215225 the library can have the following two names:
find_library(ROGUEWAVE_STDCXX_LIBRARY NAMES stdcxx4 stdcxx)
if(NOT ROGUEWAVE_STDCXX_LIBRARY)
@@ -107,45 +48,21 @@ if(HAVE_CRUN_LIB)
endif()
set_target_properties(tag_c PROPERTIES
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
VERSION 0.0.0
SOVERSION 0
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
)
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(tag_c PUBLIC TAGLIB_STATIC)
endif()
if(TAGLIB_INSTALL_SUFFIX)
if(BUILD_SHARED_LIBS)
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}")
else()
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif()
set_target_properties(tag_c PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX})
endif()
install(TARGETS tag_c
EXPORT taglibTargets
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
)
if(NOT BUILD_FRAMEWORK)
if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
else()
set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
endif()
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR})
else()
set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
RENAME taglib${TAGLIB_INSTALL_SUFFIX}_c.pc)
if(NOT WIN32 AND NOT BUILD_FRAMEWORK)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
endif()

View File

@@ -19,278 +19,109 @@
* USA *
***************************************************************************/
#include "tag_c.h"
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <utility>
#ifdef HAVE_CONFIG_H
# include "config.h"
#include "config.h"
#endif
#include "taglib_config.h"
#include "tstringlist.h"
#include "tbytevectorstream.h"
#include "tiostream.h"
#include "tfile.h"
#include "tpropertymap.h"
#include "fileref.h"
#include "mpegfile.h"
#include "tag.h"
#include "id3v2framefactory.h"
#ifdef TAGLIB_WITH_ASF
#include "asffile.h"
#endif
#ifdef TAGLIB_WITH_VORBIS
#include "vorbisfile.h"
#include "flacfile.h"
#include "oggflacfile.h"
#include "speexfile.h"
#include "opusfile.h"
#endif
#ifdef TAGLIB_WITH_APE
#include "mpcfile.h"
#include "wavpackfile.h"
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
#include "trueaudiofile.h"
#endif
#ifdef TAGLIB_WITH_MP4
#include "mp4file.h"
#endif
#ifdef TAGLIB_WITH_RIFF
#include "aifffile.h"
#include "wavfile.h"
#endif
#ifdef TAGLIB_WITH_APE
#include "apefile.h"
#endif
#ifdef TAGLIB_WITH_MOD
#include "itfile.h"
#include "modfile.h"
#include "s3mfile.h"
#include "xmfile.h"
#endif
#ifdef TAGLIB_WITH_DSF
#include "dsffile.h"
#include "dsdifffile.h"
#endif
#ifdef TAGLIB_WITH_SHORTEN
#include "shortenfile.h"
#endif
#include <stdlib.h>
#include <fileref.h>
#include <tfile.h>
#include <asffile.h>
#include <vorbisfile.h>
#include <mpegfile.h>
#include <flacfile.h>
#include <oggflacfile.h>
#include <mpcfile.h>
#include <wavpackfile.h>
#include <speexfile.h>
#include <trueaudiofile.h>
#include <mp4file.h>
#include <tag.h>
#include <string.h>
#include <id3v2framefactory.h>
#include "tag_c.h"
using namespace TagLib;
namespace
{
List<char *> strings;
bool unicodeStrings = true;
bool stringManagementEnabled = true;
char *stringToCharArray(const String &s)
{
const std::string str = s.to8Bit(unicodeStrings);
#ifdef HAVE_ISO_STRDUP
return ::_strdup(str.c_str());
#else
return ::strdup(str.c_str());
#endif
}
String charArrayToString(const char *s)
{
return String(s, unicodeStrings ? String::UTF8 : String::Latin1);
}
} // namespace
static List<char *> strings;
static bool unicodeStrings = true;
static bool stringManagementEnabled = true;
void taglib_set_strings_unicode(BOOL unicode)
{
unicodeStrings = (unicode != 0);
unicodeStrings = bool(unicode);
}
void taglib_set_string_management_enabled(BOOL management)
{
stringManagementEnabled = (management != 0);
}
void taglib_free(void* pointer)
{
free(pointer);
stringManagementEnabled = bool(management);
}
////////////////////////////////////////////////////////////////////////////////
// TagLib::IOStream wrapper
////////////////////////////////////////////////////////////////////////////////
TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size)
{
return reinterpret_cast<TagLib_IOStream *>(
new ByteVectorStream(ByteVector(data, size)));
}
void taglib_iostream_free(TagLib_IOStream *stream)
{
delete reinterpret_cast<IOStream *>(stream);
}
////////////////////////////////////////////////////////////////////////////////
// TagLib::FileRef wrapper
// TagLib::File wrapper
////////////////////////////////////////////////////////////////////////////////
TagLib_File *taglib_file_new(const char *filename)
{
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
}
#ifdef _WIN32
TagLib_File *taglib_file_new_wchar(const wchar_t *filename)
{
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
}
#endif
template<typename T>
TagLib_File *taglib_file_new_type_any_char(const T *filename, TagLib_File_Type type)
{
File *file = NULL;
switch(type) {
case TagLib_File_MPEG:
file = new MPEG::File(filename);
break;
#ifdef TAGLIB_WITH_VORBIS
case TagLib_File_OggVorbis:
file = new Ogg::Vorbis::File(filename);
break;
case TagLib_File_FLAC:
file = new FLAC::File(filename);
break;
case TagLib_File_OggFlac:
file = new Ogg::FLAC::File(filename);
break;
case TagLib_File_Speex:
file = new Ogg::Speex::File(filename);
break;
case TagLib_File_Opus:
file = new Ogg::Opus::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_APE
case TagLib_File_MPC:
file = new MPC::File(filename);
break;
case TagLib_File_WavPack:
file = new WavPack::File(filename);
break;
case TagLib_File_APE:
file = new APE::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
case TagLib_File_TrueAudio:
file = new TrueAudio::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_MP4
case TagLib_File_MP4:
file = new MP4::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_ASF
case TagLib_File_ASF:
file = new ASF::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_RIFF
case TagLib_File_AIFF:
file = new RIFF::AIFF::File(filename);
break;
case TagLib_File_WAV:
file = new RIFF::WAV::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_MOD
case TagLib_File_IT:
file = new IT::File(filename);
break;
case TagLib_File_Mod:
file = new Mod::File(filename);
break;
case TagLib_File_S3M:
file = new S3M::File(filename);
break;
case TagLib_File_XM:
file = new XM::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_DSF
case TagLib_File_DSF:
file = new DSF::File(filename);
break;
case TagLib_File_DSDIFF:
file = new DSDIFF::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_SHORTEN
case TagLib_File_SHORTEN:
file = new Shorten::File(filename);
break;
#endif
default:
break;
}
return file ? reinterpret_cast<TagLib_File *>(new FileRef(file)) : NULL;
return reinterpret_cast<TagLib_File *>(FileRef::create(filename));
}
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
{
return taglib_file_new_type_any_char(filename, type);
}
switch(type) {
case TagLib_File_MPEG:
return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
case TagLib_File_OggVorbis:
return reinterpret_cast<TagLib_File *>(new Ogg::Vorbis::File(filename));
case TagLib_File_FLAC:
return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
case TagLib_File_MPC:
return reinterpret_cast<TagLib_File *>(new MPC::File(filename));
case TagLib_File_OggFlac:
return reinterpret_cast<TagLib_File *>(new Ogg::FLAC::File(filename));
case TagLib_File_WavPack:
return reinterpret_cast<TagLib_File *>(new WavPack::File(filename));
case TagLib_File_Speex:
return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
case TagLib_File_TrueAudio:
return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
case TagLib_File_MP4:
return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
case TagLib_File_ASF:
return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
default:
return 0;
}
#ifdef _WIN32
TagLib_File *taglib_file_new_type_wchar(const wchar_t *filename, TagLib_File_Type type)
{
return taglib_file_new_type_any_char(filename, type);
}
#endif
TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream)
{
return reinterpret_cast<TagLib_File *>(
new FileRef(reinterpret_cast<IOStream *>(stream)));
return 0;
}
void taglib_file_free(TagLib_File *file)
{
delete reinterpret_cast<FileRef *>(file);
delete reinterpret_cast<File *>(file);
}
BOOL taglib_file_is_valid(const TagLib_File *file)
{
return !reinterpret_cast<const FileRef *>(file)->isNull();
return reinterpret_cast<const File *>(file)->isValid();
}
TagLib_Tag *taglib_file_tag(const TagLib_File *file)
{
auto f = reinterpret_cast<const FileRef *>(file);
const File *f = reinterpret_cast<const File *>(file);
return reinterpret_cast<TagLib_Tag *>(f->tag());
}
const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
{
auto f = reinterpret_cast<const FileRef *>(file);
const File *f = reinterpret_cast<const File *>(file);
return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
}
BOOL taglib_file_save(TagLib_File *file)
{
return reinterpret_cast<FileRef *>(file)->save();
return reinterpret_cast<File *>(file)->save();
}
////////////////////////////////////////////////////////////////////////////////
@@ -299,8 +130,8 @@ BOOL taglib_file_save(TagLib_File *file)
char *taglib_tag_title(const TagLib_Tag *tag)
{
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->title());
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->title().toCString(unicodeStrings));
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -308,8 +139,8 @@ char *taglib_tag_title(const TagLib_Tag *tag)
char *taglib_tag_artist(const TagLib_Tag *tag)
{
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->artist());
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->artist().toCString(unicodeStrings));
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -317,8 +148,8 @@ char *taglib_tag_artist(const TagLib_Tag *tag)
char *taglib_tag_album(const TagLib_Tag *tag)
{
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->album());
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->album().toCString(unicodeStrings));
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -326,8 +157,8 @@ char *taglib_tag_album(const TagLib_Tag *tag)
char *taglib_tag_comment(const TagLib_Tag *tag)
{
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->comment());
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->comment().toCString(unicodeStrings));
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -335,8 +166,8 @@ char *taglib_tag_comment(const TagLib_Tag *tag)
char *taglib_tag_genre(const TagLib_Tag *tag)
{
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->genre());
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->genre().toCString(unicodeStrings));
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -344,55 +175,55 @@ char *taglib_tag_genre(const TagLib_Tag *tag)
unsigned int taglib_tag_year(const TagLib_Tag *tag)
{
auto t = reinterpret_cast<const Tag *>(tag);
const Tag *t = reinterpret_cast<const Tag *>(tag);
return t->year();
}
unsigned int taglib_tag_track(const TagLib_Tag *tag)
{
auto t = reinterpret_cast<const Tag *>(tag);
const Tag *t = reinterpret_cast<const Tag *>(tag);
return t->track();
}
void taglib_tag_set_title(TagLib_Tag *tag, const char *title)
{
auto t = reinterpret_cast<Tag *>(tag);
t->setTitle(charArrayToString(title));
Tag *t = reinterpret_cast<Tag *>(tag);
t->setTitle(String(title, unicodeStrings ? String::UTF8 : String::Latin1));
}
void taglib_tag_set_artist(TagLib_Tag *tag, const char *artist)
{
auto t = reinterpret_cast<Tag *>(tag);
t->setArtist(charArrayToString(artist));
Tag *t = reinterpret_cast<Tag *>(tag);
t->setArtist(String(artist, unicodeStrings ? String::UTF8 : String::Latin1));
}
void taglib_tag_set_album(TagLib_Tag *tag, const char *album)
{
auto t = reinterpret_cast<Tag *>(tag);
t->setAlbum(charArrayToString(album));
Tag *t = reinterpret_cast<Tag *>(tag);
t->setAlbum(String(album, unicodeStrings ? String::UTF8 : String::Latin1));
}
void taglib_tag_set_comment(TagLib_Tag *tag, const char *comment)
{
auto t = reinterpret_cast<Tag *>(tag);
t->setComment(charArrayToString(comment));
Tag *t = reinterpret_cast<Tag *>(tag);
t->setComment(String(comment, unicodeStrings ? String::UTF8 : String::Latin1));
}
void taglib_tag_set_genre(TagLib_Tag *tag, const char *genre)
{
auto t = reinterpret_cast<Tag *>(tag);
t->setGenre(charArrayToString(genre));
Tag *t = reinterpret_cast<Tag *>(tag);
t->setGenre(String(genre, unicodeStrings ? String::UTF8 : String::Latin1));
}
void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year)
{
auto t = reinterpret_cast<Tag *>(tag);
Tag *t = reinterpret_cast<Tag *>(tag);
t->setYear(year);
}
void taglib_tag_set_track(TagLib_Tag *tag, unsigned int track)
{
auto t = reinterpret_cast<Tag *>(tag);
Tag *t = reinterpret_cast<Tag *>(tag);
t->setTrack(track);
}
@@ -401,8 +232,8 @@ void taglib_tag_free_strings()
if(!stringManagementEnabled)
return;
for(auto &string : std::as_const(strings))
free(string);
for(List<char *>::Iterator it = strings.begin(); it != strings.end(); ++it)
free(*it);
strings.clear();
}
@@ -412,25 +243,25 @@ void taglib_tag_free_strings()
int taglib_audioproperties_length(const TagLib_AudioProperties *audioProperties)
{
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->lengthInSeconds();
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->length();
}
int taglib_audioproperties_bitrate(const TagLib_AudioProperties *audioProperties)
{
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->bitrate();
}
int taglib_audioproperties_samplerate(const TagLib_AudioProperties *audioProperties)
{
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->sampleRate();
}
int taglib_audioproperties_channels(const TagLib_AudioProperties *audioProperties)
{
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->channels();
}
@@ -456,420 +287,3 @@ void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding)
ID3v2::FrameFactory::instance()->setDefaultTextEncoding(type);
}
/******************************************************************************
* Properties API
******************************************************************************/
namespace {
void _taglib_property_set(TagLib_File *file, const char* prop, const char* value, bool append)
{
if(file == NULL || prop == NULL)
return;
auto tfile = reinterpret_cast<FileRef *>(file);
PropertyMap map = tfile->tag()->properties();
if(value) {
auto property = map.find(prop);
if(property == map.end()) {
map.insert(prop, StringList(charArrayToString(value)));
}
else {
if(append) {
property->second.append(charArrayToString(value));
}
else {
property->second = StringList(charArrayToString(value));
}
}
}
else {
map.erase(prop);
}
tfile->setProperties(map);
}
} // namespace
void taglib_property_set(TagLib_File *file, const char *prop, const char *value)
{
_taglib_property_set(file, prop, value, false);
}
void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value)
{
_taglib_property_set(file, prop, value, true);
}
char** taglib_property_keys(const TagLib_File *file)
{
if(file == NULL)
return NULL;
const PropertyMap map = reinterpret_cast<const FileRef *>(file)->properties();
if(map.isEmpty())
return NULL;
auto props = static_cast<char **>(malloc(sizeof(char *) * (map.size() + 1)));
char **pp = props;
for(const auto &i : map) {
*pp++ = stringToCharArray(i.first);
}
*pp = NULL;
return props;
}
char **taglib_property_get(const TagLib_File *file, const char *prop)
{
if(file == NULL || prop == NULL)
return NULL;
const PropertyMap map = reinterpret_cast<const FileRef *>(file)->properties();
auto property = map.find(prop);
if(property == map.end())
return NULL;
auto props = static_cast<char **>(malloc(sizeof(char *) * (property->second.size() + 1)));
char **pp = props;
for(const auto &i : property->second) {
*pp++ = stringToCharArray(i);
}
*pp = NULL;
return props;
}
void taglib_property_free(char **props)
{
if(props == NULL)
return;
char **p = props;
while(*p) {
free(*p++);
}
free(props);
}
/******************************************************************************
* Complex Properties API
******************************************************************************/
namespace {
bool _taglib_complex_property_set(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value, bool append)
{
if(file == NULL || key == NULL)
return false;
auto tfile = reinterpret_cast<FileRef *>(file);
if(value == NULL) {
return tfile->setComplexProperties(key, {});
}
VariantMap map;
const TagLib_Complex_Property_Attribute** attrPtr = value;
while(*attrPtr) {
const TagLib_Complex_Property_Attribute *attr = *attrPtr;
String attrKey(attr->key);
switch(attr->value.type) {
case TagLib_Variant_Void:
map.insert(attrKey, Variant());
break;
case TagLib_Variant_Bool:
map.insert(attrKey, attr->value.value.boolValue != 0);
break;
case TagLib_Variant_Int:
map.insert(attrKey, attr->value.value.intValue);
break;
case TagLib_Variant_UInt:
map.insert(attrKey, attr->value.value.uIntValue);
break;
case TagLib_Variant_LongLong:
map.insert(attrKey, attr->value.value.longLongValue);
break;
case TagLib_Variant_ULongLong:
map.insert(attrKey, attr->value.value.uLongLongValue);
break;
case TagLib_Variant_Double:
map.insert(attrKey, attr->value.value.doubleValue);
break;
case TagLib_Variant_String:
map.insert(attrKey, charArrayToString(attr->value.value.stringValue));
break;
case TagLib_Variant_StringList: {
StringList strs;
if(attr->value.value.stringListValue) {
char **s = attr->value.value.stringListValue;;
while(*s) {
strs.append(charArrayToString(*s++));
}
}
map.insert(attrKey, strs);
break;
}
case TagLib_Variant_ByteVector:
map.insert(attrKey, ByteVector(attr->value.value.byteVectorValue,
attr->value.size));
break;
}
++attrPtr;
}
return append ? tfile->setComplexProperties(key, tfile->complexProperties(key).append(map))
: tfile->setComplexProperties(key, {map});
}
} // namespace
BOOL taglib_complex_property_set(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value)
{
return _taglib_complex_property_set(file, key, value, false);
}
BOOL taglib_complex_property_set_append(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value)
{
return _taglib_complex_property_set(file, key, value, true);
}
char** taglib_complex_property_keys(const TagLib_File *file)
{
if(file == NULL) {
return NULL;
}
const StringList strs = reinterpret_cast<const FileRef *>(file)->complexPropertyKeys();
if(strs.isEmpty()) {
return NULL;
}
auto keys = static_cast<char **>(malloc(sizeof(char *) * (strs.size() + 1)));
char **keyPtr = keys;
for(const auto &str : strs) {
*keyPtr++ = stringToCharArray(str);
}
*keyPtr = NULL;
return keys;
}
TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
const TagLib_File *file, const char *key)
{
if(file == NULL || key == NULL) {
return NULL;
}
const auto variantMaps = reinterpret_cast<const FileRef *>(file)->complexProperties(key);
if(variantMaps.isEmpty()) {
return NULL;
}
auto props = static_cast<TagLib_Complex_Property_Attribute ***>(
malloc(sizeof(TagLib_Complex_Property_Attribute **) * (variantMaps.size() + 1)));
TagLib_Complex_Property_Attribute ***propPtr = props;
for(const auto &variantMap : variantMaps) {
if(!variantMap.isEmpty()) {
auto attrs = static_cast<TagLib_Complex_Property_Attribute **>(
malloc(sizeof(TagLib_Complex_Property_Attribute *) * (variantMap.size() + 1)));
auto attr = static_cast<TagLib_Complex_Property_Attribute *>(
malloc(sizeof(TagLib_Complex_Property_Attribute) * variantMap.size()));
TagLib_Complex_Property_Attribute **attrPtr = attrs;
// The next assignment is redundant to silence the clang analyzer,
// it is done at the end of the loop, which must be entered because
// variantMap is not empty.
*attrPtr = attr;
for(const auto &[k, v] : variantMap) {
attr->key = stringToCharArray(k);
attr->value.size = 0;
switch(v.type()) {
case Variant::Void:
attr->value.type = TagLib_Variant_Void;
attr->value.value.stringValue = NULL;
break;
case Variant::Bool:
attr->value.type = TagLib_Variant_Bool;
attr->value.value.boolValue = v.value<bool>();
break;
case Variant::Int:
attr->value.type = TagLib_Variant_Int;
attr->value.value.intValue = v.value<int>();
break;
case Variant::UInt:
attr->value.type = TagLib_Variant_UInt;
attr->value.value.uIntValue = v.value<unsigned int>();
break;
case Variant::LongLong:
attr->value.type = TagLib_Variant_LongLong;
attr->value.value.longLongValue = v.value<long long>();
break;
case Variant::ULongLong:
attr->value.type = TagLib_Variant_ULongLong;
attr->value.value.uLongLongValue = v.value<unsigned long long>();
break;
case Variant::Double:
attr->value.type = TagLib_Variant_Double;
attr->value.value.doubleValue = v.value<double>();
break;
case Variant::String: {
attr->value.type = TagLib_Variant_String;
auto str = v.value<String>();
attr->value.value.stringValue = stringToCharArray(str);
attr->value.size = str.size();
break;
}
case Variant::StringList: {
attr->value.type = TagLib_Variant_StringList;
auto strs = v.value<StringList>();
auto strPtr = static_cast<char **>(malloc(sizeof(char *) * (strs.size() + 1)));
attr->value.value.stringListValue = strPtr;
attr->value.size = strs.size();
for(const auto &str : strs) {
*strPtr++ = stringToCharArray(str);
}
*strPtr = NULL;
break;
}
case Variant::ByteVector: {
attr->value.type = TagLib_Variant_ByteVector;
const ByteVector data = v.value<ByteVector>();
auto bytePtr = static_cast<char *>(malloc(data.size()));
attr->value.value.byteVectorValue = bytePtr;
attr->value.size = data.size();
::memcpy(bytePtr, data.data(), data.size());
break;
}
case Variant::ByteVectorList:
case Variant::VariantList:
case Variant::VariantMap: {
attr->value.type = TagLib_Variant_String;
std::stringstream ss;
ss << v;
attr->value.value.stringValue = stringToCharArray(ss.str());
break;
}
}
*attrPtr++ = attr++;
}
*attrPtr = NULL;
*propPtr++ = attrs;
}
}
*propPtr = NULL;
return props;
}
void taglib_picture_from_complex_property(
TagLib_Complex_Property_Attribute*** properties,
TagLib_Complex_Property_Picture_Data *picture)
{
if(!properties || !picture) {
return;
}
std::memset(picture, 0, sizeof(*picture));
TagLib_Complex_Property_Attribute*** propPtr = properties;
while(!picture->data && *propPtr) {
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
while(*attrPtr) {
TagLib_Complex_Property_Attribute *attr = *attrPtr;
switch(attr->value.type) {
case TagLib_Variant_String:
if(strcmp("mimeType", attr->key) == 0) {
picture->mimeType = attr->value.value.stringValue;
}
else if(strcmp("description", attr->key) == 0) {
picture->description = attr->value.value.stringValue;
}
else if(strcmp("pictureType", attr->key) == 0) {
picture->pictureType = attr->value.value.stringValue;
}
break;
case TagLib_Variant_ByteVector:
if(strcmp("data", attr->key) == 0) {
picture->data = attr->value.value.byteVectorValue;
picture->size = attr->value.size;
}
break;
default:
break;
}
++attrPtr;
}
++propPtr;
}
}
void taglib_complex_property_free_keys(char **keys)
{
if(keys == NULL) {
return;
}
char **k = keys;
while(*k) {
free(*k++);
}
free(keys);
}
void taglib_complex_property_free(
TagLib_Complex_Property_Attribute ***props)
{
if(props == NULL) {
return;
}
TagLib_Complex_Property_Attribute*** propPtr = props;
while(*propPtr) {
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
while(*attrPtr) {
TagLib_Complex_Property_Attribute *attr = *attrPtr;
switch(attr->value.type) {
case TagLib_Variant_String:
free(attr->value.value.stringValue);
break;
case TagLib_Variant_StringList:
if(attr->value.value.stringListValue) {
char **s = attr->value.value.stringListValue;
while(*s) {
free(*s++);
}
free(attr->value.value.stringListValue);
}
break;
case TagLib_Variant_ByteVector:
free(attr->value.value.byteVectorValue);
break;
case TagLib_Variant_Void:
case TagLib_Variant_Bool:
case TagLib_Variant_Int:
case TagLib_Variant_UInt:
case TagLib_Variant_LongLong:
case TagLib_Variant_ULongLong:
case TagLib_Variant_Double:
break;
}
free(attr->key);
++attrPtr;
}
free(**propPtr);
free(*propPtr++);
}
free(props);
}

View File

@@ -29,9 +29,7 @@
extern "C" {
#endif
#if defined(TAGLIB_STATIC)
#define TAGLIB_C_EXPORT
#elif defined(_WIN32) || defined(_WIN64)
#if defined(_WIN32) || defined(_WIN64)
#ifdef MAKE_TAGLIB_C_LIB
#define TAGLIB_C_EXPORT __declspec(dllexport)
#else
@@ -43,11 +41,7 @@ extern "C" {
#define TAGLIB_C_EXPORT
#endif
#include <wchar.h>
#ifdef _MSC_VER
/* minwindef.h contains typedef int BOOL */
#include <windows.h>
#elif !defined BOOL
#ifndef BOOL
#define BOOL int
#endif
@@ -61,15 +55,14 @@ extern "C" {
*******************************************************************************/
/*
* These are used to give the C API some type safety (as opposed to
* using void * ), but pointers to them are simply cast to the corresponding C++
* These are used for type provide some type safety to the C API (as opposed to
* using void *, but pointers to them are simply cast to the corresponding C++
* types in the implementation.
*/
typedef struct { int dummy; } TagLib_File;
typedef struct { int dummy; } TagLib_Tag;
typedef struct { int dummy; } TagLib_AudioProperties;
typedef struct { int dummy; } TagLib_IOStream;
/*!
* By default all strings coming into or out of TagLib's C API are in UTF8.
@@ -86,27 +79,6 @@ TAGLIB_C_EXPORT void taglib_set_strings_unicode(BOOL unicode);
*/
TAGLIB_C_EXPORT void taglib_set_string_management_enabled(BOOL management);
/*!
* Explicitly free a string returned from TagLib
*/
TAGLIB_C_EXPORT void taglib_free(void* pointer);
/*******************************************************************************
* Stream API
******************************************************************************/
/*!
* Creates a byte vector stream from \a size bytes of \a data.
* Such a stream can be used with taglib_file_new_iostream() to create a file
* from memory.
*/
TAGLIB_C_EXPORT TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size);
/*!
* Frees and closes the stream.
*/
TAGLIB_C_EXPORT void taglib_iostream_free(TagLib_IOStream *stream);
/*******************************************************************************
* File API
******************************************************************************/
@@ -121,50 +93,23 @@ typedef enum {
TagLib_File_Speex,
TagLib_File_TrueAudio,
TagLib_File_MP4,
TagLib_File_ASF,
TagLib_File_AIFF,
TagLib_File_WAV,
TagLib_File_APE,
TagLib_File_IT,
TagLib_File_Mod,
TagLib_File_S3M,
TagLib_File_XM,
TagLib_File_Opus,
TagLib_File_DSF,
TagLib_File_DSDIFF,
TagLib_File_SHORTEN
TagLib_File_ASF
} TagLib_File_Type;
/*!
* Creates a TagLib file based on \a filename. TagLib will try to guess the file
* type.
*
*
* \returns NULL if the file type cannot be determined or the file cannot
* be opened.
*/
TAGLIB_C_EXPORT TagLib_File *taglib_file_new(const char *filename);
#ifdef _WIN32
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_wchar(const wchar_t *filename);
#endif
/*!
* Creates a TagLib file based on \a filename. Rather than attempting to guess
* the type, it will use the one specified by \a type.
*/
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type);
#ifdef _WIN32
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type_wchar(const wchar_t *filename, TagLib_File_Type type);
#endif
/*!
* Creates a TagLib file from a \a stream.
* A byte vector stream can be used to read a file from memory and write to
* memory, e.g. when fetching network data.
* The stream has to be created using taglib_memory_iostream_new() and shall be
* freed using taglib_iostream_free() \e after this file is freed with
* taglib_file_free().
*/
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream);
/*!
* Frees and closes the file.
@@ -172,7 +117,7 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream);
TAGLIB_C_EXPORT void taglib_file_free(TagLib_File *file);
/*!
* Returns \c true if the file is open and readable and valid information for
* Returns true if the file is open and readble and valid information for
* the Tag and / or AudioProperties was found.
*/
@@ -185,7 +130,7 @@ TAGLIB_C_EXPORT BOOL taglib_file_is_valid(const TagLib_File *file);
TAGLIB_C_EXPORT TagLib_Tag *taglib_file_tag(const TagLib_File *file);
/*!
* Returns a pointer to the audio properties associated with this file. This
* Returns a pointer to the the audio properties associated with this file. This
* will be freed automatically when the file is freed.
*/
TAGLIB_C_EXPORT const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file);
@@ -240,12 +185,12 @@ TAGLIB_C_EXPORT char *taglib_tag_comment(const TagLib_Tag *tag);
TAGLIB_C_EXPORT char *taglib_tag_genre(const TagLib_Tag *tag);
/*!
* Returns the tag's year or 0 if the year is not set.
* Returns the tag's year or 0 if year is not set.
*/
TAGLIB_C_EXPORT unsigned int taglib_tag_year(const TagLib_Tag *tag);
/*!
* Returns the tag's track number or 0 if the track number is not set.
* Returns the tag's track number or 0 if track number is not set.
*/
TAGLIB_C_EXPORT unsigned int taglib_tag_track(const TagLib_Tag *tag);
@@ -340,275 +285,6 @@ typedef enum {
TAGLIB_C_EXPORT void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding);
/******************************************************************************
* Properties API
******************************************************************************/
/*!
* Sets the property \a prop with \a value. Use \a value = NULL to remove
* the property, otherwise it will be replaced.
*/
TAGLIB_C_EXPORT void taglib_property_set(TagLib_File *file, const char *prop, const char *value);
/*!
* Appends \a value to the property \a prop (sets it if non-existing).
* Use \a value = NULL to remove all values associated with the property.
*/
TAGLIB_C_EXPORT void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value);
/*!
* Get the keys of the property map.
*
* \return NULL terminated array of C-strings (char *), only NULL if empty.
* It must be freed by the client using taglib_property_free().
*/
TAGLIB_C_EXPORT char** taglib_property_keys(const TagLib_File *file);
/*!
* Get value(s) of property \a prop.
*
* \return NULL terminated array of C-strings (char *), only NULL if empty.
* It must be freed by the client using taglib_property_free().
*/
TAGLIB_C_EXPORT char** taglib_property_get(const TagLib_File *file, const char *prop);
/*!
* Frees the NULL terminated array \a props and the C-strings it contains.
*/
TAGLIB_C_EXPORT void taglib_property_free(char **props);
/******************************************************************************
* Complex Properties API
******************************************************************************/
/*!
* Types which can be stored in a TagLib_Variant.
*
* \related TagLib::Variant::Type
* These correspond to TagLib::Variant::Type, but ByteVectorList, VariantList,
* VariantMap are not supported and will be returned as their string
* representation.
*/
typedef enum {
TagLib_Variant_Void,
TagLib_Variant_Bool,
TagLib_Variant_Int,
TagLib_Variant_UInt,
TagLib_Variant_LongLong,
TagLib_Variant_ULongLong,
TagLib_Variant_Double,
TagLib_Variant_String,
TagLib_Variant_StringList,
TagLib_Variant_ByteVector
} TagLib_Variant_Type;
/*!
* Discriminated union used in complex property attributes.
*
* \e type must be set according to the \e value union used.
* \e size is only required for TagLib_Variant_ByteVector and must contain
* the number of bytes.
*
* \related TagLib::Variant.
*/
typedef struct {
TagLib_Variant_Type type;
unsigned int size;
union {
char *stringValue;
char *byteVectorValue;
char **stringListValue;
BOOL boolValue;
int intValue;
unsigned int uIntValue;
long long longLongValue;
unsigned long long uLongLongValue;
double doubleValue;
} value;
} TagLib_Variant;
/*!
* Attribute of a complex property.
* Complex properties consist of a NULL-terminated array of pointers to
* this structure with \e key and \e value.
*/
typedef struct {
char *key;
TagLib_Variant value;
} TagLib_Complex_Property_Attribute;
/*!
* Picture data extracted from a complex property by the convenience function
* taglib_picture_from_complex_property().
*/
typedef struct {
char *mimeType;
char *description;
char *pictureType;
char *data;
unsigned int size;
} TagLib_Complex_Property_Picture_Data;
/*!
* Declare complex property attributes to set a picture.
* Can be used to define a variable \a var which can be used with
* taglib_complex_property_set() and a "PICTURE" key to set an
* embedded picture with the picture data \a dat of size \a siz
* and description \a desc, mime type \a mime and picture type
* \a typ (size is unsigned int, the other input parameters char *).
*/
#define TAGLIB_COMPLEX_PROPERTY_PICTURE(var, dat, siz, desc, mime, typ) \
const TagLib_Complex_Property_Attribute \
var##Attrs[] = { \
{ \
(char *)"data", \
{ \
TagLib_Variant_ByteVector, \
(siz), \
{ \
(char *)(dat) \
} \
} \
}, \
{ \
(char *)"mimeType", \
{ \
TagLib_Variant_String, \
0U, \
{ \
(char *)(mime) \
} \
} \
}, \
{ \
(char *)"description", \
{ \
TagLib_Variant_String, \
0U, \
{ \
(char *)(desc) \
} \
} \
}, \
{ \
(char *)"pictureType", \
{ \
TagLib_Variant_String, \
0U, \
{ \
(char *)(typ) \
} \
} \
} \
}; \
const TagLib_Complex_Property_Attribute *var[] = { \
&var##Attrs[0], &var##Attrs[1], &var##Attrs[2], \
&var##Attrs[3], NULL \
}
/*!
* Sets the complex property \a key with \a value. Use \a value = NULL to
* remove the property, otherwise it will be replaced with the NULL
* terminated array of attributes in \a value.
*
* A picture can be set with the TAGLIB_COMPLEX_PROPERTY_PICTURE macro:
*
* \code {.c}
* TagLib_File *file = taglib_file_new("myfile.mp3");
* FILE *fh = fopen("mypicture.jpg", "rb");
* if(fh) {
* fseek(fh, 0L, SEEK_END);
* long size = ftell(fh);
* fseek(fh, 0L, SEEK_SET);
* char *data = (char *)malloc(size);
* fread(data, size, 1, fh);
* TAGLIB_COMPLEX_PROPERTY_PICTURE(props, data, size, "Written by TagLib",
* "image/jpeg", "Front Cover");
* taglib_complex_property_set(file, "PICTURE", props);
* taglib_file_save(file);
* free(data);
* fclose(fh);
* }
* \endcode
*/
TAGLIB_C_EXPORT BOOL taglib_complex_property_set(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value);
/*!
* Appends \a value to the complex property \a key (sets it if non-existing).
* Use \a value = NULL to remove all values associated with the \a key.
*/
TAGLIB_C_EXPORT BOOL taglib_complex_property_set_append(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value);
/*!
* Get the keys of the complex properties.
*
* \return NULL terminated array of C-strings (char *), only NULL if empty.
* It must be freed by the client using taglib_complex_property_free_keys().
*/
TAGLIB_C_EXPORT char** taglib_complex_property_keys(const TagLib_File *file);
/*!
* Get value(s) of complex property \a key.
*
* \return NULL terminated array of property values, which are themselves an
* array of property attributes, only NULL if empty.
* It must be freed by the client using taglib_complex_property_free().
*/
TAGLIB_C_EXPORT TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
const TagLib_File *file, const char *key);
/*!
* Extract the complex property values of a picture.
*
* This function can be used to get the data from a "PICTURE" complex property
* without having to traverse the whole variant map. A picture can be
* retrieved like this:
*
* \code {.c}
* TagLib_File *file = taglib_file_new("myfile.mp3");
* TagLib_Complex_Property_Attribute*** properties =
* taglib_complex_property_get(file, "PICTURE");
* TagLib_Complex_Property_Picture_Data picture;
* taglib_picture_from_complex_property(properties, &picture);
* // Do something with picture.mimeType, picture.description,
* // picture.pictureType, picture.data, picture.size, e.g. extract it.
* FILE *fh = fopen("mypicture.jpg", "wb");
* if(fh) {
* fwrite(picture.data, picture.size, 1, fh);
* fclose(fh);
* }
* taglib_complex_property_free(properties);
* \endcode
*
* Note that the data in \a picture contains pointers to data in \a properties,
* i.e. it only lives as long as the properties, until they are freed with
* taglib_complex_property_free().
* If you want to access multiple pictures or additional properties of FLAC
* pictures ("width", "height", "numColors", "colorDepth" int values), you
* have to traverse the \a properties yourself.
*/
TAGLIB_C_EXPORT void taglib_picture_from_complex_property(
TagLib_Complex_Property_Attribute*** properties,
TagLib_Complex_Property_Picture_Data *picture);
/*!
* Frees the NULL terminated array \a keys (as returned by
* taglib_complex_property_keys()) and the C-strings it contains.
*/
TAGLIB_C_EXPORT void taglib_complex_property_free_keys(char **keys);
/*!
* Frees the NULL terminated array \a props of property attribute arrays
* (as returned by taglib_complex_property_get()) and the data such as
* C-strings and byte vectors contained in these attributes.
*/
TAGLIB_C_EXPORT void taglib_complex_property_free(
TagLib_Complex_Property_Attribute ***props);
#ifdef __cplusplus
}
#endif

View File

@@ -1,11 +1,12 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=@CMAKE_PC_LIBDIR@
includedir=@CMAKE_PC_INCLUDEDIR@
prefix=${CMAKE_INSTALL_PREFIX}
exec_prefix=${CMAKE_INSTALL_PREFIX}
libdir=${LIB_INSTALL_DIR}
includedir=${INCLUDE_INSTALL_DIR}
Name: TagLib C Bindings
Description: Audio meta-data library (C bindings)
Requires: taglib
Version: @TAGLIB_LIB_VERSION_STRING@
Libs: -L${libdir} -ltag_c@TAGLIB_INSTALL_SUFFIX@
Cflags: -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@
Version: ${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
Libs: -L${LIB_INSTALL_DIR} -ltag_c
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib

View File

@@ -8,7 +8,7 @@
include (MacroEnsureVersion)
if(NOT CPPUNIT_MIN_VERSION)
SET(CPPUNIT_MIN_VERSION 1.14.0)
SET(CPPUNIT_MIN_VERSION 1.12.0)
endif(NOT CPPUNIT_MIN_VERSION)
FIND_PROGRAM(CPPUNIT_CONFIG_EXECUTABLE cppunit-config )
@@ -33,7 +33,7 @@ ELSE(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
FIND_PATH(CPPUNIT_CFLAGS cppunit/TestRunner.h PATHS /usr/include /usr/local/include )
FIND_LIBRARY(CPPUNIT_LIBRARIES NAMES cppunit PATHS /usr/lib /usr/local/lib )
# how can we find cppunit version?
MESSAGE (STATUS "Ensure your cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
MESSAGE (STATUS "Ensure you cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
SET (CPPUNIT_INSTALLED_VERSION ${CPPUNIT_MIN_VERSION})
ENDIF(CPPUNIT_CONFIG_EXECUTABLE)
@@ -56,7 +56,7 @@ IF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
macro_ensure_version( ${CPPUNIT_MIN_VERSION} ${CPPUNIT_INSTALLED_VERSION} CPPUNIT_INSTALLED_VERSION_OK )
IF(NOT CPPUNIT_INSTALLED_VERSION_OK)
MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or newer is required")
MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or major is required")
SET(CppUnit_FOUND FALSE)
ENDIF(NOT CPPUNIT_INSTALLED_VERSION_OK)

11
config-taglib.h.cmake Normal file
View File

@@ -0,0 +1,11 @@
/* config-taglib.h. Generated by cmake from config-taglib.h.cmake */
/* Define if you have libz */
#cmakedefine HAVE_ZLIB 1
#cmakedefine NO_ITUNES_HACKS 1
#cmakedefine WITH_ASF 1
#cmakedefine WITH_MP4 1
#cmakedefine TESTS_DIR "@TESTS_DIR@"

View File

@@ -1,25 +0,0 @@
/* config.h. Generated by cmake from config.h.cmake */
#ifndef TAGLIB_CONFIG_H
#define TAGLIB_CONFIG_H
/* Defined if your compiler supports some byte swap functions */
#cmakedefine HAVE_GCC_BYTESWAP 1
#cmakedefine HAVE_GLIBC_BYTESWAP 1
#cmakedefine HAVE_MSC_BYTESWAP 1
#cmakedefine HAVE_MAC_BYTESWAP 1
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
/* Defined if your compiler supports ISO _strdup */
#cmakedefine HAVE_ISO_STRDUP 1
/* Defined if zlib is installed */
#cmakedefine HAVE_ZLIB 1
/* Indicates whether debug messages are shown even in release mode */
#cmakedefine TRACE_IN_RELEASE 1
#cmakedefine TESTS_DIR "@TESTS_DIR@"
#cmakedefine TESTS_TMPDIR "@TESTS_TMPDIR@"
#endif

View File

@@ -1,2 +1,4 @@
</div>
</div>
</body>
</html>

View File

@@ -1,38 +1,41 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>$title ($projectname)</title>
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<link href="taglib-api.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="top">
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo" width="200px"/></td>
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">$projectname
&#160;<span id="projectnumber">$projectnumber</span>
</div>
<div id="projectbrief">$projectbrief</div>
</td>
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<td>$searchbox</td>
</tr>
</tbody>
</table>
</div>
<div id="container">
<table border="0" width="100%">
<tr>
<td width="1">
<img src="../taglib.png">
</td>
<td>
<div id="intro">
<table border="0" height="119" cellpadding="0" cellspacing="0" width="100%">
<tr><td valign="top"><h1>TagLib $projectnumber ($title)</h1></td></tr>
<tr>
<td valign="bottom">
<div id="links">
<a href="index.html">Home</a>
<a href="inherits.html">Class&nbsp;Hierarchy</a>
<a href="namespaces.html">Namespaces</a>
<a href="annotated.html">Classes</a>
<a href="files.html">Headers</a>
<a href="namespacemembers.html">Namespace&nbsp;Members</a>
<a href="functions.html">Class&nbsp;Members</a>
<a href="globals.html">File&nbsp;Members</a>
</div>
</td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<div id="text">

395
doc/taglib-api.css Normal file
View File

@@ -0,0 +1,395 @@
body {
font-family: sans-serif;
background: white;
color: black;
margin: 0px;
padding: 15px;
}
a:link {
font-weight: bold;
text-decoration: none;
color: gray;
}
a:visited {
font-weight: bold;
text-decoration: none;
color: gray;
}
a:hover {
color: #cccccc;
text-decoration: underline;
}
a:active {
color: #cccccc;
text-decoration: underline;
}
img {
border-style: none;
}
h1 {
font-family: sans-serif;
}
h2 {
font-family: sans-serif;
}
h3 {
font-family: sans-serif;
}
/* container */
#container {
position: absolute;
border-width: thin;
border-style: solid;
width: 95%;
}
/* intro */
#intro {
padding: 5px;
margin: 0px;
background: #cccccc;
border-width: medium;
border-style: solid;
}
#intro h1 {
margin: 5px;
padding: 5px;
}
/* links */
#links {
font-size: x-small;
vertical-align: bottom;
}
#links a {
border-width: thin;
border-style: dotted;
border-color: white;
/* margin: 0px 10px 0px 0px; */
margin: 1px;
padding: 3px;
line-height: 230%
}
#links a:hover {
color: black;
text-decoration: underline;
}
#links h3 {
outline-width: thin;
border-style: solid;
padding: 2px;
margin: 3px 0px 3px 0px;
}
/* menu */
#menu h3 {
text-align: center;
}
/* text */
#text {
margin: 0px;
padding: 5px 5px 0px 5px;
float: left;
}
#text h3 {
border-width: thin;
border-style: solid;
padding: 2px;
margin: 3px 0px 3px 0px;
}
#text li {
margin: 0px 0px 10px 0px;
}
#text ul {
margin: 5px;
padding: 0px 0px 0px 20px;
}
#leftcolumn {
float: left;
width: 300px;
margin: 0px 10px 0px 0px;
padding: 0px;
}
#rightcolumn {
float: right;
width: 210px;
margin: 0px;
padding: 0px;
}
/* vspacer */
.vspacer {
height: 10px;
}
.silver {
border-width: thin;
border-color: black;
border-style: solid;
background: #cccccc;
}
a.code {
text-decoration: none;
font-weight: normal;
color: #4444ee
}
a.codeRef {
font-weight: normal;
color: #4444ee
}
div.fragment {
width: 98%;
border: 1px solid #CCCCCC;
background-color: #f5f5f5;
padding-left: 4px;
margin: 4px;
}
div.ah {
background-color: black;
font-weight: bold; color: #ffffff;
margin-bottom: 3px;
margin-top: 3px
}
#text td {
width: auto;
}
div.memdoc {
margin-top: 0px;
margin-bottom: 20px;
padding: 10px 10px 10px 40px;
}
div.memproto {
border: thin solid black;
background-color: #f2f2ff;
width: 100%;
margin-top: 20px;
padding-top: 10px;
padding-bottom: 10px;
}
td.paramtype {
color: #602020;
}
table.memname {
font-weight: bold;
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
margin-bottom: 6px;
font-weight: bold
}
div.groupText {
margin-left: 16px;
font-style: italic;
font-size: smaller
}
body {
background: white;
color: black;
margin-right: 20px;
margin-left: 20px;
}
td.indexkey {
background-color: #eeeeff;
font-weight: bold;
padding-right : 10px;
padding-top : 2px;
padding-left : 10px;
padding-bottom : 2px;
margin-left : 0px;
margin-right : 0px;
margin-top : 2px;
margin-bottom : 2px
}
td.indexvalue {
background-color: #eeeeff;
font-style: italic;
padding-right : 10px;
padding-top : 2px;
padding-left : 10px;
padding-bottom : 2px;
margin-left : 0px;
margin-right : 0px;
margin-top : 2px;
margin-bottom : 2px
}
tr.memlist {
background-color: #f0f0f0;
}
p.formulaDsp {
text-align: center;
}
img.formulaDsp {
}
img.formulaInl {
vertical-align: middle;
}
span.keyword {
color: #008000
}
span.keywordtype {
color: #604020
}
span.keywordflow {
color: #e08000
}
span.comment {
color: #800000
}
span.preprocessor {
color: #806020
}
span.stringliteral {
color: #002080
}
span.charliteral {
color: #008080
}
.mdTable {
border: 1px solid #868686;
background-color: #f2f2ff;
}
.mdRow {
padding: 8px 20px;
}
.mdescLeft {
font-size: smaller;
font-family: Arial, Helvetica, sans-serif;
background-color: #FAFAFA;
padding-left: 8px;
border-top: 1px none #E0E0E0;
border-right: 1px none #E0E0E0;
border-bottom: 1px none #E0E0E0;
border-left: 1px none #E0E0E0;
margin: 0px;
}
.mdescRight {
font-size: smaller;
font-family: Arial, Helvetica, sans-serif;
font-style: italic;
background-color: #FAFAFA;
padding-left: 4px;
border-top: 1px none #E0E0E0;
border-right: 1px none #E0E0E0;
border-bottom: 1px none #E0E0E0;
border-left: 1px none #E0E0E0;
margin: 0px;
padding-bottom: 0px;
padding-right: 8px;
}
.memItemLeft {
padding: 1px 0px 0px 8px;
margin: 4px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-top-color: #E0E0E0;
border-right-color: #E0E0E0;
border-bottom-color: #E0E0E0;
border-left-color: #E0E0E0;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
background-color: #FAFAFA;
font-family: Geneva, Arial, Helvetica, sans-serif;
font-size: 12px;
}
.memItemRight {
padding: 1px 0px 0px 8px;
margin: 4px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-top-color: #E0E0E0;
border-right-color: #E0E0E0;
border-bottom-color: #E0E0E0;
border-left-color: #E0E0E0;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
background-color: #FAFAFA;
font-family: Geneva, Arial, Helvetica, sans-serif;
font-size: 13px;
}
.search {
color: #0000ee;
font-weight: bold;
}
form.search {
margin-bottom: 0px;
margin-top: 0px;
}
input.search {
font-size: 75%;
color: #000080;
font-weight: normal;
background-color: #eeeeff;
}
td.tiny {
font-size: 75%;
}

BIN
doc/taglib.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,45 +1,50 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../taglib
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/
)
if(BUILD_EXAMPLES)
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/ )
if(NOT BUILD_SHARED_LIBS)
add_definitions(-DTAGLIB_STATIC)
endif()
if(ENABLE_STATIC)
add_definitions(-DTAGLIB_STATIC)
endif(ENABLE_STATIC)
########### next target ###############
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader tag)
ADD_EXECUTABLE(tagreader tagreader.cpp)
TARGET_LINK_LIBRARIES(tagreader tag )
########### next target ###############
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c tag_c)
ADD_EXECUTABLE(tagreader_c tagreader_c.c)
TARGET_LINK_LIBRARIES(tagreader_c tag_c )
########### next target ###############
add_executable(tagwriter tagwriter.cpp)
target_link_libraries(tagwriter tag)
ADD_EXECUTABLE(tagwriter tagwriter.cpp)
TARGET_LINK_LIBRARIES(tagwriter tag )
########### next target ###############
add_executable(framelist framelist.cpp)
target_link_libraries(framelist tag)
ADD_EXECUTABLE(framelist framelist.cpp)
TARGET_LINK_LIBRARIES(framelist tag )
########### next target ###############
add_executable(strip-id3v1 strip-id3v1.cpp)
target_link_libraries(strip-id3v1 tag)
ADD_EXECUTABLE(strip-id3v1 strip-id3v1.cpp)
TARGET_LINK_LIBRARIES(strip-id3v1 tag )
endif(BUILD_EXAMPLES)
install(TARGETS tagreader tagreader_c tagwriter framelist strip-id3v1
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

View File

@@ -23,20 +23,21 @@
*/
#include <iostream>
#include <cstdlib>
#include <stdlib.h>
#include "taglib_config.h"
#include "tbytevector.h"
#include "mpegfile.h"
#include "id3v2tag.h"
#include "id3v2frame.h"
#include "id3v2header.h"
#include "commentsframe.h"
#include "id3v1tag.h"
#ifdef TAGLIB_WITH_APE
#include "apetag.h"
#endif
#include <tbytevector.h>
#include <mpegfile.h>
#include <id3v2tag.h>
#include <id3v2frame.h>
#include <id3v2header.h>
#include <id3v1tag.h>
#include <apetag.h>
using namespace std;
using namespace TagLib;
int main(int argc, char *argv[])
@@ -46,7 +47,7 @@ int main(int argc, char *argv[])
for(int i = 1; i < argc; i++) {
std::cout << "******************** \"" << argv[i] << "\"********************" << std::endl;
cout << "******************** \"" << argv[i] << "\"********************" << endl;
MPEG::File f(argv[i]);
@@ -54,64 +55,52 @@ int main(int argc, char *argv[])
if(id3v2tag) {
std::cout << "ID3v2."
cout << "ID3v2."
<< id3v2tag->header()->majorVersion()
<< "."
<< id3v2tag->header()->revisionNumber()
<< ", "
<< id3v2tag->header()->tagSize()
<< " bytes in tag"
<< std::endl;
<< endl;
const auto &frames = id3v2tag->frameList();
for(auto it = frames.begin(); it != frames.end(); it++) {
std::cout << (*it)->frameID();
if(auto comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
if(!comment->description().isEmpty())
std::cout << " [" << comment->description() << "]";
std::cout << " - \"" << (*it)->toString() << "\"" << std::endl;
}
ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
for(; it != id3v2tag->frameList().end(); it++)
cout << (*it)->frameID() << " - \"" << (*it)->toString() << "\"" << endl;
}
else
std::cout << "file does not have a valid id3v2 tag" << std::endl;
cout << "file does not have a valid id3v2 tag" << endl;
std::cout << std::endl << "ID3v1" << std::endl;
cout << endl << "ID3v1" << endl;
ID3v1::Tag *id3v1tag = f.ID3v1Tag();
if(id3v1tag) {
std::cout << "title - \"" << id3v1tag->title() << "\"" << std::endl;
std::cout << "artist - \"" << id3v1tag->artist() << "\"" << std::endl;
std::cout << "album - \"" << id3v1tag->album() << "\"" << std::endl;
std::cout << "year - \"" << id3v1tag->year() << "\"" << std::endl;
std::cout << "comment - \"" << id3v1tag->comment() << "\"" << std::endl;
std::cout << "track - \"" << id3v1tag->track() << "\"" << std::endl;
std::cout << "genre - \"" << id3v1tag->genre() << "\"" << std::endl;
cout << "title - \"" << id3v1tag->title() << "\"" << endl;
cout << "artist - \"" << id3v1tag->artist() << "\"" << endl;
cout << "album - \"" << id3v1tag->album() << "\"" << endl;
cout << "year - \"" << id3v1tag->year() << "\"" << endl;
cout << "comment - \"" << id3v1tag->comment() << "\"" << endl;
cout << "track - \"" << id3v1tag->track() << "\"" << endl;
cout << "genre - \"" << id3v1tag->genre() << "\"" << endl;
}
else
std::cout << "file does not have a valid id3v1 tag" << std::endl;
cout << "file does not have a valid id3v1 tag" << endl;
#ifdef TAGLIB_WITH_APE
APE::Tag *ape = f.APETag();
std::cout << std::endl << "APE" << std::endl;
cout << endl << "APE" << endl;
if(ape) {
const auto &items = ape->itemListMap();
for(auto it = items.begin(); it != items.end(); ++it)
for(APE::ItemListMap::ConstIterator it = ape->itemListMap().begin();
it != ape->itemListMap().end(); ++it)
{
if((*it).second.type() != APE::Item::Binary)
std::cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << std::endl;
else
std::cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << std::endl;
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
}
}
else
std::cout << "file does not have a valid APE tag" << std::endl;
#endif
cout << "file does not have a valid APE tag" << endl;
std::cout << std::endl;
cout << endl;
}
}

View File

@@ -23,9 +23,8 @@
*/
#include <iostream>
#include "tstring.h"
#include "mpegfile.h"
#include <mpegfile.h>
#include <tstring.h>
using namespace TagLib;

View File

@@ -23,20 +23,25 @@
*/
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <stdio.h>
#include "tpropertymap.h"
#include "tstringlist.h"
#include "tvariant.h"
#include "fileref.h"
#include "tag.h"
#include <fileref.h>
#include <tag.h>
using namespace std;
TagLib::String formatSeconds(int seconds)
{
char secondsString[3];
sprintf(secondsString, "%02i", seconds);
return secondsString;
}
int main(int argc, char *argv[])
{
for(int i = 1; i < argc; i++) {
std::cout << "******************** \"" << argv[i] << "\" ********************" << std::endl;
cout << "******************** \"" << argv[i] << "\" ********************" << endl;
TagLib::FileRef f(argv[i]);
@@ -44,77 +49,29 @@ int main(int argc, char *argv[])
TagLib::Tag *tag = f.tag();
std::cout << "-- TAG (basic) --" << std::endl;
std::cout << "title - \"" << tag->title() << "\"" << std::endl;
std::cout << "artist - \"" << tag->artist() << "\"" << std::endl;
std::cout << "album - \"" << tag->album() << "\"" << std::endl;
std::cout << "year - \"" << tag->year() << "\"" << std::endl;
std::cout << "comment - \"" << tag->comment() << "\"" << std::endl;
std::cout << "track - \"" << tag->track() << "\"" << std::endl;
std::cout << "genre - \"" << tag->genre() << "\"" << std::endl;
TagLib::PropertyMap tags = f.properties();
if(!tags.isEmpty()) {
unsigned int longest = 0;
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
if (j->first.size() > longest) {
longest = j->first.size();
}
}
std::cout << "-- TAG (properties) --" << std::endl;
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
for(auto k = j->second.begin(); k != j->second.end(); ++k) {
std::cout << std::left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << std::endl;
}
}
}
TagLib::StringList names = f.complexPropertyKeys();
for(const auto &name : names) {
const auto& properties = f.complexProperties(name);
for(const auto &property : properties) {
std::cout << name << ":" << std::endl;
for(const auto &[key, value] : property) {
std::cout << " " << std::left << std::setfill(' ') << std::setw(11) << key << " - ";
if(value.type() == TagLib::Variant::ByteVector) {
std::cout << "(" << value.value<TagLib::ByteVector>().size() << " bytes)" << std::endl;
/* The picture could be extracted using:
std::ofstream picture;
TagLib::String fn(argv[i]);
int slashPos = fn.rfind('/');
int dotPos = fn.rfind('.');
if(slashPos >= 0 && dotPos > slashPos) {
fn = fn.substr(slashPos + 1, dotPos - slashPos - 1);
}
fn += ".jpg";
picture.open(fn.toCString(), std::ios_base::out | std::ios_base::binary);
picture << value.value<TagLib::ByteVector>();
picture.close();
*/
}
else {
std::cout << value << std::endl;
}
}
}
}
cout << "-- TAG --" << endl;
cout << "title - \"" << tag->title() << "\"" << endl;
cout << "artist - \"" << tag->artist() << "\"" << endl;
cout << "album - \"" << tag->album() << "\"" << endl;
cout << "year - \"" << tag->year() << "\"" << endl;
cout << "comment - \"" << tag->comment() << "\"" << endl;
cout << "track - \"" << tag->track() << "\"" << endl;
cout << "genre - \"" << tag->genre() << "\"" << endl;
}
if(!f.isNull() && f.audioProperties()) {
TagLib::AudioProperties *properties = f.audioProperties();
int seconds = properties->lengthInSeconds() % 60;
int minutes = (properties->lengthInSeconds() - seconds) / 60;
int seconds = properties->length() % 60;
int minutes = (properties->length() - seconds) / 60;
std::cout << "-- AUDIO --" << std::endl;
std::cout << "bitrate - " << properties->bitrate() << std::endl;
std::cout << "sample rate - " << properties->sampleRate() << std::endl;
std::cout << "channels - " << properties->channels() << std::endl;
std::cout << "length - " << minutes << ":" << std::setfill('0') << std::setw(2) << std::right << seconds << std::endl;
cout << "-- AUDIO --" << endl;
cout << "bitrate - " << properties->bitrate() << endl;
cout << "sample rate - " << properties->sampleRate() << endl;
cout << "channels - " << properties->channels() << endl;
cout << "length - " << minutes << ":" << formatSeconds(seconds) << endl;
}
}
return 0;
}

View File

@@ -22,10 +22,8 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "tag_c.h"
#include <stdio.h>
#include <string.h>
#include <tag_c.h>
#ifndef FALSE
#define FALSE 0
@@ -34,16 +32,15 @@
int main(int argc, char *argv[])
{
int i;
int seconds;
int minutes;
TagLib_File *file;
TagLib_Tag *tag;
const TagLib_AudioProperties *properties;
taglib_set_strings_unicode(1);
taglib_set_strings_unicode(FALSE);
for(i = 1; i < argc; i++) {
TagLib_File *file;
TagLib_Tag *tag;
const TagLib_AudioProperties *properties;
char **propertiesMap;
char **complexKeys;
printf("******************** \"%s\" ********************\n", argv[i]);
file = taglib_file_new(argv[i]);
@@ -53,115 +50,21 @@ int main(int argc, char *argv[])
tag = taglib_file_tag(file);
properties = taglib_file_audioproperties(file);
propertiesMap = taglib_property_keys(file);
complexKeys = taglib_complex_property_keys(file);
if(tag != NULL) {
printf("-- TAG (basic) --\n");
printf("-- TAG --\n");
printf("title - \"%s\"\n", taglib_tag_title(tag));
printf("artist - \"%s\"\n", taglib_tag_artist(tag));
printf("album - \"%s\"\n", taglib_tag_album(tag));
printf("year - \"%u\"\n", taglib_tag_year(tag));
printf("year - \"%i\"\n", taglib_tag_year(tag));
printf("comment - \"%s\"\n", taglib_tag_comment(tag));
printf("track - \"%u\"\n", taglib_tag_track(tag));
printf("track - \"%i\"\n", taglib_tag_track(tag));
printf("genre - \"%s\"\n", taglib_tag_genre(tag));
}
if(propertiesMap != NULL) {
char **keyPtr = propertiesMap;
int longest = 0;
while(*keyPtr) {
int len = (int)strlen(*keyPtr++);
if(len > longest) {
longest = len;
}
}
keyPtr = propertiesMap;
printf("-- TAG (properties) --\n");
while(*keyPtr) {
char **valPtr;
char **propertyValues = valPtr = taglib_property_get(file, *keyPtr);
while(valPtr && *valPtr)
{
printf("%-*s - \"%s\"\n", longest, *keyPtr, *valPtr++);
}
taglib_property_free(propertyValues);
++keyPtr;
}
}
if(complexKeys != NULL) {
char **keyPtr = complexKeys;
while(*keyPtr) {
TagLib_Complex_Property_Attribute*** props =
taglib_complex_property_get(file, *keyPtr);
if(props != NULL) {
TagLib_Complex_Property_Attribute*** propPtr = props;
while(*propPtr) {
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
printf("%s:\n", *keyPtr);
while(*attrPtr) {
TagLib_Complex_Property_Attribute *attr = *attrPtr;
TagLib_Variant_Type type = attr->value.type;
printf(" %-11s - ", attr->key);
switch(type) {
case TagLib_Variant_Void:
printf("null\n");
break;
case TagLib_Variant_Bool:
printf("%s\n", attr->value.value.boolValue ? "true" : "false");
break;
case TagLib_Variant_Int:
printf("%d\n", attr->value.value.intValue);
break;
case TagLib_Variant_UInt:
printf("%u\n", attr->value.value.uIntValue);
break;
case TagLib_Variant_LongLong:
printf("%lld\n", attr->value.value.longLongValue);
break;
case TagLib_Variant_ULongLong:
printf("%llu\n", attr->value.value.uLongLongValue);
break;
case TagLib_Variant_Double:
printf("%f\n", attr->value.value.doubleValue);
break;
case TagLib_Variant_String:
printf("\"%s\"\n", attr->value.value.stringValue);
break;
case TagLib_Variant_StringList:
if(attr->value.value.stringListValue) {
char **strs = attr->value.value.stringListValue;
char **s = strs;
while(*s) {
if(s != strs) {
printf(" ");
}
printf("%s", *s++);
}
}
printf("\n");
break;
case TagLib_Variant_ByteVector:
printf("(%u bytes)\n", attr->value.size);
break;
}
++attrPtr;
}
++propPtr;
}
taglib_complex_property_free(props);
}
++keyPtr;
}
taglib_complex_property_free_keys(complexKeys);
}
if(properties != NULL) {
int seconds = taglib_audioproperties_length(properties) % 60;
int minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
seconds = taglib_audioproperties_length(properties) % 60;
minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
printf("-- AUDIO --\n");
printf("bitrate - %i\n", taglib_audioproperties_bitrate(properties));
@@ -170,7 +73,6 @@ int main(int argc, char *argv[])
printf("length - %i:%02i\n", minutes, seconds);
}
taglib_property_free(propertiesMap);
taglib_tag_free_strings();
taglib_file_free(file);
}

View File

@@ -23,21 +23,19 @@
*/
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include "tlist.h"
#include "tfile.h"
#include "tpropertymap.h"
#include "tvariant.h"
#include "fileref.h"
#include "tag.h"
#include <tlist.h>
#include <fileref.h>
#include <tfile.h>
#include <tag.h>
using namespace std;
bool isArgument(const char *s)
{
@@ -46,55 +44,32 @@ bool isArgument(const char *s)
bool isFile(const char *s)
{
#ifdef _WIN32
struct _stat64 st;
return ::_stat64(s, &st) == 0 && (st.st_mode & S_IFREG);
#else
struct stat st;
#ifdef _WIN32
return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG));
#else
return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG | S_IFLNK));
#endif
}
void usage()
{
std::cout << std::endl;
std::cout << "Usage: tagwriter <fields> <files>" << std::endl;
std::cout << std::endl;
std::cout << "Where the valid fields are:" << std::endl;
std::cout << " -t <title>" << std::endl;
std::cout << " -a <artist>" << std::endl;
std::cout << " -A <album>" << std::endl;
std::cout << " -c <comment>" << std::endl;
std::cout << " -g <genre>" << std::endl;
std::cout << " -y <year>" << std::endl;
std::cout << " -T <track>" << std::endl;
std::cout << " -R <tagname> <tagvalue>" << std::endl;
std::cout << " -I <tagname> <tagvalue>" << std::endl;
std::cout << " -D <tagname>" << std::endl;
std::cout << " -p <picturefile> <description> (\"\" \"\" to remove)" << std::endl;
std::cout << std::endl;
cout << endl;
cout << "Usage: tagwriter <fields> <files>" << endl;
cout << endl;
cout << "Where the valid fields are:" << endl;
cout << " -t <title>" << endl;
cout << " -a <artist>" << endl;
cout << " -A <album>" << endl;
cout << " -c <comment>" << endl;
cout << " -g <genre>" << endl;
cout << " -y <year>" << endl;
cout << " -T <track>" << endl;
cout << endl;
exit(1);
}
void checkForRejectedProperties(const TagLib::PropertyMap &tags)
{ // stolen from tagreader.cpp
if(tags.size() > 0) {
unsigned int longest = 0;
for(auto i = tags.begin(); i != tags.end(); ++i) {
if(i->first.size() > longest) {
longest = i->first.size();
}
}
std::cout << "-- rejected TAGs (properties) --" << std::endl;
for(auto i = tags.begin(); i != tags.end(); ++i) {
for(auto j = i->second.begin(); j != i->second.end(); ++j) {
std::cout << std::left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << std::endl;
}
}
}
}
int main(int argc, char *argv[])
{
TagLib::List<TagLib::FileRef> fileList;
@@ -112,18 +87,17 @@ int main(int argc, char *argv[])
if(fileList.isEmpty())
usage();
int i = 1;
while(i < argc - 1) {
for(int i = 1; i < argc - 1; i += 2) {
if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
char field = argv[i][1];
TagLib::String value = argv[i + 1];
int numArgsConsumed = 2;
for(auto &f : fileList) {
TagLib::List<TagLib::FileRef>::Iterator it;
for(it = fileList.begin(); it != fileList.end(); ++it) {
TagLib::Tag *t = f.tag();
TagLib::Tag *t = (*it).tag();
switch (field) {
case 't':
@@ -147,79 +121,19 @@ int main(int argc, char *argv[])
case 'T':
t->setTrack(value.toInt());
break;
case 'R':
case 'I':
if(i + 2 < argc) {
TagLib::PropertyMap map = f.properties();
if(field == 'R') {
map.replace(value, TagLib::String(argv[i + 2]));
}
else {
map.insert(value, TagLib::String(argv[i + 2]));
}
numArgsConsumed = 3;
checkForRejectedProperties(f.setProperties(map));
}
else {
usage();
}
break;
case 'D': {
TagLib::PropertyMap map = f.properties();
map.erase(value);
checkForRejectedProperties(f.setProperties(map));
break;
}
case 'p': {
if(i + 2 < argc) {
numArgsConsumed = 3;
if(!value.isEmpty()) {
if(!isFile(value.toCString())) {
std::cout << value.toCString() << " not found." << std::endl;
return 1;
}
std::ifstream picture;
picture.open(value.toCString(), std::ios::in | std::ios::binary);
std::stringstream buffer;
buffer << picture.rdbuf();
picture.close();
TagLib::String buf(buffer.str());
TagLib::ByteVector data(buf.data(TagLib::String::Latin1));
TagLib::String mimeType = data.startsWith("\x89PNG\x0d\x0a\x1a\x0a")
? "image/png" : "image/jpeg";
TagLib::String description(argv[i + 2]);
f.setComplexProperties("PICTURE", {
{
{"data", data},
{"pictureType", "Front Cover"},
{"mimeType", mimeType},
{"description", description}
}
});
}
else {
// empty value, remove pictures
f.setComplexProperties("PICTURE", {});
}
}
else {
usage();
}
break;
}
default:
usage();
break;
}
}
i += numArgsConsumed;
}
else
usage();
}
for(auto &f : fileList)
f.save();
TagLib::List<TagLib::FileRef>::Iterator it;
for(it = fileList.begin(); it != fileList.end(); ++it)
(*it).file()->save();
return 0;
}

View File

@@ -2,32 +2,22 @@
usage()
{
echo "usage: $0 [OPTIONS]"
echo "usage: $0 [OPTIONS]"
cat << EOH
options:
[--libs]
[--cflags]
[--version]
[--prefix]
[--libs]
[--cflags]
[--version]
[--prefix]
EOH
exit 1
exit 1;
}
# Looks useless as it is, but could be replaced with a "pcfiledir" by Buildroot.
prefix=
exec_prefix=
if test -z "$prefix"; then
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
else
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
fi
if test -z "$exec_prefix"; then
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
else
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
fi
prefix=${CMAKE_INSTALL_PREFIX}
exec_prefix=${CMAKE_INSTALL_PREFIX}
libdir=${LIB_INSTALL_DIR}
includedir=${INCLUDE_INSTALL_DIR}
flags=""
@@ -39,22 +29,22 @@ while test $# -gt 0
do
case $1 in
--libs)
flags="$flags -L$libdir -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@"
;;
flags="$flags -L$libdir -ltag"
;;
--cflags)
flags="$flags -I$includedir -I$includedir/taglib@TAGLIB_INSTALL_SUFFIX@"
;;
flags="$flags -I$includedir/taglib"
;;
--version)
echo @TAGLIB_LIB_VERSION_STRING@
;;
echo ${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
;;
--prefix)
echo ${prefix:-@CMAKE_INSTALL_PREFIX@}
;;
*)
echo "$0: unknown option $1"
echo
usage
;;
echo $prefix
;;
*)
echo "$0: unknown option $1"
echo
usage
;;
esac
shift
done

View File

@@ -1,12 +0,0 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/taglib-targets.cmake")
set(TAGLIB_FOUND ${TagLib_FOUND})
set(TAGLIB_VERSION ${TagLib_VERSION})
check_required_components("TagLib")
if(NOT TARGET TagLib::TagLib)
add_library(TagLib::TagLib ALIAS TagLib::tag)
endif()

View File

@@ -1,35 +0,0 @@
@echo off
goto beginning
*
* It is what it is, you can do with it as you please.
*
* Just don't blame me if it teaches your computer to smoke!
*
* -Enjoy
* fh :)_~
*
:beginning
if /i "%1#" == "--libs#" goto doit
if /i "%1#" == "--cflags#" goto doit
if /i "%1#" == "--version#" goto doit
if /i "%1#" == "--prefix#" goto doit
echo usage: %0 [OPTIONS]
echo [--libs]
echo [--cflags]
echo [--version]
echo [--prefix]
goto theend
*
* NOTE: Windows does not assume libraries are prefixed with 'lib'.
* NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process
* to allow for static, shared or debug builds.
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
:doit
if /i "%1#" == "--libs#" echo -L${CMAKE_INSTALL_FULL_LIBDIR} -llibtag${TAGLIB_INSTALL_SUFFIX}
if /i "%1#" == "--cflags#" echo -I${CMAKE_INSTALL_FULL_INCLUDEDIR} -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
:theend

View File

@@ -1,11 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=@CMAKE_PC_LIBDIR@
includedir=@CMAKE_PC_INCLUDEDIR@
prefix=${CMAKE_INSTALL_PREFIX}
exec_prefix=${CMAKE_INSTALL_PREFIX}
libdir=${LIB_INSTALL_DIR}
includedir=${INCLUDE_INSTALL_DIR}
Name: TagLib
Description: Audio meta-data library
Requires:
Version: @TAGLIB_LIB_VERSION_STRING@
Libs: -L${libdir} -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@
Cflags: -I${includedir} -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@
Requires:
Version: ${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
Libs: -L${LIB_INSTALL_DIR} -ltag
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib

View File

@@ -1,77 +1,40 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(tag_HDR_DIRS
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/toolkit
${CMAKE_CURRENT_SOURCE_DIR}/asf
${CMAKE_CURRENT_SOURCE_DIR}/mpeg
${CMAKE_CURRENT_SOURCE_DIR}/ogg
${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
${CMAKE_CURRENT_SOURCE_DIR}/flac
${CMAKE_CURRENT_SOURCE_DIR}/mpc
${CMAKE_CURRENT_SOURCE_DIR}/mp4
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1
${CMAKE_CURRENT_SOURCE_DIR}/ape
${CMAKE_CURRENT_SOURCE_DIR}/wavpack
${CMAKE_CURRENT_SOURCE_DIR}/trueaudio
${CMAKE_CURRENT_SOURCE_DIR}/riff
${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff
${CMAKE_CURRENT_SOURCE_DIR}/riff/wav
${CMAKE_CURRENT_SOURCE_DIR}/mod
${CMAKE_CURRENT_SOURCE_DIR}/s3m
${CMAKE_CURRENT_SOURCE_DIR}/it
${CMAKE_CURRENT_SOURCE_DIR}/xm
)
if(WITH_ASF)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/asf
)
if(ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIR})
endif()
if(WITH_VORBIS)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/ogg
${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
${CMAKE_CURRENT_SOURCE_DIR}/flac
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus
)
endif()
if(WITH_APE)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/mpc
${CMAKE_CURRENT_SOURCE_DIR}/ape
${CMAKE_CURRENT_SOURCE_DIR}/wavpack
)
endif()
if(WITH_MP4)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/mp4
)
endif()
if(WITH_TRUEAUDIO)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/trueaudio
)
endif()
if(WITH_RIFF)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/riff
${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff
${CMAKE_CURRENT_SOURCE_DIR}/riff/wav
)
endif()
if(WITH_MOD)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/mod
${CMAKE_CURRENT_SOURCE_DIR}/s3m
${CMAKE_CURRENT_SOURCE_DIR}/it
${CMAKE_CURRENT_SOURCE_DIR}/xm
)
endif()
if(WITH_DSF)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/dsf
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
)
endif()
if(WITH_SHORTEN)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/shorten
)
endif()
include_directories(${tag_HDR_DIRS})
set(tag_HDRS
tag.h
fileref.h
audioproperties.h
taglib_export.h
${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
${CMAKE_BINARY_DIR}/taglib_config.h
toolkit/taglib.h
toolkit/tstring.h
toolkit/tlist.h
@@ -79,24 +42,18 @@ set(tag_HDRS
toolkit/tstringlist.h
toolkit/tbytevector.h
toolkit/tbytevectorlist.h
toolkit/tvariant.h
toolkit/tbytevectorstream.h
toolkit/tiostream.h
toolkit/tfile.h
toolkit/tfilestream.h
toolkit/tmap.h
toolkit/tmap.tcc
toolkit/tpicturetype.h
toolkit/tpropertymap.h
toolkit/tdebuglistener.h
toolkit/tversionnumber.h
mpeg/mpegfile.h
mpeg/mpegproperties.h
mpeg/mpegheader.h
mpeg/xingheader.h
mpeg/id3v1/id3v1tag.h
mpeg/id3v1/id3v1genres.h
mpeg/id3v2/id3v2.h
mpeg/id3v2/id3v2extendedheader.h
mpeg/id3v2/id3v2frame.h
mpeg/id3v2/id3v2header.h
@@ -106,120 +63,66 @@ set(tag_HDRS
mpeg/id3v2/id3v2tag.h
mpeg/id3v2/frames/attachedpictureframe.h
mpeg/id3v2/frames/commentsframe.h
mpeg/id3v2/frames/eventtimingcodesframe.h
mpeg/id3v2/frames/generalencapsulatedobjectframe.h
mpeg/id3v2/frames/ownershipframe.h
mpeg/id3v2/frames/popularimeterframe.h
mpeg/id3v2/frames/privateframe.h
mpeg/id3v2/frames/relativevolumeframe.h
mpeg/id3v2/frames/synchronizedlyricsframe.h
mpeg/id3v2/frames/textidentificationframe.h
mpeg/id3v2/frames/uniquefileidentifierframe.h
mpeg/id3v2/frames/unknownframe.h
mpeg/id3v2/frames/unsynchronizedlyricsframe.h
mpeg/id3v2/frames/urllinkframe.h
mpeg/id3v2/frames/chapterframe.h
mpeg/id3v2/frames/tableofcontentsframe.h
mpeg/id3v2/frames/podcastframe.h
ogg/oggfile.h
ogg/oggpage.h
ogg/oggpageheader.h
ogg/xiphcomment.h
ogg/vorbis/vorbisfile.h
ogg/vorbis/vorbisproperties.h
ogg/flac/oggflacfile.h
ogg/speex/speexfile.h
ogg/speex/speexproperties.h
flac/flacfile.h
flac/flacpicture.h
flac/flacproperties.h
flac/flacmetadatablock.h
ape/apefile.h
ape/apeproperties.h
ape/apetag.h
ape/apefooter.h
ape/apeitem.h
mpc/mpcfile.h
mpc/mpcproperties.h
wavpack/wavpackfile.h
wavpack/wavpackproperties.h
trueaudio/trueaudiofile.h
trueaudio/trueaudioproperties.h
riff/rifffile.h
riff/aiff/aifffile.h
riff/aiff/aiffproperties.h
riff/wav/wavfile.h
riff/wav/wavproperties.h
asf/asffile.h
asf/asfproperties.h
asf/asftag.h
asf/asfattribute.h
asf/asfpicture.h
mp4/mp4file.h
mp4/mp4atom.h
mp4/mp4tag.h
mp4/mp4item.h
mp4/mp4properties.h
mp4/mp4coverart.h
mod/modfilebase.h
mod/modfile.h
mod/modtag.h
mod/modproperties.h
it/itfile.h
it/itproperties.h
s3m/s3mfile.h
s3m/s3mproperties.h
xm/xmfile.h
xm/xmproperties.h
)
if(WITH_VORBIS)
set(tag_HDRS ${tag_HDRS}
ogg/oggfile.h
ogg/oggpage.h
ogg/oggpageheader.h
ogg/xiphcomment.h
ogg/vorbis/vorbisfile.h
ogg/vorbis/vorbisproperties.h
ogg/flac/oggflacfile.h
ogg/speex/speexfile.h
ogg/speex/speexproperties.h
ogg/opus/opusfile.h
ogg/opus/opusproperties.h
flac/flacfile.h
flac/flacpicture.h
flac/flacproperties.h
flac/flacmetadatablock.h
)
endif()
if(WITH_APE)
set(tag_HDRS ${tag_HDRS}
ape/apefile.h
ape/apeproperties.h
ape/apetag.h
ape/apefooter.h
ape/apeitem.h
mpc/mpcfile.h
mpc/mpcproperties.h
wavpack/wavpackfile.h
wavpack/wavpackproperties.h
)
endif()
if(WITH_TRUEAUDIO)
set(tag_HDRS ${tag_HDRS}
trueaudio/trueaudiofile.h
trueaudio/trueaudioproperties.h
)
endif()
if(WITH_RIFF)
set(tag_HDRS ${tag_HDRS}
riff/rifffile.h
riff/aiff/aifffile.h
riff/aiff/aiffproperties.h
riff/wav/wavfile.h
riff/wav/wavproperties.h
riff/wav/infotag.h
)
endif()
if(WITH_ASF)
set(tag_HDRS ${tag_HDRS}
asf/asffile.h
asf/asfproperties.h
asf/asftag.h
asf/asfattribute.h
asf/asfpicture.h
)
endif()
if(WITH_MP4)
set(tag_HDRS ${tag_HDRS}
mp4/mp4file.h
mp4/mp4atom.h
mp4/mp4tag.h
mp4/mp4item.h
mp4/mp4properties.h
mp4/mp4coverart.h
mp4/mp4itemfactory.h
)
endif()
if(WITH_MOD)
set(tag_HDRS ${tag_HDRS}
mod/modfilebase.h
mod/modfile.h
mod/modtag.h
mod/modproperties.h
it/itfile.h
it/itproperties.h
s3m/s3mfile.h
s3m/s3mproperties.h
xm/xmfile.h
xm/xmproperties.h
)
endif()
if(WITH_DSF)
set(tag_HDRS ${tag_HDRS}
dsf/dsffile.h
dsf/dsfproperties.h
dsdiff/dsdifffile.h
dsdiff/dsdiffproperties.h
dsdiff/dsdiffdiintag.h
)
endif()
if(WITH_SHORTEN)
set(tag_HDRS ${tag_HDRS}
shorten/shortenfile.h
shorten/shortenproperties.h
shorten/shortentag.h
)
endif()
set(mpeg_SRCS
mpeg/mpegfile.cpp
@@ -246,276 +149,169 @@ set(id3v2_SRCS
set(frames_SRCS
mpeg/id3v2/frames/attachedpictureframe.cpp
mpeg/id3v2/frames/commentsframe.cpp
mpeg/id3v2/frames/eventtimingcodesframe.cpp
mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp
mpeg/id3v2/frames/ownershipframe.cpp
mpeg/id3v2/frames/popularimeterframe.cpp
mpeg/id3v2/frames/privateframe.cpp
mpeg/id3v2/frames/relativevolumeframe.cpp
mpeg/id3v2/frames/synchronizedlyricsframe.cpp
mpeg/id3v2/frames/textidentificationframe.cpp
mpeg/id3v2/frames/uniquefileidentifierframe.cpp
mpeg/id3v2/frames/unknownframe.cpp
mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp
mpeg/id3v2/frames/urllinkframe.cpp
mpeg/id3v2/frames/chapterframe.cpp
mpeg/id3v2/frames/tableofcontentsframe.cpp
mpeg/id3v2/frames/podcastframe.cpp
)
if(WITH_VORBIS)
set(ogg_SRCS
ogg/oggfile.cpp
ogg/oggpage.cpp
ogg/oggpageheader.cpp
ogg/xiphcomment.cpp
)
set(ogg_SRCS
ogg/oggfile.cpp
ogg/oggpage.cpp
ogg/oggpageheader.cpp
ogg/xiphcomment.cpp
)
set(vorbis_SRCS
ogg/vorbis/vorbisfile.cpp
ogg/vorbis/vorbisproperties.cpp
)
set(vorbis_SRCS
ogg/vorbis/vorbisfile.cpp
ogg/vorbis/vorbisproperties.cpp
)
set(flacs_SRCS
flac/flacfile.cpp
flac/flacpicture.cpp
flac/flacproperties.cpp
flac/flacmetadatablock.cpp
flac/flacunknownmetadatablock.cpp
)
set(flacs_SRCS
flac/flacfile.cpp
flac/flacpicture.cpp
flac/flacproperties.cpp
flac/flacmetadatablock.cpp
flac/flacunknownmetadatablock.cpp
)
set(oggflacs_SRCS
ogg/flac/oggflacfile.cpp
)
set(oggflacs_SRCS
ogg/flac/oggflacfile.cpp
)
set(speex_SRCS
ogg/speex/speexfile.cpp
ogg/speex/speexproperties.cpp
)
set(mpc_SRCS
mpc/mpcfile.cpp
mpc/mpcproperties.cpp
)
set(opus_SRCS
ogg/opus/opusfile.cpp
ogg/opus/opusproperties.cpp
)
endif()
set(mp4_SRCS
mp4/mp4file.cpp
mp4/mp4atom.cpp
mp4/mp4tag.cpp
mp4/mp4item.cpp
mp4/mp4properties.cpp
mp4/mp4coverart.cpp
)
if(WITH_APE)
set(mpc_SRCS
mpc/mpcfile.cpp
mpc/mpcproperties.cpp
)
set(ape_SRCS
ape/apetag.cpp
ape/apefooter.cpp
ape/apeitem.cpp
ape/apefile.cpp
ape/apeproperties.cpp
)
set(ape_SRCS
ape/apetag.cpp
ape/apefooter.cpp
ape/apeitem.cpp
ape/apefile.cpp
ape/apeproperties.cpp
)
set(wavpack_SRCS
wavpack/wavpackfile.cpp
wavpack/wavpackproperties.cpp
)
set(wavpack_SRCS
wavpack/wavpackfile.cpp
wavpack/wavpackproperties.cpp
)
endif()
set(speex_SRCS
ogg/speex/speexfile.cpp
ogg/speex/speexproperties.cpp
)
if(WITH_MP4)
set(mp4_SRCS
mp4/mp4file.cpp
mp4/mp4atom.cpp
mp4/mp4tag.cpp
mp4/mp4item.cpp
mp4/mp4properties.cpp
mp4/mp4coverart.cpp
mp4/mp4itemfactory.cpp
)
endif()
set(trueaudio_SRCS
trueaudio/trueaudiofile.cpp
trueaudio/trueaudioproperties.cpp
)
if(WITH_TRUEAUDIO)
set(trueaudio_SRCS
trueaudio/trueaudiofile.cpp
trueaudio/trueaudioproperties.cpp
)
endif()
set(asf_SRCS
asf/asftag.cpp
asf/asffile.cpp
asf/asfproperties.cpp
asf/asfattribute.cpp
asf/asfpicture.cpp
)
if(WITH_ASF)
set(asf_SRCS
asf/asftag.cpp
asf/asffile.cpp
asf/asfproperties.cpp
asf/asfattribute.cpp
asf/asfpicture.cpp
)
endif()
set(riff_SRCS
riff/rifffile.cpp
)
if(WITH_RIFF)
set(riff_SRCS
riff/rifffile.cpp
)
set(aiff_SRCS
riff/aiff/aifffile.cpp
riff/aiff/aiffproperties.cpp
)
set(aiff_SRCS
riff/aiff/aifffile.cpp
riff/aiff/aiffproperties.cpp
)
set(wav_SRCS
riff/wav/wavfile.cpp
riff/wav/wavproperties.cpp
)
set(wav_SRCS
riff/wav/wavfile.cpp
riff/wav/wavproperties.cpp
riff/wav/infotag.cpp
)
endif()
set(mod_SRCS
mod/modfilebase.cpp
mod/modfile.cpp
mod/modtag.cpp
mod/modproperties.cpp
)
if(WITH_MOD)
set(mod_SRCS
mod/modfilebase.cpp
mod/modfile.cpp
mod/modtag.cpp
mod/modproperties.cpp
)
set(s3m_SRCS
s3m/s3mfile.cpp
s3m/s3mproperties.cpp
)
set(s3m_SRCS
s3m/s3mfile.cpp
s3m/s3mproperties.cpp
)
set(it_SRCS
it/itfile.cpp
it/itproperties.cpp
)
set(it_SRCS
it/itfile.cpp
it/itproperties.cpp
)
set(xm_SRCS
xm/xmfile.cpp
xm/xmproperties.cpp
)
endif()
if(WITH_DSF)
set(dsf_SRCS
dsf/dsffile.cpp
dsf/dsfproperties.cpp
)
set(dsdiff_SRCS
dsdiff/dsdifffile.cpp
dsdiff/dsdiffproperties.cpp
dsdiff/dsdiffdiintag.cpp
)
endif()
if(WITH_SHORTEN)
set(shorten_SRCS
shorten/shortenfile.cpp
shorten/shortenproperties.cpp
shorten/shortentag.cpp
)
endif()
set(xm_SRCS
xm/xmfile.cpp
xm/xmproperties.cpp
)
set(toolkit_SRCS
toolkit/tstring.cpp
toolkit/tstringlist.cpp
toolkit/tbytevector.cpp
toolkit/tbytevectorlist.cpp
toolkit/tvariant.cpp
toolkit/tbytevectorstream.cpp
toolkit/tiostream.cpp
toolkit/tfile.cpp
toolkit/tfilestream.cpp
toolkit/tdebug.cpp
toolkit/tpicturetype.cpp
toolkit/tpropertymap.cpp
toolkit/tdebuglistener.cpp
toolkit/tzlib.cpp
toolkit/tversionnumber.cpp
toolkit/unicode.cpp
)
set(tag_LIB_SRCS
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
${dsf_SRCS} ${dsdiff_SRCS} ${shorten_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS}
tag.cpp
tagunion.cpp
fileref.cpp
audioproperties.cpp
tagutils.cpp
)
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
target_include_directories(tag INTERFACE
$<INSTALL_INTERFACE:include>
$<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}>
)
target_link_libraries(tag
PRIVATE $<IF:$<TARGET_EXISTS:utf8::cpp>,utf8::cpp,$<$<TARGET_EXISTS:utf8cpp>:utf8cpp>>
$<$<TARGET_EXISTS:ZLIB::ZLIB>:ZLIB::ZLIB>
)
if(ZLIB_FOUND)
target_link_libraries(tag ${ZLIB_LIBRARIES})
endif()
set_target_properties(tag PROPERTIES
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
DEFINE_SYMBOL MAKE_TAGLIB_LIB
INTERFACE_LINK_LIBRARIES "${ZLIB_INTERFACE_LINK_LIBRARIES}"
LINK_INTERFACE_LIBRARIES ""
PUBLIC_HEADER "${tag_HDRS}"
)
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(tag PUBLIC TAGLIB_STATIC)
endif()
if(VISIBILITY_HIDDEN)
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
set_target_properties(tag PROPERTIES CXX_VISIBILITY_PRESET hidden)
endif()
if(BUILD_FRAMEWORK)
unset(INSTALL_NAME_DIR)
set_target_properties(tag PROPERTIES
FRAMEWORK TRUE
MACOSX_RPATH 1
VERSION "A"
SOVERSION "A"
)
endif()
if(TAGLIB_INSTALL_SUFFIX)
if(BUILD_SHARED_LIBS)
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}")
else()
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif()
set_target_properties(tag PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX})
set_target_properties(tag PROPERTIES FRAMEWORK TRUE)
endif()
install(TARGETS tag
EXPORT taglibTargets
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/taglib-config.cmake.in"
"${PROJECT_BINARY_DIR}/taglib-config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/taglib-config-version.cmake"
VERSION "${TAGLIB_LIB_VERSION_STRING}"
COMPATIBILITY AnyNewerVersion
)
install(EXPORT taglibTargets
FILE taglib-targets.cmake
NAMESPACE TagLib::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
)
install(FILES "${PROJECT_BINARY_DIR}/taglib-config.cmake"
"${PROJECT_BINARY_DIR}/taglib-config-version.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
)

View File

@@ -49,8 +49,8 @@ APE Tag Version 2.000 (with header, recommended):
APE tag items should be sorted ascending by size. When streaming, parts of the
APE tag may be dropped to reduce the danger of drop outs between tracks. This
is not required, but is strongly recommended. It would be desirable for the
items to be sorted by importance / size, but this is not feasible. This
is not required, but is strongly recommended. It would be desirable for the i
tems to be sorted by importance / size, but this is not feasible. This
convention should only be broken when adding less important small items and it
is not desirable to rewrite the entire tag. An APE tag at the end of a file
(the recommended location) must have at least a footer; an APE tag at the

View File

@@ -31,14 +31,14 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tstring.h>
#include <tdebug.h>
#include <tagunion.h>
#include <id3v1tag.h>
#include "apefile.h"
#include "tdebug.h"
#include "tpropertymap.h"
#include "id3v1tag.h"
#include "id3v2header.h"
#include "tagunion.h"
#include "tagutils.h"
#include "apetag.h"
#include "apefooter.h"
@@ -46,86 +46,72 @@ using namespace TagLib;
namespace
{
enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
} // namespace
enum { APEIndex, ID3v1Index };
}
class APE::File::FilePrivate
{
public:
offset_t APELocation { -1 };
long APESize { 0 };
FilePrivate() :
APELocation(-1),
APESize(0),
ID3v1Location(-1),
properties(0),
hasAPE(false),
hasID3v1(false) {}
offset_t ID3v1Location { -1 };
~FilePrivate()
{
delete properties;
}
std::unique_ptr<ID3v2::Header> ID3v2Header;
offset_t ID3v2Location { -1 };
long ID3v2Size { 0 };
long APELocation;
uint APESize;
long ID3v1Location;
TagUnion tag;
std::unique_ptr<Properties> properties;
Properties *properties;
// These indicate whether the file *on disk* has these tags, not if
// this data structure does. This is used in computing offsets.
bool hasAPE;
bool hasID3v1;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool APE::File::isSupported(IOStream *stream)
{
// An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return buffer.find("MAC ") >= 0;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(std::make_unique<FilePrivate>())
APE::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
{
if(isOpen())
read(readProperties);
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>())
APE::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
{
if(isOpen())
read(readProperties);
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
APE::File::~File() = default;
APE::File::~File()
{
delete d;
}
TagLib::Tag *APE::File::tag() const
{
return &d->tag;
}
PropertyMap APE::File::properties() const
{
return d->tag.properties();
}
void APE::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap APE::File::setProperties(const PropertyMap &properties)
{
if(ID3v1Tag())
ID3v1Tag()->setProperties(properties);
return APETag(true)->setProperties(properties);
}
APE::Properties *APE::File::audioProperties() const
{
return d->properties.get();
return d->properties;
}
bool APE::File::save()
@@ -137,158 +123,155 @@ bool APE::File::save()
// Update ID3v1 tag
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
// ID3v1 tag is not empty. Update the old one or create a new one.
if(d->ID3v1Location >= 0) {
if(ID3v1Tag()) {
if(d->hasID3v1) {
seek(d->ID3v1Location);
writeBlock(ID3v1Tag()->render());
}
else {
seek(0, End);
d->ID3v1Location = tell();
writeBlock(ID3v1Tag()->render());
d->hasID3v1 = true;
}
writeBlock(ID3v1Tag()->render());
}
else {
// ID3v1 tag is empty. Remove the old one.
if(d->ID3v1Location >= 0) {
truncate(d->ID3v1Location);
d->ID3v1Location = -1;
if(d->hasID3v1) {
removeBlock(d->ID3v1Location, 128);
d->hasID3v1 = false;
if(d->hasAPE) {
if(d->APELocation > d->ID3v1Location)
d->APELocation -= 128;
}
}
}
// Update APE tag
if(APETag() && !APETag()->isEmpty()) {
// APE tag is not empty. Update the old one or create a new one.
if(d->APELocation < 0) {
if(d->ID3v1Location >= 0)
if(APETag()) {
if(d->hasAPE)
insert(APETag()->render(), d->APELocation, d->APESize);
else {
if(d->hasID3v1) {
insert(APETag()->render(), d->ID3v1Location, 0);
d->APESize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
d->APELocation = d->ID3v1Location;
else
d->APELocation = length();
d->ID3v1Location += d->APESize;
}
else {
seek(0, End);
d->APELocation = tell();
writeBlock(APETag()->render());
d->APESize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
}
}
const ByteVector data = APETag()->render();
insert(data, d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += static_cast<long>(data.size()) - d->APESize;
d->APESize = data.size();
}
else {
// APE tag is empty. Remove the old one.
if(d->APELocation >= 0) {
if(d->hasAPE) {
removeBlock(d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->APESize;
d->APELocation = -1;
d->APESize = 0;
d->hasAPE = false;
if(d->hasID3v1) {
if(d->ID3v1Location > d->APELocation) {
d->ID3v1Location -= d->APESize;
}
}
}
}
return true;
return true;
}
ID3v1::Tag *APE::File::ID3v1Tag(bool create)
{
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, create);
return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
}
APE::Tag *APE::File::APETag(bool create)
{
return d->tag.access<APE::Tag>(ApeAPEIndex, create);
return d->tag.access<APE::Tag>(APEIndex, create);
}
void APE::File::strip(int tags)
{
if(tags & ID3v1)
d->tag.set(ApeID3v1Index, nullptr);
if(tags & APE)
d->tag.set(ApeAPEIndex, nullptr);
if(!ID3v1Tag())
if(tags & ID3v1) {
d->tag.set(ID3v1Index, 0);
APETag(true);
}
}
bool APE::File::hasAPETag() const
{
return d->APELocation >= 0;
}
if(tags & APE) {
d->tag.set(APEIndex, 0);
bool APE::File::hasID3v1Tag() const
{
return d->ID3v1Location >= 0;
if(!ID3v1Tag())
APETag(true);
}
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void APE::File::read(bool readProperties)
void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
{
// Look for an ID3v2 tag
d->ID3v2Location = Utils::findID3v2(this);
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location);
d->ID3v2Header = std::make_unique<ID3v2::Header>(readBlock(ID3v2::Header::size()));
d->ID3v2Size = d->ID3v2Header->completeTagSize();
}
// Look for an ID3v1 tag
d->ID3v1Location = Utils::findID3v1(this);
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0)
d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
// Look for an APE tag
d->APELocation = Utils::findAPE(this, d->ID3v1Location);
d->APELocation = findAPE();
if(d->APELocation >= 0) {
d->tag.set(ApeAPEIndex, new APE::Tag(this, d->APELocation));
d->tag.set(APEIndex, new APE::Tag(this, d->APELocation));
d->APESize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APE::Footer::size() - d->APESize;
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
d->hasAPE = true;
}
if(d->ID3v1Location < 0)
if(!d->hasID3v1)
APETag(true);
// Look for APE audio properties
if(readProperties) {
offset_t streamLength;
if(d->APELocation >= 0)
streamLength = d->APELocation;
else if(d->ID3v1Location >= 0)
streamLength = d->ID3v1Location;
else
streamLength = length();
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location + d->ID3v2Size);
streamLength -= d->ID3v2Location + d->ID3v2Size;
}
else {
seek(0);
}
d->properties = std::make_unique<Properties>(this, streamLength);
d->properties = new Properties(this);
}
}
long APE::File::findAPE()
{
if(!isValid())
return -1;
if(d->hasID3v1)
seek(-160, End);
else
seek(-32, End);
long p = tell();
if(readBlock(8) == APE::Tag::fileIdentifier())
return p;
return -1;
}
long APE::File::findID3v1()
{
if(!isValid())
return -1;
seek(-128, End);
long p = tell();
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
return -1;
}

View File

@@ -48,7 +48,7 @@ namespace TagLib {
//! An implementation of APE metadata
/*!
* This is an implementation of APE metadata.
* This is implementation of APE metadata.
*
* This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
* properties from the file.
@@ -59,7 +59,7 @@ namespace TagLib {
//! An implementation of TagLib::File with APE specific methods
/*!
* This implements and provides an interface for APE files to the
* This implements and provides an interface APE WavPack 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 APE files.
@@ -84,22 +84,17 @@ namespace TagLib {
};
/*!
* Constructs an APE file from \a file. If \a readProperties is \c true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
* Contructs an WavPack 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);
/*!
* Constructs an APE file from \a stream. If \a readProperties is \c 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.
* Contructs an WavPack 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(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -107,42 +102,19 @@ namespace TagLib {
/*!
* Destroys this instance of the File.
*/
~File() override;
File(const File &) = delete;
File &operator=(const File &) = delete;
virtual ~File();
/*!
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
* or a combination of the two.
*/
TagLib::Tag *tag() const override;
/*!
* 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 override;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties) override;
/*!
* Implements the unified property interface -- import function.
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
* tag will be updated as well.
*/
PropertyMap setProperties(const PropertyMap &) override;
virtual TagLib::Tag *tag() const;
/*!
* Returns the APE::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Properties *audioProperties() const override;
virtual Properties *audioProperties() const;
/*!
* Saves the file.
@@ -150,43 +122,32 @@ namespace TagLib {
* \note According to the official Monkey's Audio SDK, an APE file
* can only have either ID3V1 or APE tags, so a parameter is used here.
*/
bool save() override;
virtual bool save();
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is \c false (the default) this may return a null pointer
* if there is no valid ID3v1 tag. If \a create is \c true it will create
* 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.
* If \a create is false (the default) this will return a null pointer
* 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
* new ID3v1 tag will be placed after it.
*
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasID3v1Tag()
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
/*!
* Returns a pointer to the APE tag of the file.
*
* If \a create is \c false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is \c true it will create
* 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.
* If \a create is false (the default) this will return a null pointer
* if there is no valid APE tag. If \a create is true it will create
* a APE tag if one does not exist.
*
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasAPETag()
*/
APE::Tag *APETag(bool create = false);
@@ -200,37 +161,19 @@ namespace TagLib {
*/
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;
/*!
* Returns whether or not the given \a stream can be opened as an APE
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
void read(bool readProperties);
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void scan();
long findID3v1();
long findAPE();
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
FilePrivate *d;
};
} // namespace APE
} // namespace TagLib
}
}
#endif

View File

@@ -24,134 +24,151 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "apefooter.h"
#include <ostream>
#include <bitset>
#include "taglib.h"
#include <tstring.h>
#include <tdebug.h>
#include "apefooter.h"
using namespace TagLib;
using namespace APE;
class APE::Footer::FooterPrivate
class Footer::FooterPrivate
{
public:
unsigned int version { 0 };
FooterPrivate() : version(0),
footerPresent(true),
headerPresent(false),
isHeader(false),
itemCount(0),
tagSize(0) {}
bool footerPresent { true };
bool headerPresent { false };
~FooterPrivate() {}
bool isHeader { false };
uint version;
unsigned int itemCount { 0 };
unsigned int tagSize { 0 };
bool footerPresent;
bool headerPresent;
bool isHeader;
uint itemCount;
uint tagSize;
static const uint size = 32;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
unsigned int APE::Footer::size()
TagLib::uint Footer::size()
{
return 32;
return FooterPrivate::size;
}
ByteVector APE::Footer::fileIdentifier()
ByteVector Footer::fileIdentifier()
{
return ByteVector("APETAGEX");
return ByteVector::fromCString("APETAGEX");
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::Footer::Footer() :
d(std::make_unique<FooterPrivate>())
Footer::Footer()
{
d = new FooterPrivate;
}
APE::Footer::Footer(const ByteVector &data) :
d(std::make_unique<FooterPrivate>())
Footer::Footer(const ByteVector &data)
{
d = new FooterPrivate;
parse(data);
}
APE::Footer::~Footer() = default;
Footer::~Footer()
{
delete d;
}
unsigned int APE::Footer::version() const
TagLib::uint Footer::version() const
{
return d->version;
}
bool APE::Footer::headerPresent() const
bool Footer::headerPresent() const
{
return d->headerPresent;
}
bool APE::Footer::footerPresent() const
bool Footer::footerPresent() const
{
return d->footerPresent;
}
bool APE::Footer::isHeader() const
bool Footer::isHeader() const
{
return d->isHeader;
}
void APE::Footer::setHeaderPresent(bool b) const
void Footer::setHeaderPresent(bool b) const
{
d->headerPresent = b;
}
unsigned int APE::Footer::itemCount() const
TagLib::uint Footer::itemCount() const
{
return d->itemCount;
}
void APE::Footer::setItemCount(unsigned int s)
void Footer::setItemCount(uint s)
{
d->itemCount = s;
}
unsigned int APE::Footer::tagSize() const
TagLib::uint Footer::tagSize() const
{
return d->tagSize;
}
unsigned int APE::Footer::completeTagSize() const
TagLib::uint Footer::completeTagSize() const
{
if(d->headerPresent)
return d->tagSize + size();
return d->tagSize;
return d->tagSize + d->size;
else
return d->tagSize;
}
void APE::Footer::setTagSize(unsigned int s)
void Footer::setTagSize(uint s)
{
d->tagSize = s;
}
void APE::Footer::setData(const ByteVector &data)
void Footer::setData(const ByteVector &data)
{
parse(data);
}
ByteVector APE::Footer::renderFooter() const
ByteVector Footer::renderFooter() const
{
return render(false);
return render(false);
}
ByteVector APE::Footer::renderHeader() const
ByteVector Footer::renderHeader() const
{
if(!d->headerPresent)
return ByteVector();
return render(true);
if (!d->headerPresent) return ByteVector();
return render(true);
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void APE::Footer::parse(const ByteVector &data)
void Footer::parse(const ByteVector &data)
{
if(data.size() < size())
return;
@@ -160,19 +177,19 @@ void APE::Footer::parse(const ByteVector &data)
// Read the version number
d->version = data.toUInt(8, false);
d->version = data.mid(8, 4).toUInt(false);
// Read the tag size
d->tagSize = data.toUInt(12, false);
d->tagSize = data.mid(12, 4).toUInt(false);
// Read the item count
d->itemCount = data.toUInt(16, false);
d->itemCount = data.mid(16, 4).toUInt(false);
// Read the flags
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(20, 4).toUInt(false)));
d->headerPresent = flags[31];
d->footerPresent = !flags[30];
@@ -180,7 +197,7 @@ void APE::Footer::parse(const ByteVector &data)
}
ByteVector APE::Footer::render(bool isHeader) const
ByteVector Footer::render(bool isHeader) const
{
ByteVector v;
@@ -209,7 +226,7 @@ ByteVector APE::Footer::render(bool isHeader) const
flags[30] = false; // footer is always present
flags[29] = isHeader;
v.append(ByteVector::fromUInt(static_cast<unsigned int>(flags.to_ulong()), false));
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
// add the reserved 64bit

View File

@@ -37,7 +37,7 @@ namespace TagLib {
/*!
* This class implements APE footers (and headers). It attempts to follow, both
* semantically and programmatically, the structure specified in
* semantically and programatically, the structure specified in
* the APE v2.0 standard. The API is based on the properties of APE footer and
* headers specified there.
*/
@@ -59,28 +59,25 @@ namespace TagLib {
/*!
* Destroys the footer.
*/
~Footer();
Footer(const Footer &) = delete;
Footer &operator=(const Footer &) = delete;
virtual ~Footer();
/*!
* Returns the version number. (Note: This is the 1000 or 2000.)
*/
unsigned int version() const;
uint version() const;
/*!
* Returns \c true if a header is present in the tag.
* Returns true if a header is present in the tag.
*/
bool headerPresent() const;
/*!
* Returns \c true if a footer is present in the tag.
* Returns true if a footer is present in the tag.
*/
bool footerPresent() const;
/*!
* Returns \c true if this is actually the header.
* Returns true this is actually the header.
*/
bool isHeader() const;
@@ -92,13 +89,13 @@ namespace TagLib {
/*!
* Returns the number of items in the tag.
*/
unsigned int itemCount() const;
uint itemCount() const;
/*!
* Set the item count to \a s.
* \see itemCount()
*/
void setItemCount(unsigned int s);
void setItemCount(uint s);
/*!
* Returns the tag size in bytes. This is the size of the frame content and footer.
@@ -106,7 +103,7 @@ namespace TagLib {
*
* \see completeTagSize()
*/
unsigned int tagSize() const;
uint tagSize() const;
/*!
* Returns the tag size, including if present, the header
@@ -114,18 +111,18 @@ namespace TagLib {
*
* \see tagSize()
*/
unsigned int completeTagSize() const;
uint completeTagSize() const;
/*!
* Set the tag size to \a s.
* \see tagSize()
*/
void setTagSize(unsigned int s);
void setTagSize(uint s);
/*!
* Returns the size of the footer. Presently this is always 32 bytes.
*/
static unsigned int size();
static uint size();
/*!
* Returns the string used to identify an APE tag inside of a file.
@@ -145,8 +142,8 @@ namespace TagLib {
ByteVector renderFooter() const;
/*!
* Renders the header corresponding to the footer. If headerPresent() is
* \c false, it returns an empty ByteVector.
* Renders the header corresponding to the footer. If headerPresent is
* set to false, it returns an empty ByteVector.
*/
ByteVector renderHeader() const;
@@ -163,12 +160,14 @@ namespace TagLib {
ByteVector render(bool isHeader) const;
private:
Footer(const Footer &);
Footer &operator=(const Footer &);
class FooterPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FooterPrivate> d;
FooterPrivate *d;
};
} // namespace APE
} // namespace TagLib
}
}
#endif

View File

@@ -23,75 +23,62 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevectorlist.h>
#include <tdebug.h>
#include "apeitem.h"
#include <utility>
#include <numeric>
#include "tdebug.h"
using namespace TagLib;
using namespace APE;
class APE::Item::ItemPrivate
{
public:
Item::ItemTypes type { Text };
ItemPrivate() : type(Text), readOnly(false) {}
Item::ItemTypes type;
String key;
ByteVector value;
StringList text;
bool readOnly { false };
bool readOnly;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::Item::Item() :
d(std::make_unique<ItemPrivate>())
APE::Item::Item()
{
d = new ItemPrivate;
}
APE::Item::Item(const String &key, const StringList &values) :
d(std::make_unique<ItemPrivate>())
APE::Item::Item(const String &key, const String &value)
{
d = new ItemPrivate;
d->key = key;
d->text.append(value);
}
APE::Item::Item(const String &key, const StringList &values)
{
d = new ItemPrivate;
d->key = key;
d->text = values;
}
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
d(std::make_unique<ItemPrivate>())
APE::Item::Item(const Item &item)
{
d->key = key;
if(binary) {
d->type = Binary;
d->value = value;
}
else {
d->text.append(value);
}
d = new ItemPrivate(*item.d);
}
APE::Item::Item(const Item &item) :
d(std::make_unique<ItemPrivate>(*item.d))
APE::Item::~Item()
{
delete d;
}
APE::Item::~Item() = default;
Item &APE::Item::operator=(const Item &item)
{
Item(item).swap(*this);
delete d;
d = new ItemPrivate(*item.d);
return *this;
}
void APE::Item::swap(Item &item) noexcept
{
using std::swap;
swap(d, item.d);
}
void APE::Item::setReadOnly(bool readOnly)
{
d->readOnly = readOnly;
@@ -117,70 +104,47 @@ String APE::Item::key() const
return d->key;
}
ByteVector APE::Item::binaryData() const
ByteVector APE::Item::value() const
{
return d->value;
}
// This seems incorrect as it won't be actually rendering the value to keep it
// up to date.
void APE::Item::setBinaryData(const ByteVector &value)
{
d->type = Binary;
d->value = value;
d->text.clear();
return d->value;
}
void APE::Item::setKey(const String &key)
{
d->key = key;
d->key = key;
}
void APE::Item::setValue(const String &value)
{
d->type = Text;
d->text = value;
d->value.clear();
d->text = value;
}
void APE::Item::setValues(const StringList &values)
void APE::Item::setValues(const StringList &value)
{
d->type = Text;
d->text = values;
d->value.clear();
d->text = value;
}
void APE::Item::appendValue(const String &value)
{
d->type = Text;
d->text.append(value);
d->value.clear();
d->text.append(value);
}
void APE::Item::appendValues(const StringList &values)
{
d->type = Text;
d->text.append(values);
d->value.clear();
d->text.append(values);
}
int APE::Item::size() const
{
int result = 8 + d->key.size() + 1;
switch(d->type) {
case Text:
if(!d->text.isEmpty()) {
result = std::accumulate(d->text.cbegin(), d->text.cend(), result,
[](int sz, const String &t) {
return sz + 1 + t.data(String::UTF8).size();
}) - 1;
}
break;
return 8 + d->key.size() + 1 + d->value.size();
}
case Binary:
case Locator:
result += d->value.size();
break;
}
return result;
StringList APE::Item::toStringList() const
{
return d->text;
}
StringList APE::Item::values() const
@@ -190,19 +154,19 @@ StringList APE::Item::values() const
String APE::Item::toString() const
{
if(d->type == Text && !isEmpty())
return d->text.front();
return String();
return isEmpty() ? String::null : d->text.front();
}
bool APE::Item::isEmpty() const
{
switch(d->type) {
case Text:
case Binary:
if(d->text.isEmpty())
return true;
return d->text.size() == 1 && d->text.front().isEmpty();
case Binary:
if(d->text.size() == 1 && d->text.front().isEmpty())
return true;
return false;
case Locator:
return d->value.isEmpty();
default:
@@ -219,52 +183,48 @@ void APE::Item::parse(const ByteVector &data)
return;
}
const unsigned int valueLength = data.toUInt(0, false);
const unsigned int flags = data.toUInt(4, false);
uint valueLength = data.mid(0, 4).toUInt(false);
uint flags = data.mid(4, 4).toUInt(false);
// An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8.
// We assume that the validity of the given key has been checked.
d->key = String(data.mid(8), String::UTF8);
d->key = String(&data[8], String::Latin1);
const ByteVector val = data.mid(8 + d->key.size() + 1, valueLength);
d->value = data.mid(8 + d->key.size() + 1, valueLength);
setReadOnly(flags & 1);
setType(static_cast<ItemTypes>((flags >> 1) & 3));
setType(ItemTypes((flags >> 1) & 3));
if(Text == d->type)
d->text = StringList(ByteVectorList::split(val, '\0'), String::UTF8);
else
d->value = val;
if(int(d->type) < 2)
d->text = StringList(ByteVectorList::split(d->value, '\0'), String::UTF8);
}
ByteVector APE::Item::render() const
{
ByteVector data;
unsigned int flags = (d->readOnly ? 1 : 0) | (d->type << 1);
ByteVector val;
TagLib::uint flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
ByteVector value;
if(isEmpty())
return data;
if(d->type == Text) {
auto it = d->text.cbegin();
StringList::ConstIterator it = d->text.begin();
val.append(it->data(String::UTF8));
for(it = std::next(it); it != d->text.cend(); ++it) {
val.append('\0');
val.append(it->data(String::UTF8));
value.append(it->data(String::UTF8));
it++;
for(; it != d->text.end(); ++it) {
value.append('\0');
value.append(it->data(String::UTF8));
}
d->value = val;
d->value = value;
}
else
val.append(d->value);
value.append(d->value);
data.append(ByteVector::fromUInt(val.size(), false));
data.append(ByteVector::fromUInt(value.size(), false));
data.append(ByteVector::fromUInt(flags, false));
data.append(d->key.data(String::Latin1));
data.append(d->key.data(String::UTF8));
data.append(ByteVector('\0'));
data.append(val);
data.append(value);
return data;
}

View File

@@ -31,7 +31,9 @@
#include "tstringlist.h"
namespace TagLib {
namespace APE {
//! An implementation of APE-items
/*!
@@ -57,15 +59,15 @@ namespace TagLib {
Item();
/*!
* Constructs a text item with \a key and \a values.
* Constructs an item with \a key and \a value.
*/
Item(const String &key, const StringList &values);
// BIC: Remove this, StringList has a constructor from a single string
Item(const String &key, const String &value);
/*!
* Constructs an item with \a key and \a value.
* If \a binary is \c true a Binary item will be created, otherwise \a value will be interpreted as text
* Constructs an item with \a key and \a values.
*/
Item(const String &key, const ByteVector &value, bool binary);
Item(const String &key, const StringList &values);
/*!
* Construct an item as a copy of \a item.
@@ -75,18 +77,13 @@ namespace TagLib {
/*!
* Destroys the item.
*/
~Item();
virtual ~Item();
/*!
* Copies the contents of \a item into this item.
*/
Item &operator=(const Item &item);
/*!
* Exchanges the content of this item with the content of \a item.
*/
void swap(Item &item) noexcept;
/*!
* Returns the key.
*/
@@ -94,15 +91,12 @@ namespace TagLib {
/*!
* 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);
ByteVector value() const;
/*!
* Sets the key for the item to \a key.
@@ -110,31 +104,31 @@ namespace TagLib {
void setKey(const String &key);
/*!
* Sets the text value of the item to \a value and clears any previous contents.
* Sets the value of the item to \a value and clears any previous contents.
*
* \see toString()
*/
void setValue(const String &value);
/*!
* Sets the text value of the item to the list of values in \a value and clears
* Sets the value of the item to the list of values in \a value and clears
* any previous contents.
*
* \see values()
* \see toStringList()
*/
void setValues(const StringList &values);
/*!
* Appends \a value to create (or extend) the current list of text values.
* Appends \a value to create (or extend) the current list of values.
*
* \see toString()
*/
void appendValue(const String &value);
/*!
* Appends \a values to extend the current list of text values.
* Appends \a values to extend the current list of values.
*
* \see values()
* \see toStringList()
*/
void appendValues(const StringList &values);
@@ -144,15 +138,19 @@ namespace TagLib {
int size() const;
/*!
* Returns the value as a single string. In case of multiple strings,
* the first is returned. If the data type is not \a Text, always returns
* an empty String.
* Returns the value as a single string. In case of multiple strings,
* the first is returned.
*/
String toString() const;
/*!
* Returns the list of text values. If the data type is not \a Text, always
* returns an empty StringList.
* \deprecated
* \see values
*/
StringList toStringList() const;
/*!
* Returns the list of values.
*/
StringList values() const;
@@ -172,16 +170,16 @@ namespace TagLib {
void setReadOnly(bool readOnly);
/*!
* Return \c true if the item is read-only.
* Return true if the item is read-only.
*/
bool isReadOnly() const;
/*!
* Sets the type of the item to \a val.
* Sets the type of the item to \a type.
*
* \see ItemTypes
*/
void setType(ItemTypes val);
void setType(ItemTypes type);
/*!
* Returns the type of the item.
@@ -189,16 +187,18 @@ namespace TagLib {
ItemTypes type() const;
/*!
* Returns \c false if the item has any real content.
* Returns if the item has any real content.
*/
bool isEmpty() const;
private:
class ItemPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<ItemPrivate> d;
ItemPrivate *d;
};
} // namespace APE
} // namespace TagLib
}
}
#endif

View File

@@ -27,41 +27,54 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstring.h>
#include <tdebug.h>
#include <bitset>
#include "id3v2tag.h"
#include "apeproperties.h"
#include "tdebug.h"
#include "apefile.h"
#include "apefooter.h"
#include "apetag.h"
using namespace TagLib;
class APE::Properties::PropertiesPrivate
{
public:
int length { 0 };
int bitrate { 0 };
int sampleRate { 0 };
int channels { 0 };
int version { 0 };
int bitsPerSample { 0 };
unsigned int sampleFrames { 0 };
PropertiesPrivate(File *file, long streamLength) :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
version(0),
bitsPerSample(0),
file(file),
streamLength(streamLength) {}
int length;
int bitrate;
int sampleRate;
int channels;
int version;
int bitsPerSample;
File *file;
long streamLength;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::Properties::Properties(File *file, offset_t streamLength, ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
APE::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
{
read(file, streamLength);
d = new PropertiesPrivate(file, file->length());
read();
}
APE::Properties::~Properties() = default;
APE::Properties::~Properties()
{
delete d;
}
int APE::Properties::lengthInMilliseconds() const
int APE::Properties::length() const
{
return d->length;
}
@@ -91,130 +104,121 @@ int APE::Properties::bitsPerSample() const
return d->bitsPerSample;
}
unsigned int APE::Properties::sampleFrames() const
{
return d->sampleFrames;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
namespace
void APE::Properties::read()
{
int headerVersion(const ByteVector &header)
{
if(header.size() < 6 || !header.startsWith("MAC "))
return -1;
return header.toUShort(4, false);
}
} // namespace
void APE::Properties::read(File *file, offset_t streamLength)
{
// First, we assume that the file pointer is set at the first descriptor.
offset_t offset = file->tell();
int vers = headerVersion(file->readBlock(6));
// Next, we look for the descriptor.
if(vers < 0) {
offset = file->find("MAC ", offset);
file->seek(offset);
vers = headerVersion(file->readBlock(6));
}
if(vers < 0) {
debug("APE::Properties::read() -- APE descriptor not found");
// First we are searching the descriptor
long offset = findDescriptor();
if(offset < 0)
return;
// Then we read the header common for all versions of APE
d->file->seek(offset);
ByteVector commonHeader=d->file->readBlock(6);
if(!commonHeader.startsWith("MAC "))
return;
d->version = commonHeader.mid(4).toUInt(false);
if(d->version >= 3980) {
analyzeCurrent();
}
d->version = vers;
if(d->version >= 3980)
analyzeCurrent(file);
else
analyzeOld(file);
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
else {
analyzeOld();
}
}
void APE::Properties::analyzeCurrent(File *file)
long APE::Properties::findDescriptor()
{
long ID3v2Location = findID3v2();
long ID3v2OriginalSize = 0;
bool hasID3v2 = false;
if(ID3v2Location >= 0) {
ID3v2::Tag tag(d->file, ID3v2Location, 0);
ID3v2OriginalSize = tag.header()->completeTagSize();
if(tag.header()->tagSize() > 0)
hasID3v2 = true;
}
long offset = 0;
if(hasID3v2)
offset = d->file->find("MAC ", ID3v2Location + ID3v2OriginalSize);
else
offset = d->file->find("MAC ");
if(offset < 0) {
debug("APE::Properties::findDescriptor() -- APE descriptor not found");
return -1;
}
return offset;
}
long APE::Properties::findID3v2()
{
if(!d->file->isValid())
return -1;
d->file->seek(0);
if(d->file->readBlock(3) == ID3v2::Header::fileIdentifier())
return 0;
return -1;
}
void APE::Properties::analyzeCurrent()
{
// Read the descriptor
file->seek(2, File::Current);
const ByteVector descriptor = file->readBlock(44);
if(descriptor.size() < 44) {
debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
return;
}
d->file->seek(2, File::Current);
ByteVector descriptor = d->file->readBlock(44);
uint descriptorBytes = descriptor.mid(0,4).toUInt(false);
if(const unsigned int descriptorBytes = descriptor.toUInt(0, false);
descriptorBytes - 52 > 0)
file->seek(descriptorBytes - 52, File::Current);
if ((descriptorBytes - 52) > 0)
d->file->seek(descriptorBytes - 52, File::Current);
// Read the header
const ByteVector header = file->readBlock(24);
if(header.size() < 24) {
debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
return;
}
ByteVector header = d->file->readBlock(24);
// Get the APE info
d->channels = header.toShort(18, false);
d->sampleRate = header.toUInt(20, false);
d->bitsPerSample = header.toShort(16, false);
d->channels = header.mid(18, 2).toShort(false);
d->sampleRate = header.mid(20, 4).toUInt(false);
d->bitsPerSample = header.mid(16, 2).toShort(false);
//d->compressionLevel =
const unsigned int totalFrames = header.toUInt(12, false);
if(totalFrames == 0)
return;
const unsigned int blocksPerFrame = header.toUInt(4, false);
const unsigned int finalFrameBlocks = header.toUInt(8, false);
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
uint totalFrames = header.mid(12, 4).toUInt(false);
uint blocksPerFrame = header.mid(4, 4).toUInt(false);
uint finalFrameBlocks = header.mid(8, 4).toUInt(false);
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = totalBlocks / d->sampleRate;
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}
void APE::Properties::analyzeOld(File *file)
void APE::Properties::analyzeOld()
{
const ByteVector header = file->readBlock(26);
if(header.size() < 26) {
debug("APE::Properties::analyzeOld() -- MAC header is too short.");
return;
}
const unsigned int totalFrames = header.toUInt(18, false);
ByteVector header = d->file->readBlock(26);
uint totalFrames = header.mid(18, 4).toUInt(false);
// Fail on 0 length APE files (catches non-finalized APE files)
if(totalFrames == 0)
return;
const short compressionLevel = header.toShort(0, false);
unsigned int blocksPerFrame;
short compressionLevel = header.mid(0, 2).toShort(false);
uint blocksPerFrame;
if(d->version >= 3950)
blocksPerFrame = 73728 * 4;
else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
blocksPerFrame = 73728;
else
blocksPerFrame = 9216;
// Get the APE info
d->channels = header.toShort(4, false);
d->sampleRate = header.toUInt(6, false);
const unsigned int finalFrameBlocks = header.toUInt(22, false);
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
// Get the bit depth from the RIFF-fmt chunk.
file->seek(16, File::Current);
const ByteVector fmt = file->readBlock(28);
if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
debug("APE::Properties::analyzeOld() -- fmt header is too short.");
return;
}
d->bitsPerSample = fmt.toShort(26, false);
d->channels = header.mid(4, 2).toShort(false);
d->sampleRate = header.mid(6, 4).toUInt(false);
uint finalFrameBlocks = header.mid(22, 4).toUInt(false);
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = totalBlocks / d->sampleRate;
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}

View File

@@ -30,7 +30,6 @@
#ifndef TAGLIB_APEPROPERTIES_H
#define TAGLIB_APEPROPERTIES_H
#include "taglib.h"
#include "taglib_export.h"
#include "audioproperties.h"
@@ -52,66 +51,48 @@ namespace TagLib {
public:
/*!
* Create an instance of APE::Properties with the data read from the
* APE::File \a file.
* ByteVector \a data.
*/
Properties(File *file, offset_t streamLength, ReadStyle style = Average);
Properties(File *f, ReadStyle style = Average);
/*!
* Destroys this APE::Properties instance.
*/
~Properties() override;
virtual ~Properties();
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
int lengthInMilliseconds() const override;
/*!
* Returns the average bit rate of the file in kb/s.
*/
int bitrate() const override;
/*!
* Returns the sample rate in Hz.
*/
int sampleRate() const override;
/*!
* Returns the number of audio channels.
*/
int channels() const override;
/*!
* Returns the number of bits per audio sample.
* Returns number of bits per sample.
*/
int bitsPerSample() const;
/*!
* Returns the total number of audio samples in file.
*/
unsigned int sampleFrames() const;
/*!
* Returns the APE version.
* Returns APE version.
*/
int version() const;
private:
void read(File *file, offset_t streamLength);
Properties(const Properties &);
Properties &operator=(const Properties &);
void analyzeCurrent(File *file);
void analyzeOld(File *file);
void read();
long findDescriptor();
long findID3v2();
void analyzeCurrent();
void analyzeOld();
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
PropertiesPrivate *d;
};
} // namespace APE
} // namespace TagLib
}
}
#endif

View File

@@ -23,7 +23,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5130)
#ifdef __SUNPRO_CC
// Sun Studio finds multiple specializations of Map because
// it considers specializations with and without class types
// to be different; this define forces Map to use only the
@@ -31,47 +31,28 @@
#define WANT_CLASS_INSTANTIATION_OF_MAP (1)
#endif
#include <tfile.h>
#include <tstring.h>
#include <tmap.h>
#include "apetag.h"
#include <array>
#include <utility>
#include "tdebug.h"
#include "tfile.h"
#include "tpropertymap.h"
#include "apefooter.h"
#include "apeitem.h"
using namespace TagLib;
using namespace APE;
namespace
{
constexpr unsigned int MinKeyLength = 2;
constexpr unsigned int MaxKeyLength = 255;
const String FRONT_COVER("COVER ART (FRONT)");
const String BACK_COVER("COVER ART (BACK)");
bool isKeyValid(const ByteVector &key)
{
static constexpr std::array invalidKeys { "ID3", "TAG", "OGGS", "MP+" };
// only allow printable ASCII including space (32..126)
return std::none_of(key.begin(), key.end(),
[](unsigned char c) { return c < 32 || c > 126; })
&& std::none_of(invalidKeys.begin(), invalidKeys.end(),
[upperKey = String(key).upper()](auto k) { return upperKey == k; });
}
} // namespace
class APE::Tag::TagPrivate
{
public:
File *file { nullptr };
offset_t footerLocation { 0 };
TagPrivate() : file(0), footerLocation(-1), tagLength(0) {}
File *file;
long footerLocation;
long tagLength;
Footer footer;
ItemListMap itemListMap;
};
@@ -79,21 +60,24 @@ public:
// public methods
////////////////////////////////////////////////////////////////////////////////
APE::Tag::Tag() :
d(std::make_unique<TagPrivate>())
APE::Tag::Tag() : TagLib::Tag()
{
d = new TagPrivate;
}
APE::Tag::Tag(TagLib::File *file, offset_t footerLocation) :
d(std::make_unique<TagPrivate>())
APE::Tag::Tag(File *file, long footerLocation) : TagLib::Tag()
{
d = new TagPrivate;
d->file = file;
d->footerLocation = footerLocation;
read();
}
APE::Tag::~Tag() = default;
APE::Tag::~Tag()
{
delete d;
}
ByteVector APE::Tag::fileIdentifier()
{
@@ -102,44 +86,51 @@ ByteVector APE::Tag::fileIdentifier()
String APE::Tag::title() const
{
Item val = d->itemListMap.value("TITLE");
return val.isEmpty() ? String() : joinTagValues(val.values());
if(d->itemListMap["TITLE"].isEmpty())
return String::null;
return d->itemListMap["TITLE"].toString();
}
String APE::Tag::artist() const
{
Item val = d->itemListMap.value("ARTIST");
return val.isEmpty() ? String() : joinTagValues(val.values());
if(d->itemListMap["ARTIST"].isEmpty())
return String::null;
return d->itemListMap["ARTIST"].toString();
}
String APE::Tag::album() const
{
Item val = d->itemListMap.value("ALBUM");
return val.isEmpty() ? String() : joinTagValues(val.values());
if(d->itemListMap["ALBUM"].isEmpty())
return String::null;
return d->itemListMap["ALBUM"].toString();
}
String APE::Tag::comment() const
{
Item val = d->itemListMap.value("COMMENT");
return val.isEmpty() ? String() : joinTagValues(val.values());
if(d->itemListMap["COMMENT"].isEmpty())
return String::null;
return d->itemListMap["COMMENT"].toString();
}
String APE::Tag::genre() const
{
Item val = d->itemListMap.value("GENRE");
return val.isEmpty() ? String() : joinTagValues(val.values());
if(d->itemListMap["GENRE"].isEmpty())
return String::null;
return d->itemListMap["GENRE"].toString();
}
unsigned int APE::Tag::year() const
TagLib::uint APE::Tag::year() const
{
Item val = d->itemListMap.value("YEAR");
return val.isEmpty() ? 0 : val.toString().toInt();
if(d->itemListMap["YEAR"].isEmpty())
return 0;
return d->itemListMap["YEAR"].toString().toInt();
}
unsigned int APE::Tag::track() const
TagLib::uint APE::Tag::track() const
{
Item val = d->itemListMap.value("TRACK");
return val.isEmpty() ? 0 : val.toString().toInt();
if(d->itemListMap["TRACK"].isEmpty())
return 0;
return d->itemListMap["TRACK"].toString().toInt();
}
void APE::Tag::setTitle(const String &s)
@@ -167,203 +158,22 @@ void APE::Tag::setGenre(const String &s)
addValue("GENRE", s, true);
}
void APE::Tag::setYear(unsigned int i)
void APE::Tag::setYear(uint i)
{
if(i == 0)
if(i <= 0)
removeItem("YEAR");
else
addValue("YEAR", String::number(i), true);
}
void APE::Tag::setTrack(unsigned int i)
void APE::Tag::setTrack(uint i)
{
if(i == 0)
if(i <= 0)
removeItem("TRACK");
else
addValue("TRACK", String::number(i), true);
}
namespace
{
// conversions of tag keys between what we use in PropertyMap and what's usual
// for APE tags
// usual, APE
constexpr std::array keyConversions {
std::pair("TRACKNUMBER", "TRACK"),
std::pair("DATE", "YEAR"),
std::pair("ALBUMARTIST", "ALBUM ARTIST"),
std::pair("DISCNUMBER", "DISC"),
std::pair("REMIXER", "MIXARTIST"),
std::pair("RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS"),
std::pair("RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE"),
};
} // namespace
PropertyMap APE::Tag::properties() const
{
PropertyMap properties;
for(const auto &[tag, item] : std::as_const(itemListMap())) {
// if the item is Binary or Locator, or if the key is an invalid string,
// add to unsupportedData
if(String tagName = tag.upper();
item.type() != Item::Text || tagName.isEmpty()) {
properties.addUnsupportedData(tag);
}
else {
// Some tags need to be handled specially
for(const auto &[k, t] : keyConversions) {
if(tagName == t)
tagName = k;
}
properties[tagName].append(item.values());
}
}
return properties;
}
void APE::Tag::removeUnsupportedProperties(const StringList &properties)
{
for(const auto &property : properties)
removeItem(property);
}
PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap props(origProps); // make a local copy that can be modified
// see comment in properties()
for(const auto &[k, t] : keyConversions)
if(props.contains(k)) {
props.insert(t, props[k]);
props.erase(k);
}
// first check if tags need to be removed completely
StringList toRemove;
for(const auto &[k, t] : std::as_const(itemListMap())) {
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
if(String key = k.upper();
!key.isEmpty() && t.type() == APE::Item::Text && !props.contains(key))
toRemove.append(k);
}
for(const auto &item : std::as_const(toRemove))
removeItem(item);
// now sync in the "forward direction"
PropertyMap invalid;
for(const auto &[tagName, val] : std::as_const(props)) {
if(!checkKey(tagName))
invalid.insert(tagName, val);
else if(!itemListMap().contains(tagName) || itemListMap()[tagName].values() != val) {
if(val.isEmpty())
removeItem(tagName);
else {
addValue(tagName, *val.begin(), true);
for(auto it = std::next(val.begin()); it != val.end(); ++it)
addValue(tagName, *it, false);
}
}
}
return invalid;
}
StringList APE::Tag::complexPropertyKeys() const
{
StringList keys;
if(d->itemListMap.contains(FRONT_COVER) ||
d->itemListMap.contains(BACK_COVER)) {
keys.append("PICTURE");
}
return keys;
}
List<VariantMap> APE::Tag::complexProperties(const String &key) const
{
List<VariantMap> props;
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
const StringList itemNames = StringList(FRONT_COVER).append(BACK_COVER);
for(const auto &itemName: itemNames) {
if(d->itemListMap.contains(itemName)) {
if(Item picture = d->itemListMap.value(itemName);
picture.type() == Item::Binary) {
ByteVector data = picture.binaryData();
// Do not search for a description if the first byte could start JPG or PNG
// data.
int index = data.isEmpty() || data.at(0) == '\xff' || data.at(0) == '\x89'
? -1 : data.find('\0');
String description;
if(index >= 0) {
description = String(data.mid(0, index), String::UTF8);
data = data.mid(index + 1);
}
VariantMap property;
property.insert("data", data);
if(!description.isEmpty()) {
property.insert("description", description);
}
property.insert("pictureType",
itemName == BACK_COVER ? "Back Cover" : "Front Cover");
props.append(property);
}
}
}
}
return props;
}
bool APE::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removeItem(FRONT_COVER);
removeItem(BACK_COVER);
auto frontItems = List<Item>();
auto backItems = List<Item>();
for(const auto &property : value) {
auto data = property.value("description").value<String>().data(String::UTF8)
.append('\0')
.append(property.value("data").value<ByteVector>());
auto pictureType = property.value("pictureType").value<String>();
Item item;
item.setType(Item::Binary);
item.setBinaryData(data);
if(pictureType == "Back Cover") {
item.setKey(BACK_COVER);
backItems.append(item);
}
else if(pictureType == "Front Cover") {
item.setKey(FRONT_COVER);
// prioritize pictures with correct type
frontItems.prepend(item);
}
else {
item.setKey(FRONT_COVER);
frontItems.append(item);
}
}
if(!frontItems.isEmpty()) {
setItem(FRONT_COVER, frontItems.front());
}
if(!backItems.isEmpty()) {
setItem(BACK_COVER, backItems.front());
}
}
else {
return false;
}
return true;
}
bool APE::Tag::checkKey(const String &key)
{
if(key.size() < MinKeyLength || key.size() > MaxKeyLength)
return false;
return isKeyValid(key.data(String::UTF8));
}
APE::Footer *APE::Tag::footer() const
{
return &d->footer;
@@ -376,46 +186,26 @@ const APE::ItemListMap& APE::Tag::itemListMap() const
void APE::Tag::removeItem(const String &key)
{
d->itemListMap.erase(key.upper());
Map<const String, Item>::Iterator it = d->itemListMap.find(key.upper());
if(it != d->itemListMap.end())
d->itemListMap.erase(it);
}
void APE::Tag::addValue(const String &key, const String &value, bool replace)
{
if(replace)
removeItem(key);
if(value.isEmpty())
return;
// Text items may contain more than one value.
// Binary or locator items may have only one value, hence always replaced.
auto it = d->itemListMap.find(key.upper());
if(it != d->itemListMap.end() && it->second.type() == Item::Text)
it->second.appendValue(value);
else
setItem(key, Item(key, value));
}
void APE::Tag::setData(const String &key, const ByteVector &value)
{
removeItem(key);
if(value.isEmpty())
return;
setItem(key, Item(key, value, true));
if(!value.isEmpty()) {
if(d->itemListMap.contains(key) || !replace)
d->itemListMap[key.upper()].appendValue(value);
else
setItem(key, Item(key, value));
}
}
void APE::Tag::setItem(const String &key, const Item &item)
{
if(!checkKey(key)) {
debug("APE::Tag::setItem() - Couldn't set an item due to an invalid key.");
return;
}
d->itemListMap[key.upper()] = item;
d->itemListMap.insert(key.upper(), item);
}
bool APE::Tag::isEmpty() const
@@ -435,7 +225,7 @@ void APE::Tag::read()
d->footer.setData(d->file->readBlock(Footer::size()));
if(d->footer.tagSize() <= Footer::size() ||
d->footer.tagSize() > static_cast<unsigned long>(d->file->length()))
d->footer.tagSize() > uint(d->file->length()))
return;
d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
@@ -446,11 +236,15 @@ void APE::Tag::read()
ByteVector APE::Tag::render() const
{
ByteVector data;
unsigned int itemCount = 0;
uint itemCount = 0;
for(const auto &[_, list] : std::as_const(d->itemListMap)) {
data.append(list.render());
itemCount++;
{
for(Map<const String, Item>::ConstIterator it = d->itemListMap.begin();
it != d->itemListMap.end(); ++it)
{
data.append(it->second.render());
itemCount++;
}
}
d->footer.setItemCount(itemCount);
@@ -462,42 +256,16 @@ ByteVector APE::Tag::render() const
void APE::Tag::parse(const ByteVector &data)
{
uint pos = 0;
// 11 bytes is the minimum size for an APE item
if(data.size() < 11)
return;
for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
APE::Item item;
item.parse(data.mid(pos));
unsigned int pos = 0;
d->itemListMap.insert(item.key().upper(), item);
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
const int nullPos = data.find('\0', pos + 8);
if(nullPos < 0) {
debug("APE::Tag::parse() - Couldn't find a key/value separator. Stopped parsing.");
return;
}
const unsigned int keyLength = nullPos - pos - 8;
const unsigned int valLength = data.toUInt(pos, false);
if(valLength >= data.size() || pos > data.size() - valLength) {
debug("APE::Tag::parse() - Invalid val length. Stopped parsing.");
return;
}
if(keyLength >= MinKeyLength
&& keyLength <= MaxKeyLength
&& isKeyValid(data.mid(pos + 8, keyLength)))
{
APE::Item item;
item.parse(data.mid(pos));
d->itemListMap.insert(item.key().upper(), item);
}
else {
debug("APE::Tag::parse() - Skipped an item due to an invalid key.");
}
pos += keyLength + valLength + 9;
pos += item.size();
}
}

View File

@@ -26,12 +26,12 @@
#ifndef TAGLIB_APETAG_H
#define TAGLIB_APETAG_H
#include "tag.h"
#include "tbytevector.h"
#include "tmap.h"
#include "tstring.h"
#include "taglib.h"
#include "taglib_export.h"
#include "tag.h"
#include "apeitem.h"
namespace TagLib {
@@ -49,7 +49,8 @@ namespace TagLib {
*
* \see APE::Tag::itemListMap()
*/
using ItemListMap = Map<const String, Item>;
typedef Map<const String, Item> ItemListMap;
//! An APE tag implementation
@@ -65,15 +66,12 @@ namespace TagLib {
* Create an APE tag and parse the data in \a file with APE footer at
* \a tagOffset.
*/
Tag(TagLib::File *file, offset_t footerLocation);
Tag(TagLib::File *file, long footerLocation);
/*!
* Destroys this Tag instance.
*/
~Tag() override;
Tag(const Tag &) = delete;
Tag &operator=(const Tag &) = delete;
virtual ~Tag();
/*!
* Renders the in memory values to a ByteVector suitable for writing to
@@ -89,57 +87,21 @@ namespace TagLib {
// Reimplementations.
String title() const override;
String artist() const override;
String album() const override;
String comment() const override;
String genre() const override;
unsigned int year() const override;
unsigned int track() const override;
virtual String title() const;
virtual String artist() const;
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual uint year() const;
virtual uint track() const;
void setTitle(const String &s) override;
void setArtist(const String &s) override;
void setAlbum(const String &s) override;
void setComment(const String &s) override;
void setGenre(const String &s) override;
void setYear(unsigned int i) override;
void setTrack(unsigned int i) override;
/*!
* 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,
* (and a few other keys, see \ref p_propertymapping)
* in order to be compliant with the names used in other formats.
*/
PropertyMap properties() const override;
void removeUnsupportedProperties(const StringList &properties) override;
/*!
* 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 &) override;
StringList complexPropertyKeys() const override;
List<VariantMap> complexProperties(const String &key) const override;
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
/*!
* Check if the given String is a valid APE tag key.
*/
static bool checkKey(const String&);
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(uint i);
virtual void setTrack(uint i);
/*!
* Returns a pointer to the tag's footer.
@@ -150,7 +112,7 @@ namespace TagLib {
* Returns a reference to the item list map. This is an ItemListMap of
* all of the items in the tag.
*
* This is the most powerful structure for accessing the items of the tag.
* This is the most powerfull structure for accessing the items of the tag.
*
* APE tags are case-insensitive, all keys in this map have been converted
* to upper case.
@@ -166,19 +128,12 @@ namespace TagLib {
void removeItem(const String &key);
/*!
* Adds to the text item specified by \a key the data \a value. If \a replace
* is \c true, then all of the other values on the same key will be removed
* first. If a binary item exists for \a key it will be removed first.
* Adds to the 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
* first.
*/
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
* present, it will be replaced.
@@ -186,9 +141,9 @@ namespace TagLib {
void setItem(const String &key, const Item &item);
/*!
* Returns \c true if the tag does not contain any data.
* Returns true if the tag does not contain any data.
*/
bool isEmpty() const override;
bool isEmpty() const;
protected:
@@ -203,11 +158,13 @@ namespace TagLib {
void parse(const ByteVector &data);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<TagPrivate> d;
TagPrivate *d;
};
} // namespace APE
} // namespace TagLib
}
}
#endif

View File

@@ -23,103 +23,118 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include "asfattribute.h"
#include "tdebug.h"
#include "asffile.h"
#include "asfutils.h"
using namespace TagLib;
class ASF::Attribute::AttributePrivate
class ASF::Attribute::AttributePrivate : public RefCounter
{
public:
AttributePrivate() :
pictureValue(ASF::Picture::fromInvalid())
{
}
AttributeTypes type { UnicodeType };
AttributePrivate()
: pictureValue(ASF::Picture::fromInvalid()),
stream(0),
language(0) {}
AttributeTypes type;
String stringValue;
ByteVector byteVectorValue;
ASF::Picture pictureValue;
unsigned long long numericValue { 0 };
int stream { 0 };
int language { 0 };
union {
unsigned int intValue;
unsigned short shortValue;
unsigned long long longLongValue;
bool boolValue;
};
int stream;
int language;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::Attribute::Attribute() :
d(std::make_shared<AttributePrivate>())
ASF::Attribute::Attribute()
{
d = new AttributePrivate;
d->type = UnicodeType;
}
ASF::Attribute::Attribute(const ASF::Attribute &) = default;
ASF::Attribute::Attribute(const String &value) :
d(std::make_shared<AttributePrivate>())
ASF::Attribute::Attribute(const ASF::Attribute &other)
: d(other.d)
{
d->ref();
}
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
{
if(d->deref())
delete d;
d = other.d;
d->ref();
return *this;
}
ASF::Attribute::~Attribute()
{
if(d->deref())
delete d;
}
ASF::Attribute::Attribute(const String &value)
{
d = new AttributePrivate;
d->type = UnicodeType;
d->stringValue = value;
}
ASF::Attribute::Attribute(const ByteVector &value) :
d(std::make_shared<AttributePrivate>())
ASF::Attribute::Attribute(const ByteVector &value)
{
d = new AttributePrivate;
d->type = BytesType;
d->byteVectorValue = value;
}
ASF::Attribute::Attribute(const ASF::Picture &value) :
d(std::make_shared<AttributePrivate>())
ASF::Attribute::Attribute(const ASF::Picture &value)
{
d = new AttributePrivate;
d->type = BytesType;
d->pictureValue = value;
}
ASF::Attribute::Attribute(unsigned int value) :
d(std::make_shared<AttributePrivate>())
ASF::Attribute::Attribute(unsigned int value)
{
d = new AttributePrivate;
d->type = DWordType;
d->numericValue = value;
d->intValue = value;
}
ASF::Attribute::Attribute(unsigned long long value) :
d(std::make_shared<AttributePrivate>())
ASF::Attribute::Attribute(unsigned long long value)
{
d = new AttributePrivate;
d->type = QWordType;
d->numericValue = value;
d->longLongValue = value;
}
ASF::Attribute::Attribute(unsigned short value) :
d(std::make_shared<AttributePrivate>())
ASF::Attribute::Attribute(unsigned short value)
{
d = new AttributePrivate;
d->type = WordType;
d->numericValue = value;
d->shortValue = value;
}
ASF::Attribute::Attribute(bool value) :
d(std::make_shared<AttributePrivate>())
ASF::Attribute::Attribute(bool value)
{
d = new AttributePrivate;
d->type = BoolType;
d->numericValue = value;
d->boolValue = value;
}
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &) = default;
void ASF::Attribute::swap(Attribute &other) noexcept
{
using std::swap;
swap(d, other.d);
}
ASF::Attribute::~Attribute() = default;
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
{
return d->type;
@@ -139,22 +154,22 @@ ByteVector ASF::Attribute::toByteVector() const
unsigned short ASF::Attribute::toBool() const
{
return d->numericValue ? 1 : 0;
return d->shortValue;
}
unsigned short ASF::Attribute::toUShort() const
{
return static_cast<unsigned short>(d->numericValue);
return d->shortValue;
}
unsigned int ASF::Attribute::toUInt() const
{
return static_cast<unsigned int>(d->numericValue);
return d->intValue;
}
unsigned long long ASF::Attribute::toULongLong() const
{
return d->numericValue;
return d->longLongValue;
}
ASF::Picture ASF::Attribute::toPicture() const
@@ -162,30 +177,30 @@ ASF::Picture ASF::Attribute::toPicture() const
return d->pictureValue;
}
String ASF::Attribute::parse(ASF::File &file, int kind)
String ASF::Attribute::parse(ASF::File &f, int kind)
{
unsigned int size, nameLength;
uint size, nameLength;
String name;
d->pictureValue = Picture::fromInvalid();
// extended content descriptor
if(kind == 0) {
nameLength = readWORD(&file);
name = readString(&file, nameLength);
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file));
size = readWORD(&file);
nameLength = f.readWORD();
name = f.readString(nameLength);
d->type = ASF::Attribute::AttributeTypes(f.readWORD());
size = f.readWORD();
}
// metadata & metadata library
else {
int temp = readWORD(&file);
int temp = f.readWORD();
// metadata library
if(kind == 2) {
d->language = temp;
}
d->stream = readWORD(&file);
nameLength = readWORD(&file);
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file));
size = readDWORD(&file);
name = readString(&file, nameLength);
d->stream = f.readWORD();
nameLength = f.readWORD();
d->type = ASF::Attribute::AttributeTypes(f.readWORD());
size = f.readDWORD();
name = f.readString(nameLength);
}
if(kind != 2 && size > 65535) {
@@ -194,33 +209,33 @@ String ASF::Attribute::parse(ASF::File &file, int kind)
switch(d->type) {
case WordType:
d->numericValue = readWORD(&file);
d->shortValue = f.readWORD();
break;
case BoolType:
if(kind == 0) {
d->numericValue = readDWORD(&file) != 0;
d->boolValue = f.readDWORD() == 1;
}
else {
d->numericValue = readWORD(&file) != 0;
d->boolValue = f.readWORD() == 1;
}
break;
case DWordType:
d->numericValue = readDWORD(&file);
d->intValue = f.readDWORD();
break;
case QWordType:
d->numericValue = readQWORD(&file);
d->longLongValue = f.readQWORD();
break;
case UnicodeType:
d->stringValue = readString(&file, size);
d->stringValue = f.readString(size);
break;
case BytesType:
case GuidType:
d->byteVectorValue = file.readBlock(size);
d->byteVectorValue = f.readBlock(size);
break;
}
@@ -240,6 +255,7 @@ int ASF::Attribute::dataSize() const
case WordType:
return 2;
case BoolType:
return 4;
case DWordType:
return 4;
case QWordType:
@@ -247,10 +263,8 @@ int ASF::Attribute::dataSize() const
case UnicodeType:
return d->stringValue.size() * 2 + 2;
case BytesType:
if(d->pictureValue.isValid()) {
if(d->pictureValue.isValid())
return d->pictureValue.dataSize();
}
return d->byteVectorValue.size();
case GuidType:
return d->byteVectorValue.size();
}
@@ -263,55 +277,52 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
switch (d->type) {
case WordType:
data.append(ByteVector::fromShort(toUShort(), false));
data.append(ByteVector::fromShort(d->shortValue, false));
break;
case BoolType:
if(kind == 0) {
data.append(ByteVector::fromUInt(toBool(), false));
data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false));
}
else {
data.append(ByteVector::fromShort(toBool(), false));
data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false));
}
break;
case DWordType:
data.append(ByteVector::fromUInt(toUInt(), false));
data.append(ByteVector::fromUInt(d->intValue, false));
break;
case QWordType:
data.append(ByteVector::fromLongLong(toULongLong(), false));
data.append(ByteVector::fromLongLong(d->longLongValue, false));
break;
case UnicodeType:
data.append(renderString(d->stringValue));
data.append(File::renderString(d->stringValue));
break;
case BytesType:
if(d->pictureValue.isValid()) {
data.append(d->pictureValue.render());
break;
}
else {
data.append(d->byteVectorValue);
}
break;
case GuidType:
data.append(d->byteVectorValue);
break;
}
if(kind == 0) {
data = renderString(name, true) +
ByteVector::fromShort(static_cast<short>(d->type), false) +
ByteVector::fromShort(static_cast<short>(data.size()), false) +
data = File::renderString(name, true) +
ByteVector::fromShort((int)d->type, false) +
ByteVector::fromShort(data.size(), false) +
data;
}
else {
ByteVector nameData = renderString(name);
data = ByteVector::fromShort(static_cast<short>(kind == 2 ? d->language : 0), false) +
ByteVector::fromShort(static_cast<short>(d->stream), false) +
ByteVector::fromShort(static_cast<short>(nameData.size()), false) +
ByteVector::fromShort(static_cast<short>(d->type), false) +
ByteVector nameData = File::renderString(name);
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
ByteVector::fromShort(d->stream, false) +
ByteVector::fromShort(nameData.size(), false) +
ByteVector::fromShort((int)d->type, false) +
ByteVector::fromUInt(data.size(), false) +
nameData +
data;
@@ -339,3 +350,4 @@ void ASF::Attribute::setStream(int value)
{
d->stream = value;
}

View File

@@ -33,13 +33,13 @@
namespace TagLib
{
namespace ASF
{
class File;
class Picture;
//! Attribute of ASF (WMA) metadata
class TAGLIB_EXPORT Attribute
{
public:
@@ -70,7 +70,7 @@ namespace TagLib
/*!
* 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.
@@ -108,25 +108,20 @@ namespace TagLib
/*!
* Construct an attribute as a copy of \a other.
*/
Attribute(const Attribute &other);
Attribute(const Attribute &item);
/*!
* Copies the contents of \a other into this item.
*/
Attribute &operator=(const Attribute &other);
/*!
* Exchanges the content of the Attribute with the content of \a other.
*/
void swap(Attribute &other) noexcept;
ASF::Attribute &operator=(const Attribute &other);
/*!
* Destroys the attribute.
*/
~Attribute();
virtual ~Attribute();
/*!
* Returns the type of the value.
* Returns type of the value.
*/
AttributeTypes type() const;
@@ -199,10 +194,10 @@ namespace TagLib
ByteVector render(const String &name, int kind = 0) const;
class AttributePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::shared_ptr<AttributePrivate> d;
AttributePrivate *d;
};
} // namespace ASF
} // namespace TagLib
}
}
#endif

View File

@@ -23,260 +23,210 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tdebug.h>
#include <tbytevectorlist.h>
#include <tstring.h>
#include "asffile.h"
#include <utility>
#include "tdebug.h"
#include "tpropertymap.h"
#include "tbytevectorlist.h"
#include "tagutils.h"
#include "asftag.h"
#include "asfproperties.h"
#include "asfutils.h"
using namespace TagLib;
class ASF::File::FilePrivate
{
public:
class BaseObject;
class UnknownObject;
class FilePropertiesObject;
class StreamPropertiesObject;
class ContentDescriptionObject;
class ExtendedContentDescriptionObject;
class HeaderExtensionObject;
class CodecListObject;
class MetadataObject;
class MetadataLibraryObject;
FilePrivate()
{
objects.setAutoDelete(true);
}
~FilePrivate() = default;
FilePrivate(const FilePrivate &) = delete;
FilePrivate &operator=(const FilePrivate &) = delete;
unsigned long long headerSize { 0 };
std::unique_ptr<ASF::Tag> tag;
std::unique_ptr<ASF::Properties> properties;
List<BaseObject *> objects;
ContentDescriptionObject *contentDescriptionObject { nullptr };
ExtendedContentDescriptionObject *extendedContentDescriptionObject { nullptr };
HeaderExtensionObject *headerExtensionObject { nullptr };
MetadataObject *metadataObject { nullptr };
MetadataLibraryObject *metadataLibraryObject { nullptr };
FilePrivate():
size(0),
tag(0),
properties(0),
contentDescriptionObject(0),
extendedContentDescriptionObject(0),
headerExtensionObject(0),
metadataObject(0),
metadataLibraryObject(0) {}
unsigned long long size;
ASF::Tag *tag;
ASF::Properties *properties;
List<ASF::File::BaseObject *> objects;
ASF::File::ContentDescriptionObject *contentDescriptionObject;
ASF::File::ExtendedContentDescriptionObject *extendedContentDescriptionObject;
ASF::File::HeaderExtensionObject *headerExtensionObject;
ASF::File::MetadataObject *metadataObject;
ASF::File::MetadataLibraryObject *metadataLibraryObject;
};
namespace
{
const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16);
const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
} // namespace
static ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
static ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
static ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
static ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 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 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::FilePrivate::BaseObject
class ASF::File::BaseObject
{
public:
ByteVector data;
virtual ~BaseObject() = default;
virtual ByteVector guid() const = 0;
virtual void parse(ASF::File *file, long long size);
virtual ~BaseObject() {}
virtual ByteVector guid() = 0;
virtual void parse(ASF::File *file, unsigned int size);
virtual ByteVector render(ASF::File *file);
};
class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::BaseObject
class ASF::File::UnknownObject : public ASF::File::BaseObject
{
ByteVector myGuid;
public:
UnknownObject(const ByteVector &guid);
ByteVector guid() const override;
ByteVector guid();
};
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
class ASF::File::FilePropertiesObject : public ASF::File::BaseObject
{
public:
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector guid();
void parse(ASF::File *file, uint size);
};
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
class ASF::File::StreamPropertiesObject : public ASF::File::BaseObject
{
public:
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector guid();
void parse(ASF::File *file, uint size);
};
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
class ASF::File::ContentDescriptionObject : public ASF::File::BaseObject
{
public:
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector render(ASF::File *file);
};
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
class ASF::File::ExtendedContentDescriptionObject : public ASF::File::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector render(ASF::File *file);
};
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
class ASF::File::MetadataObject : public ASF::File::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector render(ASF::File *file);
};
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
class ASF::File::MetadataLibraryObject : public ASF::File::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector render(ASF::File *file);
};
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
class ASF::File::HeaderExtensionObject : public ASF::File::BaseObject
{
public:
List<ASF::File::FilePrivate::BaseObject *> objects;
HeaderExtensionObject();
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
List<ASF::File::BaseObject *> objects;
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector render(ASF::File *file);
};
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
private:
enum CodecType
{
Video = 0x0001,
Audio = 0x0002,
Unknown = 0xFFFF
};
};
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, long long size)
void ASF::File::BaseObject::parse(ASF::File *file, unsigned int size)
{
data.clear();
if(size > 24 && size <= file->length())
if (size > 24 && size <= (unsigned int)(file->length()))
data = file->readBlock(size - 24);
else
data = ByteVector();
data = ByteVector::null;
}
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
ByteVector ASF::File::BaseObject::render(ASF::File * /*file*/)
{
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
}
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
ASF::File::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
{
}
ByteVector ASF::File::FilePrivate::UnknownObject::guid() const
ByteVector ASF::File::UnknownObject::guid()
{
return myGuid;
}
ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
ByteVector ASF::File::FilePropertiesObject::guid()
{
return filePropertiesGuid;
}
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long long size)
void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
{
BaseObject::parse(file, size);
if(data.size() < 64) {
debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short.");
return;
}
const long long duration = data.toLongLong(40, false);
const long long preroll = data.toLongLong(56, false);
file->d->properties->setLengthInMilliseconds(
static_cast<int>(static_cast<double>(duration) / 10000.0 - static_cast<double>(preroll) + 0.5));
file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L));
}
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
ByteVector ASF::File::StreamPropertiesObject::guid()
{
return streamPropertiesGuid;
}
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, long long size)
void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
{
BaseObject::parse(file, size);
if(data.size() < 70) {
debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short.");
return;
}
file->d->properties->setCodec(data.toUShort(54, false));
file->d->properties->setChannels(data.toUShort(56, false));
file->d->properties->setSampleRate(data.toUInt(58, false));
file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
file->d->properties->setBitsPerSample(data.toUShort(68, false));
file->d->properties->setChannels(data.mid(56, 2).toShort(false));
file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false));
file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000);
}
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
ByteVector ASF::File::ContentDescriptionObject::guid()
{
return contentDescriptionGuid;
}
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
void ASF::File::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
{
const int titleLength = readWORD(file);
const int artistLength = readWORD(file);
const int copyrightLength = readWORD(file);
const int commentLength = readWORD(file);
const int ratingLength = readWORD(file);
file->d->tag->setTitle(readString(file,titleLength));
file->d->tag->setArtist(readString(file,artistLength));
file->d->tag->setCopyright(readString(file,copyrightLength));
file->d->tag->setComment(readString(file,commentLength));
file->d->tag->setRating(readString(file,ratingLength));
file->d->contentDescriptionObject = this;
int titleLength = file->readWORD();
int artistLength = file->readWORD();
int copyrightLength = file->readWORD();
int commentLength = file->readWORD();
int ratingLength = file->readWORD();
file->d->tag->setTitle(file->readString(titleLength));
file->d->tag->setArtist(file->readString(artistLength));
file->d->tag->setCopyright(file->readString(copyrightLength));
file->d->tag->setComment(file->readString(commentLength));
file->d->tag->setRating(file->readString(ratingLength));
}
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file)
ByteVector ASF::File::ContentDescriptionObject::render(ASF::File *file)
{
const ByteVector v1 = renderString(file->d->tag->title());
const ByteVector v2 = renderString(file->d->tag->artist());
const ByteVector v3 = renderString(file->d->tag->copyright());
const ByteVector v4 = renderString(file->d->tag->comment());
const ByteVector v5 = renderString(file->d->tag->rating());
ByteVector v1 = file->renderString(file->d->tag->title());
ByteVector v2 = file->renderString(file->d->tag->artist());
ByteVector v3 = file->renderString(file->d->tag->copyright());
ByteVector v4 = file->renderString(file->d->tag->comment());
ByteVector v5 = file->renderString(file->d->tag->rating());
data.clear();
data.append(ByteVector::fromShort(static_cast<short>(v1.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v2.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v3.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v4.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v5.size()), false));
data.append(ByteVector::fromShort(v1.size(), false));
data.append(ByteVector::fromShort(v2.size(), false));
data.append(ByteVector::fromShort(v3.size(), false));
data.append(ByteVector::fromShort(v4.size(), false));
data.append(ByteVector::fromShort(v5.size(), false));
data.append(v1);
data.append(v2);
data.append(v3);
@@ -285,14 +235,15 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *f
return BaseObject::render(file);
}
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const
ByteVector ASF::File::ExtendedContentDescriptionObject::guid()
{
return extendedContentDescriptionGuid;
}
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
void ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
{
int count = readWORD(file);
file->d->extendedContentDescriptionObject = this;
int count = file->readWORD();
while(count--) {
ASF::Attribute attribute;
String name = attribute.parse(*file);
@@ -300,22 +251,23 @@ void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *
}
}
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
ByteVector ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(ByteVector::null));
return BaseObject::render(file);
}
ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
ByteVector ASF::File::MetadataObject::guid()
{
return metadataGuid;
}
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*size*/)
void ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
{
int count = readWORD(file);
file->d->metadataObject = this;
int count = file->readWORD();
while(count--) {
ASF::Attribute attribute;
String name = attribute.parse(*file, 1);
@@ -323,22 +275,23 @@ void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*
}
}
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
ByteVector ASF::File::MetadataObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(ByteVector::null));
return BaseObject::render(file);
}
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
ByteVector ASF::File::MetadataLibraryObject::guid()
{
return metadataLibraryGuid;
}
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long long /*size*/)
void ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
{
int count = readWORD(file);
file->d->metadataLibraryObject = this;
int count = file->readWORD();
while(count--) {
ASF::Attribute attribute;
String name = attribute.parse(*file, 2);
@@ -346,52 +299,46 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long
}
}
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
ByteVector ASF::File::MetadataLibraryObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(ByteVector::null));
return BaseObject::render(file);
}
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
{
objects.setAutoDelete(true);
}
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
ByteVector ASF::File::HeaderExtensionObject::guid()
{
return headerExtensionGuid;
}
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long long /*size*/)
void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
{
file->d->headerExtensionObject = this;
file->seek(18, File::Current);
long long dataSize = readDWORD(file);
long long dataSize = file->readDWORD();
long long dataPos = 0;
while(dataPos < dataSize) {
ByteVector uid = file->readBlock(16);
if(uid.size() != 16) {
ByteVector guid = file->readBlock(16);
if(guid.size() != 16) {
file->setValid(false);
break;
}
bool ok;
long long size = readQWORD(file, &ok);
if(!ok || size < 0 || size > dataSize - dataPos) {
long long size = file->readQWORD(&ok);
if(!ok) {
file->setValid(false);
break;
}
BaseObject *obj;
if(uid == metadataGuid) {
file->d->metadataObject = new MetadataObject();
obj = file->d->metadataObject;
if(guid == metadataGuid) {
obj = new MetadataObject();
}
else if(uid == metadataLibraryGuid) {
file->d->metadataLibraryObject = new MetadataLibraryObject();
obj = file->d->metadataLibraryObject;
else if(guid == metadataLibraryGuid) {
obj = new MetadataLibraryObject();
}
else {
obj = new UnknownObject(uid);
obj = new UnknownObject(guid);
}
obj->parse(file, size);
objects.append(obj);
@@ -399,128 +346,124 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long
}
}
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
ByteVector ASF::File::HeaderExtensionObject::render(ASF::File *file)
{
data.clear();
for(const auto &object : std::as_const(objects)) {
data.append(object->render(file));
for(unsigned int i = 0; i < objects.size(); i++) {
data.append(objects[i]->render(file));
}
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
return BaseObject::render(file);
}
ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
{
return codecListGuid;
}
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long size)
{
BaseObject::parse(file, size);
if(data.size() <= 20) {
debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short.");
return;
}
unsigned int pos = 16;
const int count = data.toUInt(pos, false);
pos += 4;
for(int i = 0; i < count; ++i) {
if(pos >= data.size())
break;
const auto type = static_cast<CodecType>(data.toUShort(pos, false));
pos += 2;
int nameLength = data.toUShort(pos, false);
pos += 2;
const unsigned int namePos = pos;
pos += nameLength * 2;
const int descLength = data.toUShort(pos, false);
pos += 2;
const unsigned int descPos = pos;
pos += descLength * 2;
const int infoLength = data.toUShort(pos, false);
pos += 2 + infoLength * 2;
if(type == CodecListObject::Audio) {
// First audio codec found.
const String name(data.mid(namePos, nameLength * 2), String::UTF16LE);
file->d->properties->setCodecName(name.stripWhiteSpace());
const String desc(data.mid(descPos, descLength * 2), String::UTF16LE);
file->d->properties->setCodecDescription(desc.stripWhiteSpace());
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool ASF::File::isSupported(IOStream *stream)
{
// An ASF file has to start with the designated GUID.
const ByteVector id = Utils::readHeader(stream, 16, false);
return id == headerGuid;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
TagLib::File(file),
d(std::make_unique<FilePrivate>())
ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle)
: TagLib::File(file)
{
if(isOpen())
read();
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>())
ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
: TagLib::File(stream)
{
if(isOpen())
read();
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
ASF::File::~File() = default;
ASF::File::~File()
{
for(unsigned int i = 0; i < d->objects.size(); i++) {
delete d->objects[i];
}
if(d->tag) {
delete d->tag;
}
if(d->properties) {
delete d->properties;
}
delete d;
}
ASF::Tag *ASF::File::tag() const
{
return d->tag.get();
}
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);
return d->tag;
}
ASF::Properties *ASF::File::audioProperties() const
{
return d->properties.get();
return d->properties;
}
void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*propertiesStyle*/)
{
if(!isValid())
return;
ByteVector guid = readBlock(16);
if(guid != headerGuid) {
debug("ASF: Not an ASF file.");
setValid(false);
return;
}
d->tag = new ASF::Tag();
d->properties = new ASF::Properties();
bool ok;
d->size = readQWORD(&ok);
if(!ok) {
setValid(false);
return;
}
int numObjects = readDWORD(&ok);
if(!ok) {
setValid(false);
return;
}
seek(2, Current);
for(int i = 0; i < numObjects; i++) {
ByteVector guid = readBlock(16);
if(guid.size() != 16) {
setValid(false);
break;
}
long size = (long)readQWORD(&ok);
if(!ok) {
setValid(false);
break;
}
BaseObject *obj;
if(guid == filePropertiesGuid) {
obj = new FilePropertiesObject();
}
else if(guid == streamPropertiesGuid) {
obj = new StreamPropertiesObject();
}
else if(guid == contentDescriptionGuid) {
obj = new ContentDescriptionObject();
}
else if(guid == extendedContentDescriptionGuid) {
obj = new ExtendedContentDescriptionObject();
}
else if(guid == headerExtensionGuid) {
obj = new HeaderExtensionObject();
}
else {
if(guid == contentEncryptionGuid ||
guid == extendedContentEncryptionGuid ||
guid == advancedContentEncryptionGuid) {
d->properties->setEncrypted(true);
}
obj = new UnknownObject(guid);
}
obj->parse(this, size);
d->objects.append(obj);
}
}
bool ASF::File::save()
@@ -536,43 +479,40 @@ bool ASF::File::save()
}
if(!d->contentDescriptionObject) {
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
d->contentDescriptionObject = new ContentDescriptionObject();
d->objects.append(d->contentDescriptionObject);
}
if(!d->extendedContentDescriptionObject) {
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
d->extendedContentDescriptionObject = new ExtendedContentDescriptionObject();
d->objects.append(d->extendedContentDescriptionObject);
}
if(!d->headerExtensionObject) {
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
d->headerExtensionObject = new HeaderExtensionObject();
d->objects.append(d->headerExtensionObject);
}
if(!d->metadataObject) {
d->metadataObject = new FilePrivate::MetadataObject();
d->metadataObject = new MetadataObject();
d->headerExtensionObject->objects.append(d->metadataObject);
}
if(!d->metadataLibraryObject) {
d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
d->metadataLibraryObject = new MetadataLibraryObject();
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
}
d->extendedContentDescriptionObject->attributeData.clear();
d->metadataObject->attributeData.clear();
d->metadataLibraryObject->attributeData.clear();
for(const auto &[name, attributes] : std::as_const(d->tag->attributeListMap())) {
ASF::AttributeListMap::ConstIterator it = d->tag->attributeListMap().begin();
for(; it != d->tag->attributeListMap().end(); it++) {
const String &name = it->first;
const AttributeList &attributes = it->second;
bool inExtendedContentDescriptionObject = false;
bool inMetadataObject = false;
for(const auto &attribute : attributes) {
const bool largeValue = attribute.dataSize() > 65535;
const bool guid = attribute.type() == Attribute::GuidType;
if(!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
for(unsigned int j = 0; j < attributes.size(); j++) {
const Attribute &attribute = attributes[j];
bool largeValue = attribute.dataSize() > 65535;
if(!inExtendedContentDescriptionObject && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
inExtendedContentDescriptionObject = true;
}
else if(!inMetadataObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
else if(!inMetadataObject && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
d->metadataObject->attributeData.append(attribute.render(name, 1));
inMetadataObject = true;
}
@@ -583,104 +523,85 @@ bool ASF::File::save()
}
ByteVector data;
for(const auto &object : std::as_const(d->objects)) {
data.append(object->render(this));
for(unsigned int i = 0; i < d->objects.size(); i++) {
data.append(d->objects[i]->render(this));
}
seek(16);
writeBlock(ByteVector::fromLongLong(data.size() + 30, false));
writeBlock(ByteVector::fromUInt(d->objects.size(), false));
writeBlock(ByteVector("\x01\x02", 2));
insert(data, 30, static_cast<unsigned long>(d->headerSize - 30));
d->headerSize = data.size() + 30;
data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
insert(data, 0, d->size);
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
// protected members
////////////////////////////////////////////////////////////////////////////////
void ASF::File::read()
int ASF::File::readBYTE(bool *ok)
{
if(!isValid())
return;
if(readBlock(16) != headerGuid) {
debug("ASF::File::read(): Not an ASF file.");
setValid(false);
return;
}
d->tag = std::make_unique<ASF::Tag>();
d->properties = std::make_unique<ASF::Properties>();
bool ok;
d->headerSize = readQWORD(this, &ok);
if(!ok) {
setValid(false);
return;
}
int numObjects = readDWORD(this, &ok);
if(!ok) {
setValid(false);
return;
}
seek(2, Current);
FilePrivate::FilePropertiesObject *filePropertiesObject = nullptr;
FilePrivate::StreamPropertiesObject *streamPropertiesObject = nullptr;
for(int i = 0; i < numObjects; i++) {
const ByteVector guid = readBlock(16);
if(guid.size() != 16) {
setValid(false);
break;
}
auto size = readQWORD(this, &ok);
if(!ok) {
setValid(false);
break;
}
FilePrivate::BaseObject *obj;
if(guid == filePropertiesGuid) {
filePropertiesObject = new FilePrivate::FilePropertiesObject();
obj = filePropertiesObject;
}
else if(guid == streamPropertiesGuid) {
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
obj = streamPropertiesObject;
}
else if(guid == contentDescriptionGuid) {
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
obj = d->contentDescriptionObject;
}
else if(guid == extendedContentDescriptionGuid) {
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
obj = d->extendedContentDescriptionObject;
}
else if(guid == headerExtensionGuid) {
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
obj = d->headerExtensionObject;
}
else if(guid == codecListGuid) {
obj = new FilePrivate::CodecListObject();
}
else {
if(guid == contentEncryptionGuid ||
guid == extendedContentEncryptionGuid ||
guid == advancedContentEncryptionGuid) {
d->properties->setEncrypted(true);
}
obj = new FilePrivate::UnknownObject(guid);
}
obj->parse(this, size);
d->objects.append(obj);
}
if(!filePropertiesObject || !streamPropertiesObject) {
debug("ASF::File::read(): Missing mandatory header objects.");
setValid(false);
ByteVector v = readBlock(1);
if(v.size() != 1) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v[0];
}
int ASF::File::readWORD(bool *ok)
{
ByteVector v = readBlock(2);
if(v.size() != 2) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toUShort(false);
}
unsigned int ASF::File::readDWORD(bool *ok)
{
ByteVector v = readBlock(4);
if(v.size() != 4) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toUInt(false);
}
long long ASF::File::readQWORD(bool *ok)
{
ByteVector v = readBlock(8);
if(v.size() != 8) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toLongLong(false);
}
String ASF::File::readString(int length)
{
ByteVector data = readBlock(length);
unsigned int size = data.size();
while (size >= 2) {
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
break;
}
size -= 2;
}
if(size != data.size()) {
data.resize(size);
}
return String(data, String::UTF16LE);
}
ByteVector ASF::File::renderString(const String &str, bool includeLength)
{
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
if(includeLength) {
data = ByteVector::fromShort(data.size(), false) + data;
}
return data;
}

View File

@@ -26,16 +26,16 @@
#ifndef TAGLIB_ASFFILE_H
#define TAGLIB_ASFFILE_H
#include "tag.h"
#include "tfile.h"
#include "taglib_export.h"
#include "tag.h"
#include "asfproperties.h"
#include "asftag.h"
namespace TagLib {
//! An implementation of ASF (WMA) metadata
namespace ASF {
//! An implementation of TagLib::File with ASF specific methods
/*!
* This implements and provides an interface for ASF files to the
@@ -48,35 +48,29 @@ namespace TagLib {
public:
/*!
* Constructs an ASF file from \a file.
* Contructs an ASF 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. The audio properties are always
* read.
* \a propertiesStyle are ignored.
*/
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.
* Contructs an ASF 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. 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.
* \a propertiesStyle are ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
~File() override;
File(const File &) = delete;
File &operator=(const File &) = delete;
virtual ~File();
/*!
* Returns a pointer to the ASF tag of the file.
@@ -88,53 +82,49 @@ namespace TagLib {
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
Tag *tag() const override;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const override;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties) override;
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &) override;
virtual Tag *tag() const;
/*!
* Returns the ASF audio properties for this file.
*/
Properties *audioProperties() const override;
virtual Properties *audioProperties() const;
/*!
* Save the file.
*
* This returns \c true if the save was successful.
* This returns true if the save was successful.
*/
bool save() override;
/*!
* Returns whether or not the given \a stream can be opened as an ASF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
virtual bool save();
private:
void read();
int readBYTE(bool *ok = 0);
int readWORD(bool *ok = 0);
unsigned int readDWORD(bool *ok = 0);
long long readQWORD(bool *ok = 0);
static ByteVector renderString(const String &str, bool includeLength = false);
String readString(int len);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
friend class Attribute;
friend class Picture;
class BaseObject;
class UnknownObject;
class FilePropertiesObject;
class StreamPropertiesObject;
class ContentDescriptionObject;
class ExtendedContentDescriptionObject;
class HeaderExtensionObject;
class MetadataObject;
class MetadataLibraryObject;
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
FilePrivate *d;
};
} // namespace ASF
} // namespace TagLib
}
}
#endif

View File

@@ -23,14 +23,19 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "asfpicture.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include "asfattribute.h"
#include "asffile.h"
#include "asfutils.h"
#include "asfpicture.h"
using namespace TagLib;
class ASF::Picture::PicturePrivate
class ASF::Picture::PicturePriavte : public RefCounter
{
public:
bool valid;
@@ -44,14 +49,23 @@ public:
// Picture class members
////////////////////////////////////////////////////////////////////////////////
ASF::Picture::Picture() :
d(std::make_shared<PicturePrivate>())
ASF::Picture::Picture()
{
d = new PicturePriavte();
d->valid = true;
}
ASF::Picture::Picture(const Picture &) = default;
ASF::Picture::~Picture() = default;
ASF::Picture::Picture(const Picture& other)
: d(other.d)
{
d->ref();
}
ASF::Picture::~Picture()
{
if(d->deref())
delete d;
}
bool ASF::Picture::isValid() const
{
@@ -105,25 +119,26 @@ int ASF::Picture::dataSize() const
d->picture.size();
}
ASF::Picture &ASF::Picture::operator=(const ASF::Picture &) = default;
void ASF::Picture::swap(Picture &other) noexcept
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
{
using std::swap;
swap(d, other.d);
if(other.d != d) {
if(d->deref())
delete d;
d = other.d;
d->ref();
}
return *this;
}
ByteVector ASF::Picture::render() const
{
if(!isValid())
return ByteVector();
return ByteVector::null;
return
ByteVector(static_cast<char>(d->type)) +
ByteVector((char)d->type) +
ByteVector::fromUInt(d->picture.size(), false) +
renderString(d->mimeType) +
renderString(d->description) +
ASF::File::renderString(d->mimeType) +
ASF::File::renderString(d->description) +
d->picture;
}
@@ -133,8 +148,8 @@ void ASF::Picture::parse(const ByteVector& bytes)
if(bytes.size() < 9)
return;
int pos = 0;
d->type = static_cast<Type>(bytes[0]); ++pos;
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
d->type = (Type)bytes[0]; ++pos;
uint dataLen = bytes.mid(pos, 4).toUInt(false); pos+=4;
const ByteVector nullStringTerminator(2, 0);
@@ -155,6 +170,7 @@ void ASF::Picture::parse(const ByteVector& bytes)
d->picture = bytes.mid(pos, dataLen);
d->valid = true;
return;
}
ASF::Picture ASF::Picture::fromInvalid()
@@ -163,3 +179,4 @@ ASF::Picture ASF::Picture::fromInvalid()
ret.d->valid = false;
return ret;
}

View File

@@ -28,8 +28,8 @@
#include "tstring.h"
#include "tbytevector.h"
#include "tpicturetype.h"
#include "taglib_export.h"
#include "attachedpictureframe.h"
namespace TagLib
{
@@ -39,9 +39,9 @@ namespace TagLib
//! An ASF attached picture interface implementation
/*!
* This is an implementation of ASF attached pictures. Pictures may be
* This is an implementation of ASF attached pictures interface. Pictures may be
* included in attributes, one per WM/Picture attribute (but there may be multiple WM/Picture
* attributes in a single tag). These pictures are usually in either JPEG or
* attribute in a single tag). These pictures are usually in either JPEG or
* PNG format.
* \see Attribute::toPicture()
* \see Attribute::Attribute(const Picture& picture)
@@ -49,10 +49,53 @@ namespace TagLib
class TAGLIB_EXPORT Picture {
public:
/*
/*!
* This describes the function or content of the picture.
*/
DECLARE_PICTURE_TYPE_ENUM(Type)
enum Type {
//! A type not enumerated below
Other = 0x00,
//! 32x32 PNG image that should be used as the file icon
FileIcon = 0x01,
//! File icon of a different size or format
OtherFileIcon = 0x02,
//! Front cover image of the album
FrontCover = 0x03,
//! Back cover image of the album
BackCover = 0x04,
//! Inside leaflet page of the album
LeafletPage = 0x05,
//! Image from the album itself
Media = 0x06,
//! Picture of the lead artist or soloist
LeadArtist = 0x07,
//! Picture of the artist or performer
Artist = 0x08,
//! Picture of the conductor
Conductor = 0x09,
//! Picture of the band or orchestra
Band = 0x0A,
//! Picture of the composer
Composer = 0x0B,
//! Picture of the lyricist or text writer
Lyricist = 0x0C,
//! Picture of the recording location or studio
RecordingLocation = 0x0D,
//! Picture of the artists during recording
DuringRecording = 0x0E,
//! Picture of the artists during performance
DuringPerformance = 0x0F,
//! Picture from a movie or video related to the track
MovieScreenCapture = 0x10,
//! Picture of a large, coloured fish
ColouredFish = 0x11,
//! Illustration related to the track
Illustration = 0x12,
//! Logo of the band or performer
BandLogo = 0x13,
//! Logo of the publisher (record company)
PublisherLogo = 0x14
};
/*!
* Constructs an empty picture.
@@ -60,14 +103,14 @@ namespace TagLib
Picture();
/*!
* Construct a picture as a copy of \a other.
* Construct an picture as a copy of \a other.
*/
Picture(const Picture& other);
/*!
* Destroys the picture.
*/
~Picture();
virtual ~Picture();
/*!
* Copies the contents of \a other into this picture.
@@ -75,12 +118,7 @@ namespace TagLib
Picture& operator=(const Picture& other);
/*!
* Exchanges the content of the Picture with the content of \a other.
*/
void swap(Picture &other) noexcept;
/*!
* Returns \c true if Picture stores valid picture
* Returns true if Picture stores valid picture
*/
bool isValid() const;
@@ -135,7 +173,7 @@ namespace TagLib
/*!
* Returns the image data as a ByteVector.
*
* \note ByteVector has a data() method that returns a <tt>const char *</tt> which
* \note ByteVector has a data() method that returns a const char * which
* should make it easy to export this data to external programs.
*
* \see setPicture()
@@ -167,14 +205,13 @@ namespace TagLib
/* THIS IS PRIVATE, DON'T TOUCH IT! */
void parse(const ByteVector& );
static Picture fromInvalid();
friend class Attribute;
#endif
private:
class PicturePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::shared_ptr<PicturePrivate> d;
struct PicturePriavte;
PicturePriavte *d;
};
} // namespace ASF
} // namespace TagLib
}
}
#endif // ASFPICTURE_H

View File

@@ -23,6 +23,12 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tdebug.h>
#include <tstring.h>
#include "asfproperties.h"
using namespace TagLib;
@@ -30,30 +36,30 @@ using namespace TagLib;
class ASF::Properties::PropertiesPrivate
{
public:
int length { 0 };
int bitrate { 0 };
int sampleRate { 0 };
int channels { 0 };
int bitsPerSample { 0 };
ASF::Properties::Codec codec { ASF::Properties::Unknown };
String codecName;
String codecDescription;
bool encrypted { false };
PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0), encrypted(false) {}
int length;
int bitrate;
int sampleRate;
int channels;
bool encrypted;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::Properties::Properties() :
AudioProperties(AudioProperties::Average),
d(std::make_unique<PropertiesPrivate>())
ASF::Properties::Properties() : AudioProperties(AudioProperties::Average)
{
d = new PropertiesPrivate;
}
ASF::Properties::~Properties() = default;
ASF::Properties::~Properties()
{
if(d)
delete d;
}
int ASF::Properties::lengthInMilliseconds() const
int ASF::Properties::length() const
{
return d->length;
}
@@ -71,27 +77,7 @@ int ASF::Properties::sampleRate() const
int ASF::Properties::channels() const
{
return d->channels;
}
int ASF::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
ASF::Properties::Codec ASF::Properties::codec() const
{
return d->codec;
}
String ASF::Properties::codecName() const
{
return d->codecName;
}
String ASF::Properties::codecDescription() const
{
return d->codecDescription;
}
}
bool ASF::Properties::isEncrypted() const
{
@@ -102,64 +88,28 @@ bool ASF::Properties::isEncrypted() const
// private members
////////////////////////////////////////////////////////////////////////////////
void ASF::Properties::setLengthInMilliseconds(int value)
void ASF::Properties::setLength(int length)
{
d->length = value;
d->length = length;
}
void ASF::Properties::setBitrate(int value)
void ASF::Properties::setBitrate(int length)
{
d->bitrate = value;
d->bitrate = length;
}
void ASF::Properties::setSampleRate(int value)
void ASF::Properties::setSampleRate(int length)
{
d->sampleRate = value;
d->sampleRate = length;
}
void ASF::Properties::setChannels(int value)
void ASF::Properties::setChannels(int length)
{
d->channels = value;
d->channels = length;
}
void ASF::Properties::setBitsPerSample(int value)
void ASF::Properties::setEncrypted(bool encrypted)
{
d->bitsPerSample = value;
d->encrypted = encrypted;
}
void ASF::Properties::setCodec(int value)
{
switch(value)
{
case 0x0160:
d->codec = WMA1;
break;
case 0x0161:
d->codec = WMA2;
break;
case 0x0162:
d->codec = WMA9Pro;
break;
case 0x0163:
d->codec = WMA9Lossless;
break;
default:
d->codec = Unknown;
break;
}
}
void ASF::Properties::setCodecName(const String &value)
{
d->codecName = value;
}
void ASF::Properties::setCodecDescription(const String &value)
{
d->codecDescription = value;
}
void ASF::Properties::setEncrypted(bool value)
{
d->encrypted = value;
}

View File

@@ -26,137 +26,51 @@
#ifndef TAGLIB_ASFPROPERTIES_H
#define TAGLIB_ASFPROPERTIES_H
#include "audioproperties.h"
#include "tstring.h"
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
namespace ASF {
//! An implementation of ASF audio properties
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Audio codec types which can be used in ASF files.
*/
enum Codec
{
/*!
* Couldn't detect the codec.
*/
Unknown = 0,
/*!
* Windows Media Audio 1
*/
WMA1,
/*!
* Windows Media Audio 2 or above
*/
WMA2,
/*!
* Windows Media Audio 9 Professional
*/
WMA9Pro,
/*!
* Windows Media Audio 9 Lossless
*/
WMA9Lossless,
};
/*!
* Creates an instance of ASF::Properties.
* Create an instance of ASF::Properties.
*/
Properties();
/*!
* Destroys this ASF::Properties instance.
*/
~Properties() override;
virtual ~Properties();
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
int lengthInMilliseconds() const override;
/*!
* Returns the average bit rate of the file in kb/s.
*/
int bitrate() const override;
/*!
* Returns the sample rate in Hz.
*/
int sampleRate() const override;
/*!
* Returns the number of audio channels.
*/
int channels() const override;
/*!
* Returns the number of bits per audio sample.
*/
int bitsPerSample() const;
/*!
* Returns the codec used in the file.
*
* \see codecName()
* \see codecDescription()
*/
Codec codec() const;
/*!
* Returns the concrete codec name, for example "Windows Media Audio 9.1"
* used in the file if available, otherwise an empty string.
*
* \see codec()
* \see codecDescription()
*/
String codecName() const;
/*!
* Returns the codec description, typically contains the encoder settings,
* for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available,
* otherwise an empty string.
*
* \see codec()
* \see codecName()
*/
String codecDescription() const;
/*!
* Returns whether or not the file is encrypted.
*/
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
bool isEncrypted() const;
#ifndef DO_NOT_DOCUMENT
void setLengthInMilliseconds(int value);
void setLength(int value);
void setBitrate(int value);
void setSampleRate(int value);
void setChannels(int value);
void setBitsPerSample(int value);
void setCodec(int value);
void setCodecName(const String &value);
void setCodecDescription(const String &value);
void setEncrypted(bool value);
#endif
private:
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
PropertiesPrivate *d;
};
} // namespace ASF
} // namespace TagLib
#endif
}
}
#endif

View File

@@ -23,29 +23,14 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "asftag.h"
#include <array>
#include <utility>
#include "tpropertymap.h"
#include "asfattribute.h"
#include "asfpicture.h"
using namespace TagLib;
namespace
{
StringList attributeListToStringList(const ASF::AttributeList &attributes)
{
StringList strs;
for(const auto &attribute : attributes) {
strs.append(attribute.toString());
}
return strs;
}
} // namespace
class ASF::Tag::TagPrivate
{
public:
@@ -57,12 +42,17 @@ public:
AttributeListMap attributeListMap;
};
ASF::Tag::Tag() :
d(std::make_unique<TagPrivate>())
ASF::Tag::Tag()
: TagLib::Tag()
{
d = new TagPrivate;
}
ASF::Tag::~Tag() = default;
ASF::Tag::~Tag()
{
if(d)
delete d;
}
String ASF::Tag::title() const
{
@@ -77,9 +67,8 @@ String ASF::Tag::artist() const
String ASF::Tag::album() const
{
if(d->attributeListMap.contains("WM/AlbumTitle"))
return joinTagValues(
attributeListToStringList(d->attributeListMap.value("WM/AlbumTitle")));
return String();
return d->attributeListMap["WM/AlbumTitle"][0].toString();
return String::null;
}
String ASF::Tag::copyright() const
@@ -110,7 +99,8 @@ unsigned int ASF::Tag::track() const
const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0];
if(attr.type() == ASF::Attribute::DWordType)
return attr.toUInt();
return attr.toString().toInt();
else
return attr.toString().toInt();
}
if(d->attributeListMap.contains("WM/Track"))
return d->attributeListMap["WM/Track"][0].toUInt();
@@ -120,9 +110,8 @@ unsigned int ASF::Tag::track() const
String ASF::Tag::genre() const
{
if(d->attributeListMap.contains("WM/Genre"))
return joinTagValues(
attributeListToStringList(d->attributeListMap.value("WM/Genre")));
return String();
return d->attributeListMap["WM/Genre"][0].toString();
return String::null;
}
void ASF::Tag::setTitle(const String &value)
@@ -160,12 +149,12 @@ void ASF::Tag::setGenre(const String &value)
setAttribute("WM/Genre", value);
}
void ASF::Tag::setYear(unsigned int value)
void ASF::Tag::setYear(uint value)
{
setAttribute("WM/Year", String::number(value));
}
void ASF::Tag::setTrack(unsigned int value)
void ASF::Tag::setTrack(uint value)
{
setAttribute("WM/TrackNumber", String::number(value));
}
@@ -175,36 +164,18 @@ ASF::AttributeListMap& ASF::Tag::attributeListMap()
return d->attributeListMap;
}
const ASF::AttributeListMap &ASF::Tag::attributeListMap() const
{
return d->attributeListMap;
}
bool ASF::Tag::contains(const String &key) const
{
return d->attributeListMap.contains(key);
}
void ASF::Tag::removeItem(const String &key)
{
d->attributeListMap.erase(key);
}
ASF::AttributeList ASF::Tag::attribute(const String &name) const
{
return d->attributeListMap[name];
AttributeListMap::Iterator it = d->attributeListMap.find(key);
if(it != d->attributeListMap.end())
d->attributeListMap.erase(it);
}
void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
{
AttributeList val;
val.append(attribute);
d->attributeListMap.insert(name, val);
}
void ASF::Tag::setAttribute(const String &name, const AttributeList &values)
{
d->attributeListMap.insert(name, values);
AttributeList value;
value.append(attribute);
d->attributeListMap.insert(name, value);
}
void ASF::Tag::addAttribute(const String &name, const Attribute &attribute)
@@ -225,224 +196,3 @@ bool ASF::Tag::isEmpty() const
d->attributeListMap.isEmpty();
}
namespace
{
constexpr std::array keyTranslation {
std::pair("WM/AlbumTitle", "ALBUM"),
std::pair("WM/AlbumArtist", "ALBUMARTIST"),
std::pair("WM/AuthorURL", "ARTISTWEBPAGE"),
std::pair("WM/Composer", "COMPOSER"),
std::pair("WM/Writer", "LYRICIST"),
std::pair("WM/Conductor", "CONDUCTOR"),
std::pair("WM/ModifiedBy", "REMIXER"),
std::pair("WM/Year", "DATE"),
std::pair("WM/OriginalAlbumTitle", "ORIGINALALBUM"),
std::pair("WM/OriginalArtist", "ORIGINALARTIST"),
std::pair("WM/OriginalFilename", "ORIGINALFILENAME"),
std::pair("WM/OriginalLyricist", "ORIGINALLYRICIST"),
std::pair("WM/OriginalReleaseYear", "ORIGINALDATE"),
std::pair("WM/Producer", "PRODUCER"),
std::pair("WM/ContentGroupDescription", "WORK"),
std::pair("WM/SubTitle", "SUBTITLE"),
std::pair("WM/SetSubTitle", "DISCSUBTITLE"),
std::pair("WM/TrackNumber", "TRACKNUMBER"),
std::pair("WM/PartOfSet", "DISCNUMBER"),
std::pair("WM/Genre", "GENRE"),
std::pair("WM/BeatsPerMinute", "BPM"),
std::pair("WM/Mood", "MOOD"),
std::pair("WM/InitialKey", "INITIALKEY"),
std::pair("WM/ISRC", "ISRC"),
std::pair("WM/Lyrics", "LYRICS"),
std::pair("WM/Media", "MEDIA"),
std::pair("WM/Publisher", "LABEL"),
std::pair("WM/CatalogNo", "CATALOGNUMBER"),
std::pair("WM/Barcode", "BARCODE"),
std::pair("WM/EncodedBy", "ENCODEDBY"),
std::pair("WM/EncodingSettings", "ENCODING"),
std::pair("WM/EncodingTime", "ENCODINGTIME"),
std::pair("WM/AudioFileURL", "FILEWEBPAGE"),
std::pair("WM/AlbumSortOrder", "ALBUMSORT"),
std::pair("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT"),
std::pair("WM/ArtistSortOrder", "ARTISTSORT"),
std::pair("WM/TitleSortOrder", "TITLESORT"),
std::pair("WM/Script", "SCRIPT"),
std::pair("WM/Language", "LANGUAGE"),
std::pair("WM/ARTISTS", "ARTISTS"),
std::pair("ASIN", "ASIN"),
std::pair("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID"),
std::pair("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID"),
std::pair("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID"),
std::pair("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"),
std::pair("MusicBrainz/Album Release Country", "RELEASECOUNTRY"),
std::pair("MusicBrainz/Album Status", "RELEASESTATUS"),
std::pair("MusicBrainz/Album Type", "RELEASETYPE"),
std::pair("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"),
std::pair("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID"),
std::pair("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID"),
std::pair("MusicIP/PUID", "MUSICIP_PUID"),
std::pair("Acoustid/Id", "ACOUSTID_ID"),
std::pair("Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT"),
};
String translateKey(const String &key)
{
for(const auto &[k, t] : keyTranslation) {
if(key == k)
return t;
}
return String();
}
} // namespace
PropertyMap ASF::Tag::properties() const
{
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;
}
for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) {
if(const String key = translateKey(k); !key.isEmpty()) {
for(const auto &attr : attributes) {
if(key == "TRACKNUMBER") {
if(attr.type() == ASF::Attribute::DWordType)
props.insert(key, String::number(attr.toUInt()));
else
props.insert(key, attr.toString());
}
else {
props.insert(key, attr.toString());
}
}
}
else {
props.addUnsupportedData(k);
}
}
return props;
}
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
{
for(const auto &prop : props)
d->attributeListMap.erase(prop);
}
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
{
static Map<String, String> reverseKeyMap;
if(reverseKeyMap.isEmpty()) {
for(const auto &[k, t] : keyTranslation) {
reverseKeyMap[t] = k;
}
}
const PropertyMap origProps = properties();
for(const auto &[prop, _] : origProps) {
if(!props.contains(prop) || props[prop].isEmpty()) {
if(prop == "TITLE") {
d->title.clear();
}
else if(prop == "ARTIST") {
d->artist.clear();
}
else if(prop == "COMMENT") {
d->comment.clear();
}
else if(prop == "COPYRIGHT") {
d->copyright.clear();
}
else {
d->attributeListMap.erase(reverseKeyMap[prop]);
}
}
}
PropertyMap ignoredProps;
for(const auto &[prop, attributes] : props) {
if(reverseKeyMap.contains(prop)) {
String name = reverseKeyMap[prop];
removeItem(name);
for(const auto &attr : attributes) {
addAttribute(name, attr);
}
}
else if(prop == "TITLE") {
d->title = attributes.toString();
}
else if(prop == "ARTIST") {
d->artist = attributes.toString();
}
else if(prop == "COMMENT") {
d->comment = attributes.toString();
}
else if(prop == "COPYRIGHT") {
d->copyright = attributes.toString();
}
else {
ignoredProps.insert(prop, attributes);
}
}
return ignoredProps;
}
StringList ASF::Tag::complexPropertyKeys() const
{
StringList keys;
if(d->attributeListMap.contains("WM/Picture")) {
keys.append("PICTURE");
}
return keys;
}
List<VariantMap> ASF::Tag::complexProperties(const String &key) const
{
List<VariantMap> props;
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
const AttributeList pictures = d->attributeListMap.value("WM/Picture");
for(const Attribute &attr : pictures) {
ASF::Picture picture = attr.toPicture();
VariantMap property;
property.insert("data", picture.picture());
property.insert("mimeType", picture.mimeType());
property.insert("description", picture.description());
property.insert("pictureType",
ASF::Picture::typeToString(picture.type()));
props.append(property);
}
}
return props;
}
bool ASF::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removeItem("WM/Picture");
for(const auto &property : value) {
ASF::Picture picture;
picture.setPicture(property.value("data").value<ByteVector>());
picture.setMimeType(property.value("mimeType").value<String>());
picture.setDescription(property.value("description").value<String>());
picture.setType(ASF::Picture::typeFromString(
property.value("pictureType").value<String>()));
addAttribute("WM/Picture", Attribute(picture));
}
}
else {
return false;
}
return true;
}

View File

@@ -26,20 +26,18 @@
#ifndef TAGLIB_ASFTAG_H
#define TAGLIB_ASFTAG_H
#include "tag.h"
#include "tlist.h"
#include "tmap.h"
#include "taglib_export.h"
#include "tag.h"
#include "asfattribute.h"
namespace TagLib {
namespace ASF {
using AttributeList = List<Attribute>;
using AttributeListMap = Map<String, AttributeList>;
//! An implementation of ASF (WMA) tags
typedef List<Attribute> AttributeList;
typedef Map<String, AttributeList> AttributeListMap;
class TAGLIB_EXPORT Tag : public TagLib::Tag {
@@ -49,37 +47,34 @@ namespace TagLib {
Tag();
~Tag() override;
Tag(const Tag &) = delete;
Tag &operator=(const Tag &) = delete;
virtual ~Tag();
/*!
* Returns the track name.
*/
String title() const override;
virtual String title() const;
/*!
* Returns the artist name.
*/
String artist() const override;
virtual String artist() const;
/*!
* Returns the album name; if no album name is present in the tag
* an empty string will be returned.
* String::null will be returned.
*/
String album() const override;
virtual String album() const;
/*!
* Returns the track comment.
*/
String comment() const override;
virtual String comment() const;
/*!
* Returns the genre name; if no genre is present in the tag an empty string
* Returns the genre name; if no genre is present in the tag String::null
* will be returned.
*/
String genre() const override;
virtual String genre() const;
/*!
* Returns the rating.
@@ -87,135 +82,105 @@ namespace TagLib {
virtual String rating() const;
/*!
* Returns the copyright information; if no copyright information is
* present in the tag an empty string will be returned.
* Returns the genre name; if no genre is present in the tag String::null
* will be returned.
*/
virtual String copyright() const;
/*!
* Returns the year; if there is no year set, this will return 0.
*/
unsigned int year() const override;
virtual uint year() const;
/*!
* Returns the track number; if there is no track number set, this will
* return 0.
*/
unsigned int track() const override;
virtual uint track() const;
/*!
* Sets the title to \a value.
* Sets the title to \a s.
*/
void setTitle(const String &value) override;
virtual void setTitle(const String &s);
/*!
* Sets the artist to \a value.
* Sets the artist to \a s.
*/
void setArtist(const String &value) override;
virtual void setArtist(const String &s);
/*!
* Sets the album to \a value. If \a value is an empty string then this value will be
* Sets the album to \a s. If \a s is String::null then this value will be
* cleared.
*/
void setAlbum(const String &value) override;
virtual void setAlbum(const String &s);
/*!
* Sets the comment to \a value.
* Sets the comment to \a s.
*/
void setComment(const String &value) override;
virtual void setComment(const String &s);
/*!
* Sets the rating to \a value.
* Sets the rating to \a s.
*/
virtual void setRating(const String &value);
virtual void setRating(const String &s);
/*!
* Sets the copyright to \a value.
* Sets the copyright to \a s.
*/
virtual void setCopyright(const String &value);
virtual void setCopyright(const String &s);
/*!
* Sets the genre to \a value.
* Sets the genre to \a s.
*/
void setGenre(const String &value) override;
virtual void setGenre(const String &s);
/*!
* Sets the year to \a value. If \a value is 0 then this value will be cleared.
* Sets the year to \a i. If \a s is 0 then this value will be cleared.
*/
void setYear(unsigned int value) override;
virtual void setYear(uint i);
/*!
* Sets the track to \a value. If \a value is 0 then this value will be cleared.
* Sets the track to \a i. If \a s is 0 then this value will be cleared.
*/
void setTrack(unsigned int value) override;
virtual void setTrack(uint i);
/*!
* Returns \c true if the tag does not contain any data. This should be
* Returns true if the tag does not contain any data. This should be
* reimplemented in subclasses that provide more than the basic tagging
* abilities in this class.
*/
bool isEmpty() const override;
/*!
* \warning You should not modify this data structure directly, instead
* use attributeListMap() const, contains(), removeItem(),
* attribute(), setAttribute(), addAttribute().
*/
AttributeListMap &attributeListMap();
virtual bool isEmpty() const;
/*!
* Returns a reference to the item list map. This is an AttributeListMap of
* all of the items in the tag.
*
* This is the most powerfull structure for accessing the items of the tag.
*/
const AttributeListMap &attributeListMap() const;
/*!
* \return \c true if a value for \a key is currently set.
*/
bool contains(const String &key) const;
AttributeListMap &attributeListMap();
/*!
* Removes the \a key attribute from the tag
*/
void removeItem(const String &key);
void removeItem(const String &name);
/*!
* \return The list of values for the key \a name, or an empty list if no
* values have been set.
*/
AttributeList attribute(const String &name) const;
/*!
* Sets the \a name attribute to the value of \a attribute. If an attribute
* with the \a name is already present, it will be replaced.
* Sets the \a key attribute to the value of \a attribute. If an attribute
* with the \a key is already present, it will be replaced.
*/
void setAttribute(const String &name, const Attribute &attribute);
/*!
* Sets multiple \a values to the key \a name.
*/
void setAttribute(const String &name, const AttributeList &values);
/*!
* Sets the \a name attribute to the value of \a attribute. If an attribute
* with the \a name is already present, it will be added to the list.
* Sets the \a key attribute to the value of \a attribute. If an attribute
* with the \a key is already present, it will be added to the list.
*/
void addAttribute(const String &name, const Attribute &attribute);
PropertyMap properties() const override;
void removeUnsupportedProperties(const StringList &props) override;
PropertyMap setProperties(const PropertyMap &props) override;
StringList complexPropertyKeys() const override;
List<VariantMap> complexProperties(const String &key) const override;
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
private:
class TagPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<TagPrivate> d;
TagPrivate *d;
};
} // namespace ASF
} // namespace TagLib
}
}
#endif

View File

@@ -1,104 +0,0 @@
/***************************************************************************
copyright : (C) 2015 by Tsuda Kageyu
email : tsuda.kageyu@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_ASFUTILS_H
#define TAGLIB_ASFUTILS_H
// THIS FILE IS NOT A PART OF THE TAGLIB API
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
namespace TagLib
{
namespace ASF
{
namespace
{
inline unsigned short readWORD(File *file, bool *ok = nullptr)
{
const ByteVector v = file->readBlock(2);
if(v.size() != 2) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toUShort(false);
}
inline unsigned int readDWORD(File *file, bool *ok = nullptr)
{
const ByteVector v = file->readBlock(4);
if(v.size() != 4) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toUInt(false);
}
inline long long readQWORD(File *file, bool *ok = nullptr)
{
const ByteVector v = file->readBlock(8);
if(v.size() != 8) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toLongLong(false);
}
inline String readString(File *file, int length)
{
ByteVector data = file->readBlock(length);
unsigned int size = data.size();
while (size >= 2) {
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
break;
}
size -= 2;
}
if(size != data.size()) {
data.resize(size);
}
return String(data, String::UTF16LE);
}
inline ByteVector renderString(const String &str, bool includeLength = false)
{
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
if(includeLength) {
data = ByteVector::fromShort(static_cast<short>(data.size()), false) + data;
}
return data;
}
} // namespace
} // namespace ASF
} // namespace TagLib
#endif
#endif

View File

@@ -29,37 +29,16 @@ using namespace TagLib;
class AudioProperties::AudioPropertiesPrivate
{
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
AudioProperties::~AudioProperties() = default;
int AudioProperties::length() const
AudioProperties::~AudioProperties()
{
return lengthInSeconds();
}
int AudioProperties::lengthInSeconds() const
{
return lengthInMilliseconds() / 1000;
}
int AudioProperties::lengthInMilliseconds() const
{
return 0;
}
int AudioProperties::bitrate() const
{
return 0;
}
int AudioProperties::sampleRate() const
{
return 0;
}
////////////////////////////////////////////////////////////////////////////////
@@ -68,4 +47,5 @@ int AudioProperties::sampleRate() const
AudioProperties::AudioProperties(ReadStyle)
{
}

View File

@@ -26,9 +26,6 @@
#ifndef TAGLIB_AUDIOPROPERTIES_H
#define TAGLIB_AUDIOPROPERTIES_H
#include <memory>
#include "taglib.h"
#include "taglib_export.h"
namespace TagLib {
@@ -37,7 +34,7 @@ namespace TagLib {
/*!
* The values here are common to most audio formats. For more specific, codec
* dependent values, please see the subclasses APIs. This is meant to
* dependant values, please see see the subclasses APIs. This is meant to
* compliment the TagLib::File and TagLib::Tag APIs in providing a simple
* interface that is sufficient for most applications.
*/
@@ -67,46 +64,22 @@ namespace TagLib {
*/
virtual ~AudioProperties();
AudioProperties(const AudioProperties &) = delete;
AudioProperties &operator=(const AudioProperties &) = delete;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated Use lengthInSeconds().
*/
TAGLIB_DEPRECATED
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
* Returns the length of the file in seconds.
*/
virtual int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const;
virtual int length() const = 0;
/*!
* Returns the most appropriate bit rate for the file in kb/s. For constant
* bitrate formats this is simply the bitrate of the file. For variable
* bitrate formats this is either the average or nominal bitrate.
*/
virtual int bitrate() const;
virtual int bitrate() const = 0;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const;
virtual int sampleRate() const = 0;
/*!
* Returns the number of audio channels.
@@ -125,11 +98,13 @@ namespace TagLib {
AudioProperties(ReadStyle style);
private:
AudioProperties(const AudioProperties &);
AudioProperties &operator=(const AudioProperties &);
class AudioPropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<AudioPropertiesPrivate> d;
AudioPropertiesPrivate *d;
};
} // namespace TagLib
}
#endif

View File

@@ -1,156 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.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 "dsdiffdiintag.h"
#include <utility>
#include "tstringlist.h"
#include "tpropertymap.h"
#include "tdebug.h"
using namespace TagLib;
using namespace DSDIFF::DIIN;
class DSDIFF::DIIN::Tag::TagPrivate
{
public:
String title;
String artist;
};
DSDIFF::DIIN::Tag::Tag() :
d(std::make_unique<TagPrivate>())
{
}
DSDIFF::DIIN::Tag::~Tag() = default;
String DSDIFF::DIIN::Tag::title() const
{
return d->title;
}
String DSDIFF::DIIN::Tag::artist() const
{
return d->artist;
}
String DSDIFF::DIIN::Tag::album() const
{
return String();
}
String DSDIFF::DIIN::Tag::comment() const
{
return String();
}
String DSDIFF::DIIN::Tag::genre() const
{
return String();
}
unsigned int DSDIFF::DIIN::Tag::year() const
{
return 0;
}
unsigned int DSDIFF::DIIN::Tag::track() const
{
return 0;
}
void DSDIFF::DIIN::Tag::setTitle(const String &title)
{
d->title = title;
}
void DSDIFF::DIIN::Tag::setArtist(const String &artist)
{
d->artist = artist;
}
void DSDIFF::DIIN::Tag::setAlbum(const String &)
{
debug("DSDIFF::DIIN::Tag::setAlbum() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setComment(const String &)
{
debug("DSDIFF::DIIN::Tag::setComment() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setGenre(const String &)
{
debug("DSDIFF::DIIN::Tag::setGenre() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setYear(unsigned int)
{
debug("DSDIFF::DIIN::Tag::setYear() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
{
debug("DSDIFF::DIIN::Tag::setTrack() -- Ignoring unsupported tag.");
}
PropertyMap DSDIFF::DIIN::Tag::properties() const
{
PropertyMap properties;
properties["TITLE"] = d->title;
properties["ARTIST"] = d->artist;
return properties;
}
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap props(origProps);
props.removeEmpty();
StringList oneValueSet;
if(props.contains("TITLE")) {
d->title = props["TITLE"].front();
oneValueSet.append("TITLE");
} else
d->title.clear();
if(props.contains("ARTIST")) {
d->artist = props["ARTIST"].front();
oneValueSet.append("ARTIST");
} else
d->artist.clear();
// for each tag that has been set above, remove the first entry in the corresponding
// value list. The others will be returned as unsupported by this format.
for(const auto &entry : std::as_const(oneValueSet)) {
if(props[entry].size() == 1)
props.erase(entry);
else
props[entry].erase(props[entry].begin());
}
return props;
}

View File

@@ -1,147 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.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_DSDIFFDIINTAG_H
#define TAGLIB_DSDIFFDIINTAG_H
#include "tag.h"
namespace TagLib {
namespace DSDIFF {
namespace DIIN {
/*!
* Tags from the Edited Master Chunk Info
*
* Only Title and Artist tags are supported
*/
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
Tag();
~Tag() override;
/*!
* Returns the track name; if no track name is present in the tag
* String() will be returned.
*/
String title() const override;
/*!
* Returns the artist name; if no artist name is present in the tag
* String() will be returned.
*/
String artist() const override;
/*!
* Not supported. Therefore always returns String().
*/
String album() const override;
/*!
* Not supported. Therefore always returns String().
*/
String comment() const override;
/*!
* Not supported. Therefore always returns String().
*/
String genre() const override;
/*!
* Not supported. Therefore always returns 0.
*/
unsigned int year() const override;
/*!
* Not supported. Therefore always returns 0.
*/
unsigned int track() const override;
/*!
* Sets the title to \a title. If \a title is String() then this
* value will be cleared.
*/
void setTitle(const String &title) override;
/*!
* Sets the artist to \a artist. If \a artist is String() then this
* value will be cleared.
*/
void setArtist(const String &artist) override;
/*!
* Not supported and therefore ignored.
*/
void setAlbum(const String &album) override;
/*!
* Not supported and therefore ignored.
*/
void setComment(const String &comment) override;
/*!
* Not supported and therefore ignored.
*/
void setGenre(const String &genre) override;
/*!
* Not supported and therefore ignored.
*/
void setYear(unsigned int year) override;
/*!
* Not supported and therefore ignored.
*/
void setTrack(unsigned int track) override;
/*!
* Implements the unified property interface -- export function.
* Since the DIIN tag is very limited, the exported map is as well.
*/
PropertyMap properties() const override;
/*!
* Implements the unified property interface -- import function.
* Because of the limitations of the DIIN file tag, any tags besides
* TITLE and ARTIST, will be
* returned. Additionally, if the map contains tags with multiple values,
* all but the first will be contained in the returned map of unsupported
* properties.
*/
PropertyMap setProperties(const PropertyMap &) override;
private:
class TagPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<TagPrivate> d;
};
} // namespace DIIN
} // namespace DSDIFF
} // namespace TagLib
#endif

View File

@@ -1,926 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.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 "dsdifffile.h"
#include <array>
#include <memory>
#include "tbytevector.h"
#include "tstringlist.h"
#include "tpropertymap.h"
#include "tdebug.h"
#include "id3v2tag.h"
#include "tagutils.h"
#include "tagunion.h"
using namespace TagLib;
namespace
{
struct Chunk64
{
ByteVector name;
unsigned long long offset;
unsigned long long size;
char padding;
};
using ChunkList = std::vector<Chunk64>;
int chunkIndex(const ChunkList &chunks, const ByteVector &id)
{
for(size_t i = 0; i < chunks.size(); i++) {
if(chunks[i].name == id)
return static_cast<int>(i);
}
return -1;
}
bool isValidChunkID(const ByteVector &name)
{
if(name.size() != 4)
return false;
for(int i = 0; i < 4; i++) {
if(name[i] < 32 || name[i] > 126)
return false;
}
return true;
}
enum {
ID3v2Index = 0,
DIINIndex = 1
};
enum {
PROPChunk = 0,
DIINChunk = 1
};
} // namespace
class DSDIFF::File::FilePrivate
{
public:
FilePrivate(const ID3v2::FrameFactory *frameFactory)
: ID3v2FrameFactory(frameFactory ? frameFactory
: ID3v2::FrameFactory::instance())
{
}
~FilePrivate() = default;
const ID3v2::FrameFactory *ID3v2FrameFactory;
Endianness endianness { BigEndian };
ByteVector type;
unsigned long long size { 0 };
ByteVector format;
ChunkList chunks;
std::array<ChunkList, 2> childChunks;
std::array<int, 2> childChunkIndex { -1, -1 };
/*
* Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
*/
bool isID3InPropChunk { false };
/*
* ID3 chunks are present. This is then the index of the one in PROP chunk that
* will be removed upon next save to remove duplicates.
*/
int duplicateID3V2chunkIndex { -1 };
std::unique_ptr<Properties> properties;
TagUnion tag;
ByteVector id3v2TagChunkID { "ID3 " };
bool hasID3v2 { false };
bool hasDiin { false };
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool DSDIFF::File::isSupported(IOStream *stream)
{
// A DSDIFF file has to start with "FRM8????????DSD ".
const ByteVector id = Utils::readHeader(stream, 16, false);
return id.startsWith("FRM8") && id.containsAt("DSD ", 12);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::~File() = default;
TagLib::Tag *DSDIFF::File::tag() const
{
return &d->tag;
}
ID3v2::Tag *DSDIFF::File::ID3v2Tag(bool create) const
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, create, d->ID3v2FrameFactory);
}
bool DSDIFF::File::hasID3v2Tag() const
{
return d->hasID3v2;
}
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag(bool create) const
{
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, create);
}
bool DSDIFF::File::hasDIINTag() const
{
return d->hasDiin;
}
PropertyMap DSDIFF::File::properties() const
{
return d->tag.properties();
}
void DSDIFF::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
{
return ID3v2Tag(true)->setProperties(properties);
}
DSDIFF::Properties *DSDIFF::File::audioProperties() const
{
return d->properties.get();
}
bool DSDIFF::File::save()
{
return save(AllTags);
}
bool DSDIFF::File::save(int tags, StripTags strip, ID3v2::Version version)
{
if(readOnly()) {
debug("DSDIFF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("DSDIFF::File::save() -- Trying to save invalid file.");
return false;
}
if(strip == StripOthers)
File::strip(~tags);
// First: save ID3V2 chunk
if(const ID3v2::Tag *id3v2Tag = ID3v2Tag(); (tags & ID3v2) && id3v2Tag) {
if(d->isID3InPropChunk) {
if(!id3v2Tag->isEmpty()) {
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(version), PROPChunk);
d->hasID3v2 = true;
}
else {
// Empty tag: remove it
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
d->hasID3v2 = false;
}
}
else {
if(!id3v2Tag->isEmpty()) {
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render(version));
d->hasID3v2 = true;
}
else {
// Empty tag: remove it
setRootChunkData(d->id3v2TagChunkID, ByteVector());
d->hasID3v2 = false;
}
}
}
// Second: save the DIIN chunk
if(const DSDIFF::DIIN::Tag *diinTag = DIINTag(); (tags & DIIN) && diinTag) {
if(!diinTag->title().isEmpty()) {
ByteVector diinTitle;
diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian));
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
setChildChunkData("DITI", diinTitle, DIINChunk);
}
else
setChildChunkData("DITI", ByteVector(), DIINChunk);
if(!diinTag->artist().isEmpty()) {
ByteVector diinArtist;
diinArtist.append(ByteVector::fromUInt(diinTag->artist().size(), d->endianness == BigEndian));
diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString()));
setChildChunkData("DIAR", diinArtist, DIINChunk);
}
else
setChildChunkData("DIAR", ByteVector(), DIINChunk);
}
// Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any
if(d->duplicateID3V2chunkIndex >= 0) {
setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk);
d->duplicateID3V2chunkIndex = -1;
}
return true;
}
void DSDIFF::File::strip(int tags)
{
if(tags & ID3v2) {
removeRootChunk("ID3 ");
removeRootChunk("id3 ");
removeChildChunk("ID3 ", PROPChunk);
removeChildChunk("id3 ", PROPChunk);
d->hasID3v2 = false;
d->tag.set(ID3v2Index, new ID3v2::Tag(nullptr, 0, d->ID3v2FrameFactory));
d->duplicateID3V2chunkIndex = -1;
d->isID3InPropChunk = false;
d->id3v2TagChunkID.setData("ID3 ");
}
if(tags & DIIN) {
removeChildChunk("DITI", DIINChunk);
removeChildChunk("DIAR", DIINChunk);
if(d->childChunks[DIINIndex].empty()) {
removeRootChunk("DIIN");
}
d->hasDiin = false;
d->tag.set(DIINIndex, new DIIN::Tag);
}
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSDIFF::File::removeRootChunk(unsigned int i)
{
unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12;
d->size -= chunkSize;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
removeBlock(d->chunks[i].offset - 12, chunkSize);
// Update the internal offsets
d->chunks.erase(d->chunks.begin() + i);
for(int &cci : d->childChunkIndex) {
if(cci > static_cast<int>(i)) {
--cci;
}
}
updateRootChunksStructure(i);
}
void DSDIFF::File::removeRootChunk(const ByteVector &id)
{
int i = chunkIndex(d->chunks, id);
if(i >= 0)
removeRootChunk(i);
}
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
{
if(data.isEmpty()) {
removeRootChunk(i);
return;
}
// Non null data: update chunk
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// Now update the specific chunk
writeChunk(d->chunks[i].name,
data,
d->chunks[i].offset - 12,
static_cast<unsigned long>(d->chunks[i].size + d->chunks[i].padding + 12));
d->chunks[i].size = data.size();
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
// Finally update the internal offsets
updateRootChunksStructure(i + 1);
}
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data)
{
if(d->chunks.empty()) {
debug("DSDIFF::File::setRootChunkData('" + name + "') - No valid chunks found.");
return;
}
int i = chunkIndex(d->chunks, name);
if(i >= 0) {
setRootChunkData(i, data);
return;
}
// Couldn't find an existing chunk, so let's create a new one.
i = static_cast<int>(d->chunks.size()) - 1;
unsigned long long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding;
// First we update the global size
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// Now add the chunk to the file
const unsigned long long fileLength = length();
writeChunk(name,
data,
offset,
static_cast<unsigned long>(fileLength > offset ? fileLength - offset : 0),
(offset & 1) ? 1 : 0);
Chunk64 chunk;
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 12;
chunk.padding = (data.size() & 0x01) ? 1 : 0;
d->chunks.push_back(chunk);
}
void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum)
{
ChunkList &childChunks = d->childChunks[childChunkNum];
// Update global size
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
d->size -= removedChunkTotalSize;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// Update child chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize;
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
d->endianness == BigEndian),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Remove the chunk
removeBlock(childChunks[i].offset - 12, removedChunkTotalSize);
// Update the internal offsets
// For child chunks
if(i + 1 < childChunks.size()) {
childChunks[i + 1].offset = childChunks[i].offset;
for(unsigned int c = i + 2; c < childChunks.size(); ++c)
childChunks[c].offset = childChunks[c - 1].offset + 12
+ childChunks[c - 1].size + childChunks[c - 1].padding;
}
// And for root chunks
childChunks.erase(childChunks.begin() + i);
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
}
void DSDIFF::File::removeChildChunk(const ByteVector &id, unsigned int childChunkNum)
{
int i = chunkIndex(d->childChunks[childChunkNum], id);
if(i >= 0)
removeChildChunk(i, childChunkNum);
}
void DSDIFF::File::setChildChunkData(unsigned int i,
const ByteVector &data,
unsigned int childChunkNum)
{
ChunkList &childChunks = d->childChunks[childChunkNum];
if(data.isEmpty()) {
removeChildChunk(i, childChunkNum);
return;
}
// Non null data: update chunk
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// And the PROP chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size +=
((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
d->endianness == BigEndian),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Now update the specific chunk
writeChunk(childChunks[i].name,
data,
childChunks[i].offset - 12,
static_cast<unsigned long>(childChunks[i].size + childChunks[i].padding + 12));
childChunks[i].size = data.size();
childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
// Now update the internal offsets
// For child Chunks
for(i++; i < childChunks.size(); i++)
childChunks[i].offset = childChunks[i - 1].offset + 12
+ childChunks[i - 1].size + childChunks[i - 1].padding;
// And for root chunks
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
}
void DSDIFF::File::setChildChunkData(const ByteVector &name,
const ByteVector &data,
unsigned int childChunkNum)
{
ChunkList &childChunks = d->childChunks[childChunkNum];
if(int i = chunkIndex(childChunks, name); i >= 0) {
setChildChunkData(i, data, childChunkNum);
return;
}
// Do not attempt to remove a non existing chunk
if(data.isEmpty())
return;
// Couldn't find an existing chunk, so let's create a new one.
unsigned long long offset = 0;
if(!childChunks.empty()) {
size_t i = childChunks.size() - 1;
offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding;
}
else if(childChunkNum == DIINChunk) {
int i = d->childChunkIndex[DIINChunk];
if(i < 0) {
setRootChunkData("DIIN", ByteVector());
if(const int lastChunkIndex = static_cast<int>(d->chunks.size()) - 1;
lastChunkIndex >= 0 && d->chunks[lastChunkIndex].name == "DIIN") {
i = lastChunkIndex;
d->childChunkIndex[DIINChunk] = lastChunkIndex;
d->hasDiin = true;
}
}
if(i >= 0) {
offset = d->chunks[i].offset; // 12 is already added in setRootChunkData()
}
}
if(offset == 0) {
debug("DSDIFF::File::setChildChunkData - No valid chunks found.");
return;
}
// First we update the global size
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// And the child chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1)
+ ((data.size() + 1) & ~1) + 12;
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
d->endianness == BigEndian),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Now add the chunk to the file
unsigned long long nextRootChunkIdx = length();
if(d->childChunkIndex[childChunkNum] + 1 < static_cast<int>(d->chunks.size()))
nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12;
writeChunk(name, data, offset,
static_cast<unsigned long>(
nextRootChunkIdx > offset ? nextRootChunkIdx - offset : 0),
offset & 1 ? 1 : 0);
// For root chunks
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
Chunk64 chunk;
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 12;
chunk.padding = data.size() & 0x01 ? 1 : 0;
childChunks.push_back(chunk);
}
void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
{
for(unsigned int i = startingChunk; i < d->chunks.size(); i++)
d->chunks[i].offset = d->chunks[i - 1].offset + 12
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
// Update child chunks structure as well
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
if(ChunkList &childChunksToUpdate = d->childChunks[PROPChunk];
!childChunksToUpdate.empty()) {
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12;
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
}
}
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
if(ChunkList &childChunksToUpdate = d->childChunks[DIINChunk];
!childChunksToUpdate.empty()) {
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12;
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
}
}
}
void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
bool bigEndian = d->endianness == BigEndian;
d->type = readBlock(4);
d->size = readBlock(8).toLongLong(bigEndian);
d->format = readBlock(4);
// + 12: chunk header at least, fix for additional junk bytes
while(tell() + 12 <= length()) {
ByteVector chunkName = readBlock(4);
unsigned long long chunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(chunkName)) {
debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<unsigned long long>(tell()) + chunkSize >
static_cast<unsigned long long>(length())) {
debug("DSDIFF::File::read() -- Chunk '" + chunkName
+ "' has invalid size (larger than the file size)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = chunkName;
chunk.size = chunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// Check padding
chunk.padding = 0;
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
if(ByteVector iByte = readBlock(1);
iByte.size() != 1 || iByte[0] != 0)
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->chunks.push_back(chunk);
}
// For DSD uncompressed
unsigned long long lengthDSDSamplesTimeChannels = 0;
// For computing bitrate
unsigned long long audioDataSizeinBytes = 0;
// For DST compressed frames
unsigned long dstNumFrames = 0;
// For DST compressed frames
unsigned short dstFrameRate = 0;
for(unsigned int i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == "DSD ") {
lengthDSDSamplesTimeChannels = d->chunks[i].size * 8;
audioDataSizeinBytes = d->chunks[i].size;
}
else if(d->chunks[i].name == "DST ") {
// Now decode the chunks inside the DST chunk to read the DST Frame Information one
long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size;
seek(d->chunks[i].offset);
audioDataSizeinBytes = d->chunks[i].size;
while(tell() + 12 <= dstChunkEnd) {
ByteVector dstChunkName = readBlock(4);
long long dstChunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(dstChunkName)) {
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + dstChunkSize > dstChunkEnd) {
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName
+ "' has invalid size (larger than the DST chunk)");
setValid(false);
break;
}
if(dstChunkName == "FRTE") {
// Found the DST frame information chunk
dstNumFrames = readBlock(4).toUInt(bigEndian);
dstFrameRate = readBlock(2).toUShort(bigEndian);
// Found the wanted one, no need to look at the others
break;
}
seek(dstChunkSize, Current);
// Check padding
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
if(ByteVector iByte = readBlock(1);
iByte.size() != 1 || iByte[0] != 0)
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
}
}
}
else if(d->chunks[i].name == "PROP") {
d->childChunkIndex[PROPChunk] = i;
// Now decodes the chunks inside the PROP chunk
long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size;
// +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
seek(d->chunks[i].offset + 4);
while(tell() + 12 <= propChunkEnd) {
ByteVector propChunkName = readBlock(4);
long long propChunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(propChunkName)) {
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + propChunkSize > propChunkEnd) {
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName
+ "' has invalid size (larger than the PROP chunk)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = propChunkName;
chunk.size = propChunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// Check padding
chunk.padding = 0;
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
if(ByteVector iByte = readBlock(1);
iByte.size() != 1 || iByte[0] != 0)
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->childChunks[PROPChunk].push_back(chunk);
}
}
else if(d->chunks[i].name == "DIIN") {
d->childChunkIndex[DIINChunk] = i;
d->hasDiin = true;
// Now decode the chunks inside the DIIN chunk
long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size;
seek(d->chunks[i].offset);
while(tell() + 12 <= diinChunkEnd) {
ByteVector diinChunkName = readBlock(4);
long long diinChunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(diinChunkName)) {
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + diinChunkSize > diinChunkEnd) {
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName
+ "' has invalid size (larger than the DIIN chunk)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = diinChunkName;
chunk.size = diinChunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// Check padding
chunk.padding = 0;
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
if(ByteVector iByte = readBlock(1);
iByte.size() != 1 || iByte[0] != 0)
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->childChunks[DIINChunk].push_back(chunk);
}
}
else if(d->chunks[i].name == "ID3 " || d->chunks[i].name == "id3 ") {
d->id3v2TagChunkID = d->chunks[i].name;
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->chunks[i].offset,
d->ID3v2FrameFactory));
d->isID3InPropChunk = false;
d->hasID3v2 = true;
}
}
if(!isValid())
return;
if(d->childChunkIndex[PROPChunk] < 0) {
debug("DSDIFF::File::read() -- no PROP chunk found");
setValid(false);
return;
}
// Read properties
unsigned int sampleRate = 0;
unsigned short channels = 0;
for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) {
if(d->childChunks[PROPChunk][i].name == "ID3 " ||
d->childChunks[PROPChunk][i].name == "id3 ") {
if(d->hasID3v2) {
d->duplicateID3V2chunkIndex = i;
// ID3V2 tag has already been found at root level
continue;
}
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset,
d->ID3v2FrameFactory));
d->isID3InPropChunk = true;
d->hasID3v2 = true;
}
else if(d->childChunks[PROPChunk][i].name == "FS ") {
// Sample rate
seek(d->childChunks[PROPChunk][i].offset);
sampleRate = readBlock(4).toUInt(0, 4, bigEndian);
}
else if(d->childChunks[PROPChunk][i].name == "CHNL") {
// Channels
seek(d->childChunks[PROPChunk][i].offset);
channels = readBlock(2).toShort(0, bigEndian);
}
}
// Read title & artist from DIIN chunk
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, true);
if(d->hasDiin) {
for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) {
if(d->childChunks[DIINChunk][i].name == "DITI") {
seek(d->childChunks[DIINChunk][i].offset);
if(unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian);
titleStrLength <= d->childChunks[DIINChunk][i].size) {
ByteVector titleStr = readBlock(titleStrLength);
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setTitle(titleStr);
}
}
else if(d->childChunks[DIINChunk][i].name == "DIAR") {
seek(d->childChunks[DIINChunk][i].offset);
if(unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian);
artistStrLength <= d->childChunks[DIINChunk][i].size) {
ByteVector artistStr = readBlock(artistStrLength);
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setArtist(artistStr);
}
}
}
}
if(readProperties) {
if(lengthDSDSamplesTimeChannels == 0) {
// DST compressed signal : need to compute length of DSD uncompressed frames
if(dstFrameRate > 0)
lengthDSDSamplesTimeChannels = static_cast<unsigned long long>(dstNumFrames) *
static_cast<unsigned long long>(sampleRate) /
static_cast<unsigned long long>(dstFrameRate);
else
lengthDSDSamplesTimeChannels = 0;
}
else {
// In DSD uncompressed files, the read number of samples is the total for each channel
if(channels > 0)
lengthDSDSamplesTimeChannels /= channels;
}
int bitrate = 0;
if(lengthDSDSamplesTimeChannels > 0)
bitrate = static_cast<int>(
audioDataSizeinBytes * 8 * sampleRate / lengthDSDSamplesTimeChannels / 1000);
d->properties = std::make_unique<Properties>(sampleRate, channels,
lengthDSDSamplesTimeChannels, bitrate, propertiesStyle);
}
if(!ID3v2Tag()) {
d->tag.access<ID3v2::Tag>(ID3v2Index, true, d->ID3v2FrameFactory);
// By default, ID3 chunk is at root level
d->isID3InPropChunk = false;
d->hasID3v2 = false;
}
}
void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
unsigned long long offset, unsigned long replace,
unsigned int leadingPadding)
{
ByteVector combined;
if(leadingPadding)
combined.append(ByteVector(leadingPadding, '\x00'));
combined.append(name);
combined.append(ByteVector::fromLongLong(data.size(), d->endianness == BigEndian));
combined.append(data);
if((data.size() & 0x01) != 0)
combined.append('\x00');
insert(combined, offset, replace);
}

View File

@@ -1,290 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.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_DSDIFFFILE_H
#define TAGLIB_DSDIFFFILE_H
#include "tfile.h"
#include "id3v2tag.h"
#include "dsdiffproperties.h"
#include "dsdiffdiintag.h"
namespace TagLib {
//! An implementation of DSDIFF metadata
/*!
* This is an implementation of DSDIFF metadata.
*
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
* chunk as well as properties from the file.
* Description of the DSDIFF format is available at
* <a href="https://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf">
* DSDIFF_1.5_Spec.pdf</a>.
* The DSDIFF standard does not explicitly specify the ID3 chunk.
* It can be found at the root level, but also sometimes inside the PROP chunk.
* In addition, title and artist info are stored as part of the standard.
*/
namespace DSDIFF {
//! An implementation of TagLib::File with DSDIFF specific methods.
/*!
* This implements and provides an interface for DSDIFF 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 DSDIFF files.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* This set of flags is used for various operations and is suitable for
* being OR-ed together.
*/
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches DIIN tags.
DIIN = 0x0001,
//! Matches ID3v2 tags.
ID3v2 = 0x0002,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Constructs a DSDIFF file from \a file. If \a readProperties is \c true
* the file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Constructs a DSDIFF file from \a stream. If \a readProperties is \c true
* the file's audio properties will also be read.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*
* \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,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Destroys this instance of the File.
*/
~File() override;
/*!
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN
* tags. The ID3v2 tag is given priority in reading the information -- if
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
* the information from the ID3v2 tag will be returned.
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use the tag-type specific calls.
*
* \note As this tag is not implemented as an ID3v2 tag or a DIIN tag,
* but a union of the two this pointer may not be cast to the specific
* tag types.
*
* \see ID3v2Tag()
* \see DIINTag()
*/
Tag *tag() const override;
/*!
* Returns the ID3V2 Tag for this file.
*
* \note This always returns 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.
*
* \see hasID3v2Tag()
*/
ID3v2::Tag *ID3v2Tag(bool create = false) const;
/*!
* Returns the DSDIFF DIIN Tag for this file
*
*/
DSDIFF::DIIN::Tag *DIINTag(bool create = false) const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const override;
void removeUnsupportedProperties(const StringList &properties) override;
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns the AIFF::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Properties *audioProperties() const override;
/*!
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
* will duplicate its content into the other tag. This returns \c true
* if saving was successful.
*
* If neither exists or if both tags are empty, this will strip the tags
* from the file.
*
* This is the same as calling save(AllTags);
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use parameterized save call below.
*
* \see save(int tags)
*/
bool save() override;
/*!
* Save the file. If \a strip is specified, it is possible to choose if
* tags not specified in \a tags should be stripped from the file or
* retained. With \a version, it is possible to specify whether ID3v2.4
* or ID3v2.3 should be used.
*/
bool save(int tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4);
/*!
* This will strip the tags that match the OR-ed together TagTypes from the
* file. By default it strips all tags. It returns \c true if the tags are
* successfully stripped.
*
* \note This will update the file immediately.
*/
void strip(int tags = AllTags);
/*!
* 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 the DSDIFF
* title and artist tags.
*
* \see DIINTag()
*/
bool hasDIINTag() const;
/*!
* Returns whether or not the given \a stream can be opened as a DSDIFF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
protected:
enum Endianness { BigEndian, LittleEndian };
private:
void removeRootChunk(const ByteVector &id);
void removeRootChunk(unsigned int i);
void removeChildChunk(const ByteVector &id, unsigned int childChunkNum);
void removeChildChunk(unsigned int i, unsigned int childChunkNum);
/*!
* Sets the data for the specified chunk at root level to \a data.
*
* \warning This will update the file immediately.
*/
void setRootChunkData(unsigned int i, const ByteVector &data);
/*!
* Sets the data for the root-level chunk \a name to \a data.
* If a root-level 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 setRootChunkData(const ByteVector &name, const ByteVector &data);
/*!
* Sets the data for the specified child chunk to \a data.
*
* If data is null, then remove the chunk
*
* \warning This will update the file immediately.
*/
void setChildChunkData(unsigned int i, const ByteVector &data,
unsigned int childChunkNum);
/*!
* Sets the data for the child 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 inside the child chunk.
*
* If data is null, then remove the chunks with \a name name
*
* \warning This will update the file immediately.
*/
void setChildChunkData(const ByteVector &name, const ByteVector &data,
unsigned int childChunkNum);
void updateRootChunksStructure(unsigned int startingChunk);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void writeChunk(const ByteVector &name, const ByteVector &data,
unsigned long long offset, unsigned long replace = 0,
unsigned int leadingPadding = 0);
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
} // namespace DSDIFF
} // namespace TagLib
#endif

View File

@@ -1,100 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.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 "dsdiffproperties.h"
#include "tstring.h"
using namespace TagLib;
class DSDIFF::Properties::PropertiesPrivate
{
public:
int length { 0 };
int bitrate { 0 };
int sampleRate { 0 };
int channels { 0 };
int sampleWidth { 0 };
unsigned long long sampleCount { 0 };
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::Properties::Properties(unsigned int sampleRate,
unsigned short channels,
unsigned long long samplesCount,
int bitrate,
ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
{
d->channels = channels;
d->sampleCount = samplesCount;
d->sampleWidth = 1;
d->sampleRate = sampleRate;
d->bitrate = bitrate;
d->length = d->sampleRate > 0
? static_cast<int>(static_cast<double>(d->sampleCount) * 1000.0 / d->sampleRate + 0.5)
: 0;
}
DSDIFF::Properties::~Properties() = default;
int DSDIFF::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int DSDIFF::Properties::lengthInMilliseconds() const
{
return d->length;
}
int DSDIFF::Properties::bitrate() const
{
return d->bitrate;
}
int DSDIFF::Properties::sampleRate() const
{
return d->sampleRate;
}
int DSDIFF::Properties::channels() const
{
return d->channels;
}
int DSDIFF::Properties::bitsPerSample() const
{
return d->sampleWidth;
}
long long DSDIFF::Properties::sampleCount() const
{
return d->sampleCount;
}

View File

@@ -1,79 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.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_DSDIFFPROPERTIES_H
#define TAGLIB_DSDIFFPROPERTIES_H
#include "audioproperties.h"
namespace TagLib {
namespace DSDIFF {
class File;
//! An implementation of audio property reading for DSDIFF
/*!
* This reads the data from a DSDIFF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of DSDIFF::Properties with the data read from the
* ByteVector \a data.
*/
Properties(unsigned int sampleRate, unsigned short channels,
unsigned long long samplesCount, int bitrate,
ReadStyle style);
/*!
* Destroys this DSDIFF::Properties instance.
*/
~Properties() override;
// Reimplementations.
int lengthInSeconds() const override;
int lengthInMilliseconds() const override;
int bitrate() const override;
int sampleRate() const override;
int channels() const override;
int bitsPerSample() const;
long long sampleCount() const;
private:
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
} // namespace DSDIFF
} // namespace TagLib
#endif

View File

@@ -1,230 +0,0 @@
/***************************************************************************
copyright : (C) 2013-2023 Stephen F. Booth
email : me@sbooth.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 "dsffile.h"
#include "tdebug.h"
#include "tpropertymap.h"
#include "tagutils.h"
using namespace TagLib;
class DSF::File::FilePrivate
{
public:
FilePrivate(const ID3v2::FrameFactory *frameFactory)
: ID3v2FrameFactory(frameFactory ? frameFactory
: ID3v2::FrameFactory::instance())
{
}
~FilePrivate() = default;
FilePrivate(const FilePrivate &) = delete;
FilePrivate &operator=(const FilePrivate &) = delete;
const ID3v2::FrameFactory *ID3v2FrameFactory;
long long fileSize = 0;
long long metadataOffset = 0;
std::unique_ptr<Properties> properties;
std::unique_ptr<ID3v2::Tag> tag;
};
bool DSF::File::isSupported(IOStream *stream)
{
// A DSF file has to start with "DSD "
const ByteVector id = Utils::readHeader(stream, 4, false);
return id.startsWith("DSD ");
}
DSF::File::File(FileName file, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(propertiesStyle);
}
DSF::File::File(IOStream *stream, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(propertiesStyle);
}
DSF::File::~File() = default;
ID3v2::Tag *DSF::File::tag() const
{
return d->tag.get();
}
PropertyMap DSF::File::properties() const
{
return d->tag->properties();
}
PropertyMap DSF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
DSF::Properties *DSF::File::audioProperties() const
{
return d->properties.get();
}
bool DSF::File::save()
{
return save(ID3v2::v4);
}
bool DSF::File::save(ID3v2::Version version)
{
if(readOnly()) {
debug("DSF::File::save() - Cannot save to a read only file.");
return false;
}
// Three things must be updated: the file size, the tag data, and the metadata offset
if(d->tag->isEmpty()) {
long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset to 0 since there is no longer a tag
if(d->metadataOffset) {
insert(ByteVector::fromLongLong(0ULL, false), 20, 8);
d->metadataOffset = 0;
}
// Delete the old tag
truncate(newFileSize);
}
else {
ByteVector tagData = d->tag->render(version);
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
long long newFileSize = newMetadataOffset + tagData.size();
long long oldTagSize = d->fileSize - newMetadataOffset;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset
if(d->metadataOffset != newMetadataOffset) {
insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8);
d->metadataOffset = newMetadataOffset;
}
// Delete the old tag and write the new one
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
}
return true;
}
void DSF::File::read(AudioProperties::ReadStyle propertiesStyle)
{
if(!isOpen())
return;
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
// The file format is not chunked in the sense of a RIFF File, though
// DSD chunk
ByteVector chunkName = readBlock(4);
if(chunkName != "DSD ") {
debug("DSF::File::read() -- Not a DSF file.");
setValid(false);
return;
}
long long dsdHeaderSize = readBlock(8).toLongLong(false);
// Integrity check
if(dsdHeaderSize != 28) {
debug("DSF::File::read() -- File is corrupted, wrong DSD header size");
setValid(false);
return;
}
d->fileSize = readBlock(8).toLongLong(false);
// File is malformed or corrupted, allow trailing garbage
if(d->fileSize > length()) {
debug("DSF::File::read() -- File is corrupted wrong length");
setValid(false);
return;
}
d->metadataOffset = readBlock(8).toLongLong(false);
// File is malformed or corrupted
if(d->metadataOffset > d->fileSize) {
debug("DSF::File::read() -- Invalid metadata offset.");
setValid(false);
return;
}
// Format chunk
chunkName = readBlock(4);
if(chunkName != "fmt ") {
debug("DSF::File::read() -- Missing 'fmt ' chunk.");
setValid(false);
return;
}
long long fmtHeaderSize = readBlock(8).toLongLong(false);
if(fmtHeaderSize != 52) {
debug("DSF::File::read() -- File is corrupted, wrong FMT header size");
setValid(false);
return;
}
d->properties = std::make_unique<Properties>(readBlock(fmtHeaderSize), propertiesStyle);
// Skip the data chunk
// A metadata offset of 0 indicates the absence of an ID3v2 tag
if(d->metadataOffset == 0)
d->tag = std::make_unique<ID3v2::Tag>(nullptr, 0, d->ID3v2FrameFactory);
else
d->tag = std::make_unique<ID3v2::Tag>(this, d->metadataOffset,
d->ID3v2FrameFactory);
}

View File

@@ -1,156 +0,0 @@
/***************************************************************************
copyright : (C) 2013-2023 Stephen F. Booth
email : me@sbooth.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_DSFFILE_H
#define TAGLIB_DSFFILE_H
#include <memory>
#include "taglib_export.h"
#include "tfile.h"
#include "dsfproperties.h"
#include "id3v2tag.h"
namespace TagLib {
//! An implementation of DSF metadata
/*!
* This is an implementation of DSF metadata using an ID3v2 tag inside the
* metadata chunk.
* The DSF specification is located at
* http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
*/
namespace DSF {
//! An implementation of TagLib::File with DSF specific methods
/*!
* This implements and provides an interface for DSF 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 DSF files.
*/
class TAGLIB_EXPORT File : public TagLib::File {
public:
/*!
* Constructs a DSD stream file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Constructs a DSD stream file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*
* \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,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Destroys this instance of the File.
*/
~File() override;
/*!
* Returns the ID3v2 Tag for this file.
*/
ID3v2::Tag *tag() const override;
/*!
* Implements the unified property interface -- export function.
* Forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const override;
/*!
* Implements the unified property interface -- import function.
* Forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns the DSF::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Properties *audioProperties() const override;
/*!
* Save the file.
*
* This returns \c true if the save was successful.
*/
bool save() override;
/*!
* Save the file.
*
* \a version specifies the ID3v2 version to be used for writing tags.
*/
bool save(ID3v2::Version version);
/*!
* Returns whether or not the given \a stream can be opened as a DSF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
void read(AudioProperties::ReadStyle propertiesStyle);
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
} // namespace DSF
} // namespace TagLib
#endif

View File

@@ -1,134 +0,0 @@
/***************************************************************************
copyright : (C) 2013-2023 Stephen F. Booth
email : me@sbooth.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 "dsfproperties.h"
using namespace TagLib;
class DSF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() = default;
~PropertiesPrivate() = default;
PropertiesPrivate(const PropertiesPrivate &) = delete;
PropertiesPrivate &operator=(const PropertiesPrivate &) = delete;
// Nomenclature is from DSF file format specification
unsigned int formatVersion = 0;
unsigned int formatID = 0;
unsigned int channelType = 0;
unsigned int channelNum = 0;
unsigned int samplingFrequency = 0;
unsigned int bitsPerSample = 0;
long long sampleCount = 0;
unsigned int blockSizePerChannel = 0;
// Computed
unsigned int bitrate = 0;
unsigned int length = 0;
};
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
{
read(data);
}
DSF::Properties::~Properties() = default;
int DSF::Properties::lengthInMilliseconds() const
{
return d->length;
}
int DSF::Properties::bitrate() const
{
return d->bitrate;
}
int DSF::Properties::sampleRate() const
{
return d->samplingFrequency;
}
int DSF::Properties::channels() const
{
return d->channelNum;
}
int DSF::Properties::formatVersion() const
{
return d->formatVersion;
}
int DSF::Properties::formatID() const
{
return d->formatID;
}
int DSF::Properties::channelType() const
{
return d->channelType;
}
int DSF::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
long long DSF::Properties::sampleCount() const
{
return d->sampleCount;
}
int DSF::Properties::blockSizePerChannel() const
{
return d->blockSizePerChannel;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSF::Properties::read(const ByteVector &data)
{
d->formatVersion = data.toUInt(0U,false);
d->formatID = data.toUInt(4U,false);
d->channelType = data.toUInt(8U,false);
d->channelNum = data.toUInt(12U,false);
d->samplingFrequency = data.toUInt(16U,false);
d->bitsPerSample = data.toUInt(20U,false);
d->sampleCount = data.toLongLong(24U,false);
d->blockSizePerChannel = data.toUInt(32U,false);
d->bitrate = static_cast<unsigned int>(
d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5);
d->length = d->samplingFrequency > 0
? static_cast<unsigned int>(static_cast<double>(d->sampleCount) * 1000.0 / d->samplingFrequency + 0.5)
: 0;
}

View File

@@ -1,73 +0,0 @@
/***************************************************************************
copyright : (C) 2013-2023 Stephen F. Booth
email : me@sbooth.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_DSFPROPERTIES_H
#define TAGLIB_DSFPROPERTIES_H
#include <memory>
#include "taglib_export.h"
#include "tbytevector.h"
#include "audioproperties.h"
namespace TagLib {
namespace DSF {
//! An implementation of audio properties for DSF
class TAGLIB_EXPORT Properties : public AudioProperties {
public:
Properties(const ByteVector &data, ReadStyle style);
~Properties() override;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
int lengthInMilliseconds() const override;
int bitrate() const override;
int sampleRate() const override;
int channels() const override;
int formatVersion() const;
int formatID() const;
/*!
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels,
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels
*/
int channelType() const;
int bitsPerSample() const;
long long sampleCount() const;
int blockSizePerChannel() const;
private:
void read(const ByteVector &data);
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
} // namespace DSF
} // namespace TagLib
#endif

View File

@@ -1,7 +1,7 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2010 by Alex Novichkov
email : novichko@atnet.ru
(added APE file support)
@@ -27,405 +27,94 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tfile.h>
#include <tstring.h>
#include <tdebug.h>
#include "fileref.h"
#include <cstring>
#include <utility>
#include "taglib_config.h"
#include "tfilestream.h"
#include "tpropertymap.h"
#include "tstringlist.h"
#include "tvariant.h"
#include "tdebug.h"
#include "asffile.h"
#include "mpegfile.h"
#ifdef TAGLIB_WITH_RIFF
#include "vorbisfile.h"
#include "flacfile.h"
#include "oggflacfile.h"
#include "mpcfile.h"
#include "mp4file.h"
#include "wavpackfile.h"
#include "speexfile.h"
#include "trueaudiofile.h"
#include "aifffile.h"
#include "wavfile.h"
#endif
#ifdef TAGLIB_WITH_APE
#include "apefile.h"
#include "mpcfile.h"
#include "wavpackfile.h"
#endif
#ifdef TAGLIB_WITH_ASF
#include "asffile.h"
#endif
#ifdef TAGLIB_WITH_VORBIS
#include "flacfile.h"
#include "speexfile.h"
#include "vorbisfile.h"
#include "oggflacfile.h"
#include "opusfile.h"
#endif
#ifdef TAGLIB_WITH_MOD
#include "itfile.h"
#include "modfile.h"
#include "xmfile.h"
#include "s3mfile.h"
#endif
#ifdef TAGLIB_WITH_MP4
#include "mp4file.h"
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
#include "trueaudiofile.h"
#endif
#ifdef TAGLIB_WITH_DSF
#include "dsffile.h"
#include "dsdifffile.h"
#endif
#ifdef TAGLIB_WITH_SHORTEN
#include "shortenfile.h"
#endif
#include "itfile.h"
#include "xmfile.h"
using namespace TagLib;
class FileRef::FileTypeResolver::FileTypeResolverPrivate
{
};
class FileRef::StreamTypeResolver::StreamTypeResolverPrivate
{
};
namespace
{
List<const FileRef::FileTypeResolver *> fileTypeResolvers;
// Detect the file type by user-defined resolvers.
File *detectByResolvers(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
#ifdef _WIN32
if(::wcslen(fileName) == 0)
return nullptr;
#else
if(::strlen(fileName) == 0)
return nullptr;
#endif
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
File *file = resolver->createFile(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
}
return nullptr;
}
File *detectByResolvers(IOStream* stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
if(auto streamResolver = dynamic_cast<const FileRef::StreamTypeResolver *>(resolver)) {
if(File *file = streamResolver->createFileFromStream(
stream, readAudioProperties, audioPropertiesStyle))
return file;
}
}
return nullptr;
}
// Detect the file type based on the file extension.
File* detectByExtension(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
#ifdef _WIN32
const String s = stream->name().toString();
#else
const String s(stream->name());
#endif
String ext;
if(const int pos = s.rfind("."); pos != -1)
ext = s.substr(pos + 1).upper();
// 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
// that a default file type resolver is created.
if(ext.isEmpty())
return nullptr;
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
File *file = nullptr;
if(ext == "MP3" || ext == "MP2" || ext == "AAC")
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
#ifdef TAGLIB_WITH_VORBIS
else if(ext == "OGG")
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "OGA") {
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
if(!file->isValid()) {
delete file;
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
}
}
else if(ext == "FLAC")
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "SPX")
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "OPUS")
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_APE
else if(ext == "MPC")
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WV")
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "APE")
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
else if(ext == "TTA")
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MP4
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_ASF
else if(ext == "WMA" || ext == "ASF")
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_RIFF
else if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WAV")
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MOD
// module, nst and wow are possible but uncommon extensions
else if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
file = new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "S3M")
file = new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "IT")
file = new IT::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "XM")
file = new XM::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_DSF
else if(ext == "DSF")
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "DFF" || ext == "DSDIFF")
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_SHORTEN
else if(ext == "SHN")
file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
// if file is not valid, leave it to content-based detection.
if(file) {
if(file->isValid())
return file;
delete file;
}
return nullptr;
}
// Detect the file type based on the actual content of the stream.
File *detectByContent(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = nullptr;
if(MPEG::File::isSupported(stream))
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
#ifdef TAGLIB_WITH_VORBIS
else if(Ogg::Vorbis::File::isSupported(stream))
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::FLAC::File::isSupported(stream))
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(FLAC::File::isSupported(stream))
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::Speex::File::isSupported(stream))
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::Opus::File::isSupported(stream))
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_APE
else if(MPC::File::isSupported(stream))
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(WavPack::File::isSupported(stream))
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
else if(APE::File::isSupported(stream))
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
else if(TrueAudio::File::isSupported(stream))
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MP4
else if(MP4::File::isSupported(stream))
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_ASF
else if(ASF::File::isSupported(stream))
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_RIFF
else if(RIFF::AIFF::File::isSupported(stream))
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(RIFF::WAV::File::isSupported(stream))
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_DSF
else if(DSF::File::isSupported(stream))
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(DSDIFF::File::isSupported(stream))
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_SHORTEN
else if(Shorten::File::isSupported(stream))
file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
// isSupported() only does a quick check, so double check the file here.
if(file) {
if(file->isValid())
return file;
delete file;
}
return nullptr;
}
} // namespace
class FileRef::FileRefPrivate
class FileRef::FileRefPrivate : public RefCounter
{
public:
FileRefPrivate() = default;
~FileRefPrivate()
{
FileRefPrivate(File *f) : RefCounter(), file(f) {}
~FileRefPrivate() {
delete file;
delete stream;
}
FileRefPrivate(const FileRefPrivate &) = delete;
FileRefPrivate &operator=(const FileRefPrivate &) = delete;
bool isNull() const
{
return !file || !file->isValid();
}
bool isNullWithDebugMessage([[maybe_unused]] const String &methodName) const
{
if(isNull()) {
debug("FileRef::" + methodName + "() - Called without a valid file.");
return true;
}
return false;
}
File *file { nullptr };
IOStream *stream { nullptr };
File *file;
static List<const FileTypeResolver *> fileTypeResolvers;
};
List<const FileRef::FileTypeResolver *> FileRef::FileRefPrivate::fileTypeResolvers;
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef() :
d(std::make_shared<FileRefPrivate>())
FileRef::FileRef()
{
d = new FileRefPrivate(0);
}
FileRef::FileRef(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) :
d(std::make_shared<FileRefPrivate>())
AudioProperties::ReadStyle audioPropertiesStyle)
{
parse(fileName, readAudioProperties, audioPropertiesStyle);
d = new FileRefPrivate(create(fileName, readAudioProperties, audioPropertiesStyle));
}
FileRef::FileRef(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
d(std::make_shared<FileRefPrivate>())
FileRef::FileRef(File *file)
{
parse(stream, readAudioProperties, audioPropertiesStyle);
d = new FileRefPrivate(file);
}
FileRef::FileRef(File *file) :
d(std::make_shared<FileRefPrivate>())
FileRef::FileRef(const FileRef &ref) : d(ref.d)
{
d->file = file;
d->ref();
}
FileRef::FileRef(const FileRef &) = default;
FileRef::~FileRef() = default;
FileRef::~FileRef()
{
if(d->deref())
delete d;
}
Tag *FileRef::tag() const
{
if(d->isNullWithDebugMessage(__func__)) {
return nullptr;
if(isNull()) {
debug("FileRef::tag() - Called without a valid file.");
return 0;
}
return d->file->tag();
}
PropertyMap FileRef::properties() const
{
if(d->isNullWithDebugMessage(__func__)) {
return PropertyMap();
}
return d->file->properties();
}
void FileRef::removeUnsupportedProperties(const StringList& properties)
{
if(d->isNullWithDebugMessage(__func__)) {
return;
}
return d->file->removeUnsupportedProperties(properties);
}
PropertyMap FileRef::setProperties(const PropertyMap &properties)
{
if(d->isNullWithDebugMessage(__func__)) {
return PropertyMap();
}
return d->file->setProperties(properties);
}
StringList FileRef::complexPropertyKeys() const
{
if(d->isNullWithDebugMessage(__func__)) {
return StringList();
}
return d->file->complexPropertyKeys();
}
List<VariantMap> FileRef::complexProperties(const String &key) const
{
if(d->isNullWithDebugMessage(__func__)) {
return List<VariantMap>();
}
return d->file->complexProperties(key);
}
bool FileRef::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(d->isNullWithDebugMessage(__func__)) {
return false;
}
return d->file->setComplexProperties(key, value);
}
AudioProperties *FileRef::audioProperties() const
{
if(d->isNullWithDebugMessage(__func__)) {
return nullptr;
if(isNull()) {
debug("FileRef::audioProperties() - Called without a valid file.");
return 0;
}
return d->file->audioProperties();
}
@@ -437,7 +126,8 @@ File *FileRef::file() const
bool FileRef::save()
{
if(d->isNullWithDebugMessage(__func__)) {
if(isNull()) {
debug("FileRef::save() - Called without a valid file.");
return false;
}
return d->file->save();
@@ -445,58 +135,33 @@ bool FileRef::save()
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
{
fileTypeResolvers.prepend(resolver);
FileRefPrivate::fileTypeResolvers.prepend(resolver);
return resolver;
}
void FileRef::clearFileTypeResolvers() // static
{
fileTypeResolvers.clear();
}
StringList FileRef::defaultFileExtensions()
{
StringList l;
l.append("mp3");
l.append("mp2");
l.append("aac");
#ifdef TAGLIB_WITH_VORBIS
l.append("ogg");
l.append("flac");
l.append("oga");
l.append("opus");
l.append("spx");
#endif
#ifdef TAGLIB_WITH_APE
l.append("mp3");
l.append("mpc");
l.append("wv");
l.append("ape");
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
l.append("spx");
l.append("tta");
#endif
#ifdef TAGLIB_WITH_MP4
l.append("m4a");
l.append("m4r");
l.append("m4b");
l.append("m4p");
l.append("3g2");
l.append("mp4");
l.append("m4v");
#endif
#ifdef TAGLIB_WITH_ASF
l.append("wma");
l.append("asf");
#endif
#ifdef TAGLIB_WITH_RIFF
l.append("aif");
l.append("aiff");
l.append("afc");
l.append("aifc");
l.append("wav");
#endif
#ifdef TAGLIB_WITH_MOD
l.append("ape");
l.append("mod");
l.append("module"); // alias for "mod"
l.append("nst"); // alias for "mod"
@@ -504,31 +169,27 @@ StringList FileRef::defaultFileExtensions()
l.append("s3m");
l.append("it");
l.append("xm");
#endif
#ifdef TAGLIB_WITH_DSF
l.append("dsf");
l.append("dff");
l.append("dsdiff"); // alias for "dff"
#endif
#ifdef TAGLIB_WITH_SHORTEN
l.append("shn");
#endif
return l;
}
bool FileRef::isNull() const
{
return d->isNull();
return !d->file || !d->file->isValid();
}
FileRef &FileRef::operator=(const FileRef &) = default;
void FileRef::swap(FileRef &ref) noexcept
FileRef &FileRef::operator=(const FileRef &ref)
{
using std::swap;
if(&ref == this)
return *this;
swap(d, ref.d);
if(d->deref())
delete d;
d = ref.d;
d->ref();
return *this;
}
bool FileRef::operator==(const FileRef &ref) const
@@ -541,66 +202,77 @@ bool FileRef::operator!=(const FileRef &ref) const
return ref.d->file != d->file;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void FileRef::parse(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
File *FileRef::create(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) // static
{
// Try user-defined resolvers.
d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
List<const FileTypeResolver *>::ConstIterator it = FileRefPrivate::fileTypeResolvers.begin();
// Try to resolve file types based on the file extension.
for(; it != FileRefPrivate::fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
}
d->stream = new FileStream(fileName);
d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Ok, this is really dumb for now, but it works for testing.
// At last, try to resolve file types based on the actual content.
String s;
d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
#ifdef _WIN32
s = (wcslen((const wchar_t *) fileName) > 0) ? String((const wchar_t *) fileName) : String((const char *) fileName);
#else
s = fileName;
#endif
// Stream have to be closed here if failed to resolve file types.
// 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
// that a default file type resolver is created.
delete d->stream;
d->stream = nullptr;
int pos = s.rfind(".");
if(pos != -1) {
String ext = s.substr(pos + 1).upper();
if(ext == "MP3")
return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OGA") {
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
if (file->isValid())
return file;
delete file;
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
}
if(ext == "FLAC")
return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "MPC")
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF")
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
}
return 0;
}
void FileRef::parse(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
// Try user-defined stream resolvers.
d->file = detectByResolvers(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try user-defined resolvers.
d->file = detectByResolvers(stream->name(), readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try to resolve file types based on the file extension.
d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// At last, try to resolve file types based on the actual content of the file.
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileTypeResolver::FileTypeResolver() = default;
FileRef::FileTypeResolver::~FileTypeResolver() = default;
FileRef::StreamTypeResolver::StreamTypeResolver() = default;
FileRef::StreamTypeResolver::~StreamTypeResolver() = default;

View File

@@ -60,29 +60,43 @@ namespace TagLib {
{
public:
//! A class for pluggable file type resolution.
//! A class for pluggable file type resolution.
/*!
* %File type resolver, better implement StreamTypeResolver in order to
* support both file and stream resolution.
*/
/*!
* This class is used to add extend TagLib's very basic file name based file
* type resolution.
*
* This can be accomplished with:
*
* \code
*
* class MyFileTypeResolver : FileTypeResolver
* {
* TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle)
* {
* if(someCheckForAnMP3File(fileName))
* return new TagLib::MPEG::File(fileName);
* return 0;
* }
* }
*
* FileRef::addFileTypeResolver(new MyFileTypeResolver);
*
* \endcode
*
* Naturally a less contrived example would be slightly more complex. This
* can be used to plug in mime-type detection systems or to add new file types
* to TagLib.
*/
class TAGLIB_EXPORT FileTypeResolver
{
TAGLIB_IGNORE_MISSING_DESTRUCTOR
public:
FileTypeResolver();
/*!
* Destroys this FileTypeResolver instance.
*/
virtual ~FileTypeResolver() = 0;
FileTypeResolver(const FileTypeResolver &) = delete;
FileTypeResolver &operator=(const FileTypeResolver &) = delete;
/*!
* This method must be overridden to provide an additional file type
* resolver. If the resolver is able to determine the file type it should
* return a valid File object; if not it should return nullptr.
* return a valid File object; if not it should return 0.
*
* \note The created file is then owned by the FileRef and should not be
* deleted. Deletion will happen automatically when the FileRef passes
@@ -92,81 +106,6 @@ namespace TagLib {
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average) const = 0;
private:
class FileTypeResolverPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FileTypeResolverPrivate> d;
};
//! A class for pluggable stream type resolution.
/*!
* This class is used to extend TagLib's very basic file name based file
* type resolution.
*
* This can be accomplished with:
*
* \code
*
* class MyStreamTypeResolver : StreamTypeResolver
* {
* TagLib::File *createFile(TagLib::FileName *fileName, bool readProps,
* AudioProperties::ReadStyle readStyle) const override
* {
* if(someCheckForAnMP3File(fileName))
* return new TagLib::MPEG::File(fileName, readProps, readStyle);
* return nullptr;
* }
*
* TagLib::File *createFileFromStream(TagLib::IOStream *s, bool readProps,
* AudioProperties::ReadStyle readStyle) const override
* {
* if(someCheckForAnMP3Stream(s))
* return new TagLib::MPEG::File(s, readProps, readStyle);
* return nullptr;
* }
* }
*
* FileRef::addFileTypeResolver(new MyStreamTypeResolver);
*
* \endcode
*
* Naturally a less contrived example would be slightly more complex. This
* can be used to plug in mime-type detection systems or to add new file types
* to TagLib.
*/
class TAGLIB_EXPORT StreamTypeResolver : public FileTypeResolver
{
public:
StreamTypeResolver();
/*!
* Destroys this StreamTypeResolver instance.
*/
virtual ~StreamTypeResolver() override = 0; // virtual is needed by SWIG
StreamTypeResolver(const StreamTypeResolver &) = delete;
StreamTypeResolver &operator=(const StreamTypeResolver &) = delete;
/*!
* This method must be overridden to provide an additional stream type
* resolver. If the resolver is able to determine the file type it should
* return a valid File object; if not it should return nullptr.
*
* \note The created file is then owned by the FileRef and should not be
* deleted. Deletion will happen automatically when the FileRef passes
* out of scope.
*
* \see createFile()
*/
virtual File *createFileFromStream(IOStream *stream,
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average) const = 0;
private:
class StreamTypeResolverPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<StreamTypeResolverPrivate> d;
};
/*!
@@ -175,9 +114,9 @@ namespace TagLib {
FileRef();
/*!
* Create a FileRef from \a fileName. If \a readAudioProperties is \c true then
* Create a FileRef from \a fileName. If \a readAudioProperties is true then
* the audio properties will be read using \a audioPropertiesStyle. If
* \a readAudioProperties is \c false then \a audioPropertiesStyle will be
* \a readAudioProperties is false then \a audioPropertiesStyle will be
* ignored.
*
* Also see the note in the class documentation about why you may not want to
@@ -189,24 +128,7 @@ namespace TagLib {
audioPropertiesStyle = AudioProperties::Average);
/*!
* Construct a FileRef from an opened \a IOStream. If \a readAudioProperties
* is \c true then the audio properties will be read using \a audioPropertiesStyle.
* If \a readAudioProperties is \c false then \a audioPropertiesStyle will be
* ignored.
*
* Also see the note in the class documentation about why you may not want to
* use this method in your application.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
explicit FileRef(IOStream* stream,
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average);
/*!
* Construct a FileRef using \a file. The FileRef now takes ownership of the
* Contruct a FileRef using \a file. The FileRef now takes ownership of the
* pointer and will delete the File when it passes out of scope.
*/
explicit FileRef(File *file);
@@ -219,102 +141,24 @@ namespace TagLib {
/*!
* Destroys this FileRef instance.
*/
~FileRef();
virtual ~FileRef();
/*!
* Returns a pointer to the represented file's tag.
* Returns a pointer to represented file's tag.
*
* \warning This pointer will become invalid when this FileRef and all
* copies pass out of scope.
*
* \warning Do not cast it to any subclasses of Tag.
* Use tag returning methods of appropriate subclasses of File instead.
* \warning Do not cast it to any subclasses of \class Tag.
* Use tag returning methods of appropriate subclasses of \class File instead.
*
* \see File::tag()
*/
Tag *tag() const;
/*!
* Exports the tags of the file as dictionary mapping (human readable) tag
* names (uppercase Strings) to StringLists of tag values. Calls this
* method on the wrapped File instance.
* For each metadata object of the file that could not be parsed into the PropertyMap
* format, the returned map's unsupportedData() list will contain one entry identifying
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
* to remove (a subset of) them.
* For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2
* tag) only the most "modern" one will be exported (ID3v2 in this case).
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties, or a subset of them, from the file's metadata.
* The parameter \a properties must contain only entries from
* properties().unsupportedData().
*/
void removeUnsupportedProperties(const StringList& properties);
/*!
* Sets the tags of the wrapped File to those specified in \a properties.
* If some value(s) could not be written to the specific metadata format,
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
* indicating that no problems occurred.
* With file types that support several tag formats (for instance, MP3 files can have
* ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one
* (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't
* be taken into account for the return value of this function.
* See the documentation of the subclass implementations for detailed descriptions.
*/
PropertyMap setProperties(const PropertyMap &properties);
/*!
* Get the keys of complex properties, i.e. properties which cannot be
* represented simply by a string.
* Because such properties might be expensive to fetch, there are separate
* operations to get the available keys - which is expected to be cheap -
* and getting and setting the property values.
* Calls the method on the wrapped File, which collects the keys from one
* or more of its tags.
*/
StringList complexPropertyKeys() const;
/*!
* Get the complex properties for a given \a key.
* In order to be flexible for different metadata formats, the properties
* are represented as variant maps. Despite this dynamic nature, some
* degree of standardization should be achieved between formats:
*
* - PICTURE
* - data: ByteVector with picture data
* - description: String with description
* - pictureType: String with type as specified for ID3v2,
* e.g. "Front Cover", "Back Cover", "Band"
* - mimeType: String with image format, e.g. "image/jpeg"
* - optionally more information found in the tag, such as
* "width", "height", "numColors", "colorDepth" int values
* in FLAC pictures
* - GENERALOBJECT
* - data: ByteVector with object data
* - description: String with description
* - fileName: String with file name
* - mimeType: String with MIME type
* - this is currently only implemented for ID3v2 GEOB frames
*
* Calls the method on the wrapped File, which gets the properties from one
* or more of its tags.
*/
List<VariantMap> complexProperties(const String &key) const;
/*!
* Set all complex properties for a given \a key using variant maps as
* \a value with the same format as returned by complexProperties().
* An empty list as \a value removes all complex properties for \a key.
*/
bool setComplexProperties(const String &key, const List<VariantMap> &value);
/*!
* Returns the audio properties for this FileRef. If no audio properties
* were read then this will return a null pointer.
* were read then this will returns a null pointer.
*/
AudioProperties *audioProperties() const;
@@ -336,7 +180,7 @@ namespace TagLib {
File *file() const;
/*!
* Saves the file. Returns \c true on success.
* Saves the file. Returns true on success.
*/
bool save();
@@ -347,18 +191,13 @@ namespace TagLib {
* is tried.
*
* Returns a pointer to the added resolver (the same one that's passed in --
* this is mostly so that static initializers have something to use for
* this is mostly so that static inialializers have something to use for
* assignment).
*
* \see FileTypeResolver
*/
static const FileTypeResolver *addFileTypeResolver(const FileTypeResolver *resolver);
/*!
* Remove all resolvers added by addFileTypeResolver().
*/
static void clearFileTypeResolvers();
/*!
* As is mentioned elsewhere in this class's documentation, the default file
* type resolution code provided by TagLib only works by comparing file
@@ -370,7 +209,7 @@ namespace TagLib {
* by TagLib for resolution is case-insensitive.
*
* \note This does not account for any additional file type resolvers that
* are plugged in. Also note that this is not intended to replace a proper
* are plugged in. Also note that this is not intended to replace a propper
* mime-type resolution system, but is just here for reference.
*
* \see FileTypeResolver
@@ -378,7 +217,7 @@ namespace TagLib {
static StringList defaultFileExtensions();
/*!
* Returns \c true if the file (and as such other pointers) are null.
* Returns true if the file (and as such other pointers) are null.
*/
bool isNull() const;
@@ -388,28 +227,35 @@ namespace TagLib {
FileRef &operator=(const FileRef &ref);
/*!
* Exchanges the content of the FileRef with the content of \a ref.
*/
void swap(FileRef &ref) noexcept;
/*!
* Returns \c true if this FileRef and \a ref point to the same File object.
* Returns true if this FileRef and \a ref point to the same File object.
*/
bool operator==(const FileRef &ref) const;
/*!
* Returns \c true if this FileRef and \a ref do not point to the same File
* Returns true if this FileRef and \a ref do not point to the same File
* object.
*/
bool operator!=(const FileRef &ref) const;
private:
void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
/*!
* A simple implementation of file type guessing. If \a readAudioProperties
* is true then the audio properties will be read using
* \a audioPropertiesStyle. If \a readAudioProperties is false then
* \a audioPropertiesStyle will be ignored.
*
* \note You generally shouldn't use this method, but instead the constructor
* directly.
*
* \deprecated
*/
static File *create(FileName fileName,
bool readAudioProperties = true,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
private:
class FileRefPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::shared_ptr<FileRefPrivate> d;
FileRefPrivate *d;
};
} // namespace TagLib

View File

@@ -23,19 +23,19 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "flacfile.h"
#include <tbytevector.h>
#include <tstring.h>
#include <tlist.h>
#include <tdebug.h>
#include <tagunion.h>
#include <algorithm>
#include <utility>
#include <id3v2header.h>
#include <id3v2tag.h>
#include <id3v1tag.h>
#include <xiphcomment.h>
#include "tdebug.h"
#include "tpropertymap.h"
#include "tagunion.h"
#include "tagutils.h"
#include "id3v2tag.h"
#include "id3v1tag.h"
#include "xiphcomment.h"
#include "flacpicture.h"
#include "flacfile.h"
#include "flacmetadatablock.h"
#include "flacunknownmetadatablock.h"
@@ -43,186 +43,107 @@ using namespace TagLib;
namespace
{
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
constexpr long MinPaddingLength = 4096;
constexpr long MaxPaddingLegnth = 1024 * 1024;
constexpr char LastBlockFlag = '\x80';
} // namespace
enum { XiphIndex = 0, ID3v2Index = 1, ID3v1Index = 2 };
enum { MinPaddingLength = 4096 };
enum { LastBlockFlag = 0x80 };
}
class FLAC::File::FilePrivate
{
public:
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
ID3v2FrameFactory(frameFactory)
FilePrivate() :
ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
ID3v2Location(-1),
ID3v2OriginalSize(0),
ID3v1Location(-1),
properties(0),
flacStart(0),
streamStart(0),
streamLength(0),
scanned(false),
hasXiphComment(false),
hasID3v2(false),
hasID3v1(false)
{
blocks.setAutoDelete(true);
}
~FilePrivate()
{
for(uint i = 0; i < blocks.size(); i++) {
delete blocks[i];
}
delete properties;
}
const ID3v2::FrameFactory *ID3v2FrameFactory;
offset_t ID3v2Location { -1 };
long ID3v2OriginalSize { 0 };
long ID3v2Location;
uint ID3v2OriginalSize;
offset_t ID3v1Location { -1 };
long ID3v1Location;
TagUnion tag;
std::unique_ptr<Properties> properties;
Properties *properties;
ByteVector streamInfoData;
ByteVector xiphCommentData;
List<FLAC::MetadataBlock *> blocks;
List<MetadataBlock *> blocks;
offset_t flacStart { 0 };
offset_t streamStart { 0 };
bool scanned { false };
long flacStart;
long streamStart;
long streamLength;
bool scanned;
bool hasXiphComment;
bool hasID3v2;
bool hasID3v1;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool FLAC::File::isSupported(IOStream *stream)
{
// A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return buffer.find("fLaC") >= 0;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::File::File(FileName file, bool readProperties,
Properties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
d(std::make_unique<FilePrivate>(
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
Properties::ReadStyle propertiesStyle) :
TagLib::File(file)
{
if(isOpen())
read(readProperties);
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(std::make_unique<FilePrivate>(frameFactory))
bool readProperties, Properties::ReadStyle propertiesStyle) :
TagLib::File(file)
{
if(isOpen())
read(readProperties);
}
FLAC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>(
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
{
if(isOpen())
read(readProperties);
d = new FilePrivate;
d->ID3v2FrameFactory = frameFactory;
read(readProperties, propertiesStyle);
}
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>(frameFactory))
bool readProperties, Properties::ReadStyle propertiesStyle) :
TagLib::File(stream)
{
if(isOpen())
read(readProperties);
d = new FilePrivate;
d->ID3v2FrameFactory = frameFactory;
read(readProperties, propertiesStyle);
}
FLAC::File::~File() = default;
FLAC::File::~File()
{
delete d;
}
TagLib::Tag *FLAC::File::tag() const
{
return &d->tag;
}
PropertyMap FLAC::File::properties() const
{
return d->tag.properties();
}
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
{
d->tag.removeUnsupportedProperties(unsupported);
}
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
{
return xiphComment(true)->setProperties(properties);
}
StringList FLAC::File::complexPropertyKeys() const
{
StringList keys = TagLib::File::complexPropertyKeys();
if(!keys.contains("PICTURE")) {
if(std::any_of(d->blocks.cbegin(), d->blocks.cend(),
[](MetadataBlock *block) {
return dynamic_cast<Picture *>(block) != nullptr;
})) {
keys.append("PICTURE");
}
}
return keys;
}
List<VariantMap> FLAC::File::complexProperties(const String &key) const
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
List<VariantMap> props;
for(const auto &block : std::as_const(d->blocks)) {
if(auto picture = dynamic_cast<Picture *>(block)) {
VariantMap property;
property.insert("data", picture->data());
property.insert("mimeType", picture->mimeType());
property.insert("description", picture->description());
property.insert("pictureType",
FLAC::Picture::typeToString(picture->type()));
property.insert("width", picture->width());
property.insert("height", picture->height());
property.insert("numColors", picture->numColors());
property.insert("colorDepth", picture->colorDepth());
props.append(property);
}
}
return props;
}
return TagLib::File::complexProperties(key);
}
bool FLAC::File::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removePictures();
for(const auto &property : value) {
auto picture = new FLAC::Picture;
picture->setData(property.value("data").value<ByteVector>());
picture->setMimeType(property.value("mimeType").value<String>());
picture->setDescription(property.value("description").value<String>());
picture->setType(FLAC::Picture::typeFromString(
property.value("pictureType").value<String>()));
picture->setWidth(property.value("width").value<int>());
picture->setHeight(property.value("height").value<int>());
picture->setNumColors(property.value("numColors").value<int>());
picture->setColorDepth(property.value("colorDepth").value<int>());
addPicture(picture);
}
}
else {
return TagLib::File::setComplexProperties(key, value);
}
return true;
}
FLAC::Properties *FLAC::File::audioProperties() const
{
return d->properties.get();
return d->properties;
}
bool FLAC::File::save()
{
if(readOnly()) {
@@ -236,253 +157,141 @@ bool FLAC::File::save()
}
// Create new vorbis comments
if(!hasXiphComment())
Tag::duplicate(&d->tag, xiphComment(true), false);
Tag::duplicate(&d->tag, xiphComment(true), false);
d->xiphCommentData = xiphComment()->render(false);
// Replace metadata blocks
MetadataBlock *commentBlock =
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
if((*it)->code() == MetadataBlock::VorbisComment) {
// Remove the old Vorbis Comment block
delete *it;
it = d->blocks.erase(it);
bool foundVorbisCommentBlock = false;
List<MetadataBlock *> newBlocks;
for(uint i = 0; i < d->blocks.size(); i++) {
MetadataBlock *block = d->blocks[i];
if(block->code() == MetadataBlock::VorbisComment) {
// Set the new Vorbis Comment block
delete block;
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
foundVorbisCommentBlock = true;
}
if(block->code() == MetadataBlock::Padding) {
delete block;
continue;
}
if(commentBlock && (*it)->code() == MetadataBlock::Picture) {
// Set the new Vorbis Comment block before the first picture block
d->blocks.insert(it, commentBlock);
commentBlock = nullptr;
}
++it;
newBlocks.append(block);
}
if(commentBlock)
d->blocks.append(commentBlock);
if(!foundVorbisCommentBlock) {
newBlocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
foundVorbisCommentBlock = true;
}
d->blocks = newBlocks;
// Render data for the metadata blocks
ByteVector data;
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
ByteVector blockData = (*it)->render();
for(uint i = 0; i < newBlocks.size(); i++) {
FLAC::MetadataBlock *block = newBlocks[i];
ByteVector blockData = block->render();
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
if(blockHeader[0] != 0) {
debug("FLAC::File::save() -- Removing too large block.");
delete *it;
it = d->blocks.erase(it);
continue;
}
blockHeader[0] = static_cast<char>((*it)->code());
blockHeader[0] = block->code();
data.append(blockHeader);
data.append(blockData);
++it;
}
// Compute the amount of padding, and append that to data.
// Adjust the padding block(s)
offset_t originalLength = d->streamStart - d->flacStart;
offset_t paddingLength = originalLength - data.size() - 4;
if(paddingLength <= 0) {
long originalLength = d->streamStart - d->flacStart;
int paddingLength = originalLength - data.size() - 4;
if (paddingLength < 0) {
paddingLength = MinPaddingLength;
}
else {
// Padding won't increase beyond 1% of the file size or 1MB.
offset_t threshold = length() / 100;
threshold = std::max<offset_t>(threshold, MinPaddingLength);
threshold = std::min<offset_t>(threshold, MaxPaddingLegnth);
if(paddingLength > threshold)
paddingLength = MinPaddingLength;
}
ByteVector paddingHeader = ByteVector::fromUInt(static_cast<unsigned int>(paddingLength));
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
data.append(paddingHeader);
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
ByteVector padding = ByteVector::fromUInt(paddingLength);
padding.resize(paddingLength + 4);
padding[0] = FLAC::MetadataBlock::Padding | LastBlockFlag;
data.append(padding);
// Write the data to the file
insert(data, d->flacStart, originalLength);
d->streamStart += static_cast<long>(data.size()) - originalLength;
if(d->ID3v1Location >= 0)
d->ID3v1Location += static_cast<long>(data.size()) - originalLength;
d->hasXiphComment = true;
// Update ID3 tags
if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
// ID3v2 tag is not empty. Update the old one or create a new one.
if(d->ID3v2Location < 0)
d->ID3v2Location = 0;
data = ID3v2Tag()->render();
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
d->flacStart += static_cast<long>(data.size()) - d->ID3v2OriginalSize;
d->streamStart += static_cast<long>(data.size()) - d->ID3v2OriginalSize;
if(d->ID3v1Location >= 0)
d->ID3v1Location += static_cast<long>(data.size()) - d->ID3v2OriginalSize;
d->ID3v2OriginalSize = data.size();
}
else {
// ID3v2 tag is empty. Remove the old one.
if(d->ID3v2Location >= 0) {
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
d->flacStart -= d->ID3v2OriginalSize;
d->streamStart -= d->ID3v2OriginalSize;
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->ID3v2OriginalSize;
d->ID3v2Location = -1;
d->ID3v2OriginalSize = 0;
if(ID3v2Tag()) {
if(d->hasID3v2) {
if(d->ID3v2Location < d->flacStart)
debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
"start of the FLAC bytestream? Not writing the ID3v2 tag.");
else
insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
}
else
insert(ID3v2Tag()->render(), 0, 0);
}
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
// ID3v1 tag is not empty. Update the old one or create a new one.
if(d->ID3v1Location >= 0) {
seek(d->ID3v1Location);
}
else {
seek(0, End);
d->ID3v1Location = tell();
}
if(ID3v1Tag()) {
seek(-128, End);
writeBlock(ID3v1Tag()->render());
}
else {
// ID3v1 tag is empty. Remove the old one.
if(d->ID3v1Location >= 0) {
truncate(d->ID3v1Location);
d->ID3v1Location = -1;
}
}
return true;
}
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create,
d->ID3v2FrameFactory);
if(!create || d->tag[ID3v2Index])
return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]);
d->tag.set(ID3v2Index, new ID3v2::Tag);
return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]);
}
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
{
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, create);
return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
}
Ogg::XiphComment *FLAC::File::xiphComment(bool create)
{
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create);
return d->tag.access<Ogg::XiphComment>(XiphIndex, create);
}
List<FLAC::Picture *> FLAC::File::pictureList()
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
{
List<Picture *> pictures;
for(const auto &block : std::as_const(d->blocks)) {
if(auto picture = dynamic_cast<Picture *>(block)) {
pictures.append(picture);
}
}
return pictures;
d->ID3v2FrameFactory = factory;
}
void FLAC::File::addPicture(Picture *picture)
{
d->blocks.append(picture);
}
void FLAC::File::removePicture(Picture *picture, bool del)
{
auto it = d->blocks.find(picture);
if(it != d->blocks.end())
d->blocks.erase(it);
if(del)
delete picture;
}
void FLAC::File::removePictures()
{
for(auto it = d->blocks.begin(); it != d->blocks.end(); ) {
if(dynamic_cast<Picture *>(*it)) {
delete *it;
it = d->blocks.erase(it);
}
else {
++it;
}
}
}
void FLAC::File::strip(int tags)
{
if(tags & ID3v1)
d->tag.set(FlacID3v1Index, nullptr);
if(tags & ID3v2)
d->tag.set(FlacID3v2Index, nullptr);
if(tags & XiphComment) {
xiphComment()->removeAllFields();
xiphComment()->removeAllPictures();
}
}
bool FLAC::File::hasXiphComment() const
{
return !d->xiphCommentData.isEmpty();
}
bool FLAC::File::hasID3v1Tag() const
{
return d->ID3v1Location >= 0;
}
bool FLAC::File::hasID3v2Tag() const
{
return d->ID3v2Location >= 0;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::File::read(bool readProperties)
void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
// Look for an ID3v2 tag
d->ID3v2Location = Utils::findID3v2(this);
d->ID3v2Location = findID3v2();
if(d->ID3v2Location >= 0) {
d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
if(ID3v2Tag()->header()->tagSize() <= 0)
d->tag.set(ID3v2Index, 0);
else
d->hasID3v2 = true;
}
// Look for an ID3v1 tag
d->ID3v1Location = Utils::findID3v1(this);
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0)
d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
// Look for FLAC metadata, including vorbis comments
@@ -491,26 +300,28 @@ void FLAC::File::read(bool readProperties)
if(!isValid())
return;
if(!d->xiphCommentData.isEmpty())
d->tag.set(FlacXiphIndex, new Ogg::XiphComment(d->xiphCommentData));
if(d->hasXiphComment)
d->tag.set(XiphIndex, new Ogg::XiphComment(xiphCommentData()));
else
d->tag.set(FlacXiphIndex, new Ogg::XiphComment());
d->tag.set(XiphIndex, new Ogg::XiphComment);
if(readProperties) {
if(readProperties)
d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
}
// First block should be the stream_info metadata
ByteVector FLAC::File::streamInfoData()
{
return isValid() ? d->streamInfoData : ByteVector();
}
const ByteVector infoData = d->blocks.front()->render();
ByteVector FLAC::File::xiphCommentData() const
{
return (isValid() && d->hasXiphComment) ? d->xiphCommentData : ByteVector();
}
offset_t streamLength;
if(d->ID3v1Location >= 0)
streamLength = d->ID3v1Location - d->streamStart;
else
streamLength = length() - d->streamStart;
d->properties = std::make_unique<Properties>(infoData, streamLength);
}
long FLAC::File::streamLength()
{
return d->streamLength;
}
void FLAC::File::scan()
@@ -523,9 +334,9 @@ void FLAC::File::scan()
if(!isValid())
return;
offset_t nextBlockOffset;
long nextBlockOffset;
if(d->ID3v2Location >= 0)
if(d->hasID3v2)
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
else
nextBlockOffset = find("fLaC");
@@ -539,96 +350,161 @@ void FLAC::File::scan()
nextBlockOffset += 4;
d->flacStart = nextBlockOffset;
while(true) {
seek(nextBlockOffset);
seek(nextBlockOffset);
const ByteVector header = readBlock(4);
if(header.size() != 4) {
debug("FLAC::File::scan() -- Failed to read a block header");
ByteVector header = readBlock(4);
// Header format (from spec):
// <1> Last-metadata-block flag
// <7> BLOCK_TYPE
// 0 : STREAMINFO
// 1 : PADDING
// ..
// 4 : VORBIS_COMMENT
// ..
// <24> Length of metadata to follow
char blockType = header[0] & 0x7f;
bool isLastBlock = (header[0] & 0x80) != 0;
uint length = header.mid(1, 3).toUInt();
// First block should be the stream_info metadata
if(blockType != MetadataBlock::StreamInfo) {
debug("FLAC::File::scan() -- invalid FLAC stream");
setValid(false);
return;
}
d->streamInfoData = readBlock(length);
d->blocks.append(new UnknownMetadataBlock(blockType, d->streamInfoData));
nextBlockOffset += length + 4;
// Search through the remaining metadata
while(!isLastBlock) {
header = readBlock(4);
blockType = header[0] & 0x7f;
isLastBlock = (header[0] & 0x80) != 0;
length = header.mid(1, 3).toUInt();
ByteVector data = readBlock(length);
if(data.size() != length) {
debug("FLAC::File::scan() -- FLAC stream corrupted");
setValid(false);
return;
}
// Header format (from spec):
// <1> Last-metadata-block flag
// <7> BLOCK_TYPE
// 0 : STREAMINFO
// 1 : PADDING
// ..
// 4 : VORBIS_COMMENT
// ..
// 6 : PICTURE
// ..
// <24> Length of metadata to follow
const char blockType = header[0] & ~LastBlockFlag;
const bool isLastBlock = (header[0] & LastBlockFlag) != 0;
const unsigned int blockLength = header.toUInt(1U, 3U);
// First block should be the stream_info metadata
if(d->blocks.isEmpty() && blockType != MetadataBlock::StreamInfo) {
debug("FLAC::File::scan() -- First block should be the stream_info metadata");
setValid(false);
return;
}
if(blockLength == 0
&& blockType != MetadataBlock::Padding && blockType != MetadataBlock::SeekTable)
{
debug("FLAC::File::scan() -- Zero-sized metadata block found");
setValid(false);
return;
}
const ByteVector data = readBlock(blockLength);
if(data.size() != blockLength) {
debug("FLAC::File::scan() -- Failed to read a metadata block");
setValid(false);
return;
}
MetadataBlock *block = nullptr;
MetadataBlock *block = 0;
// Found the vorbis-comment
if(blockType == MetadataBlock::VorbisComment) {
if(d->xiphCommentData.isEmpty()) {
if(!d->hasXiphComment) {
d->xiphCommentData = data;
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
d->hasXiphComment = true;
}
else {
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, using the first one");
}
}
else if(blockType == MetadataBlock::Picture) {
auto picture = new FLAC::Picture();
FLAC::Picture *picture = new FLAC::Picture();
if(picture->parse(data)) {
block = picture;
}
else {
debug("FLAC::File::scan() -- invalid picture found, discarding");
debug("FLAC::File::scan() -- invalid picture found, discarting");
delete picture;
}
}
else if(blockType == MetadataBlock::Padding) {
// Skip all padding blocks.
}
else {
if(!block) {
block = new UnknownMetadataBlock(blockType, data);
}
if(block)
if(block->code() != MetadataBlock::Padding) {
d->blocks.append(block);
}
else {
delete block;
}
nextBlockOffset += blockLength + 4;
nextBlockOffset += length + 4;
if(isLastBlock)
break;
if(nextBlockOffset >= File::length()) {
debug("FLAC::File::scan() -- FLAC stream corrupted");
setValid(false);
return;
}
seek(nextBlockOffset);
}
// End of metadata, now comes the datastream
d->streamStart = nextBlockOffset;
d->streamLength = File::length() - d->streamStart;
if(d->hasID3v1)
d->streamLength -= 128;
d->scanned = true;
}
long FLAC::File::findID3v1()
{
if(!isValid())
return -1;
seek(-128, End);
long p = tell();
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
return -1;
}
long FLAC::File::findID3v2()
{
if(!isValid())
return -1;
seek(0);
if(readBlock(3) == ID3v2::Header::fileIdentifier())
return 0;
return -1;
}
List<FLAC::Picture *> FLAC::File::pictureList()
{
List<Picture *> pictures;
for(uint i = 0; i < d->blocks.size(); i++) {
Picture *picture = dynamic_cast<Picture *>(d->blocks[i]);
if(picture) {
pictures.append(picture);
}
}
return pictures;
}
void FLAC::File::addPicture(Picture *picture)
{
d->blocks.append(picture);
}
void FLAC::File::removePictures()
{
List<MetadataBlock *> newBlocks;
for(uint i = 0; i < d->blocks.size(); i++) {
Picture *picture = dynamic_cast<Picture *>(d->blocks[i]);
if(picture) {
delete picture;
}
else {
newBlocks.append(d->blocks[i]);
}
}
d->blocks = newBlocks;
}

View File

@@ -26,16 +26,17 @@
#ifndef TAGLIB_FLACFILE_H
#define TAGLIB_FLACFILE_H
#include "taglib_export.h"
#include "tfile.h"
#include "tlist.h"
#include "taglib_export.h"
#include "tag.h"
#include "flacpicture.h"
#include "flacproperties.h"
namespace TagLib {
class Tag;
namespace ID3v2 { class FrameFactory; class Tag; }
namespace ID3v1 { class Tag; }
namespace Ogg { class XiphComment; }
@@ -43,9 +44,9 @@ namespace TagLib {
//! An implementation of FLAC metadata
/*!
* This is an implementation of FLAC metadata for non-Ogg FLAC files. At some
* This is implementation of FLAC metadata for non-Ogg FLAC files. At some
* point when Ogg / FLAC is more common there will be a similar implementation
* under the Ogg hierarchy.
* under the Ogg hiearchy.
*
* This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream
* properties from the file.
@@ -66,82 +67,38 @@ namespace TagLib {
{
public:
/*!
* This set of flags is used for various operations and is suitable for
* being OR-ed together.
*/
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches Vorbis comments.
XiphComment = 0x0001,
//! Matches ID3v1 tags.
ID3v1 = 0x0002,
//! Matches ID3v2 tags.
ID3v2 = 0x0004,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Constructs a FLAC file from \a file. If \a readProperties is \c true the
* file's audio properties will also be read.
* Contructs a FLAC 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, \a propertiesStyle is ignored.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
* \deprecated This constructor will be dropped in favor of the one below
* in a future version.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs a FLAC file from \a file. If \a readProperties is \c true the
* file's audio properties will also be read.
* Contructs a FLAC 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.
*
* If this file contains an ID3v2 tag, the frames will be created using
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated Use the constructor above.
*/
TAGLIB_DEPRECATED
// BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs a FLAC file from \a stream. If \a readProperties is \c true the
* file's audio properties will also be read.
* Contructs a FLAC 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 TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Constructs a FLAC file from \a stream. If \a readProperties is \c 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 an ID3v2 tag, the frames will be created using
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated Use the constructor above.
*/
TAGLIB_DEPRECATED
// BIC: merge with the above constructor
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -149,10 +106,7 @@ namespace TagLib {
/*!
* Destroys this instance of the File.
*/
~File() override;
File(const File &) = delete;
File &operator=(const File &) = delete;
virtual ~File();
/*!
* Returns the Tag for this file. This will be a union of XiphComment,
@@ -162,127 +116,92 @@ namespace TagLib {
* \see ID3v1Tag()
* \see XiphComment()
*/
TagLib::Tag *tag() const override;
/*!
* 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 override;
void removeUnsupportedProperties(const StringList &) override;
/*!
* 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 &) override;
/*!
* Returns ["PICTURE"] if any picture is stored in METADATA_BLOCK_PICTURE.
*/
StringList complexPropertyKeys() const override;
/*!
* Get the pictures stored in METADATA_BLOCK_PICTURE as complex properties
* for \a key "PICTURE".
*/
List<VariantMap> complexProperties(const String &key) const override;
/*!
* Set the complex properties \a value as pictures in METADATA_BLOCK_PICTURE
* for \a key "PICTURE".
*/
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
virtual TagLib::Tag *tag() const;
/*!
* Returns the FLAC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Properties *audioProperties() const override;
virtual Properties *audioProperties() const;
/*!
* Save the file. This will primarily save the XiphComment, but
* will also keep any old ID3-tags up to date. If the file
* has no XiphComment, one will be constructed from the ID3-tags.
*
* This returns \c true if the save was successful.
* This returns true if the save was successful.
*/
bool save() override;
virtual bool save();
/*!
* Returns a pointer to the ID3v2 tag of the file.
*
* If \a create is \c false (the default) this returns a null pointer
* if there is no valid ID3v2 tag. If \a create is \c true it will create
* 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.
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist.
*
* \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 hasID3v2Tag()
*/
ID3v2::Tag *ID3v2Tag(bool create = false);
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is \c false (the default) this returns a null pointer
* if there is no valid APE tag. If \a create is \c true it will create
* 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 ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist.
*
* \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 hasID3v1Tag()
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
/*!
* Returns a pointer to the XiphComment for the file.
*
* If \a create is \c false (the default) this returns a null pointer
* if there is no valid XiphComment. If \a create is \c true it will create
* 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.
* If \a create is false (the default) this will return a null pointer
* if there is no valid XiphComment. If \a create is true it will create
* a XiphComment if one does not exist.
*
* \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()
*/
Ogg::XiphComment *xiphComment(bool create = false);
/*!
* Set the ID3v2::FrameFactory to something other than the default. This
* can be used to specify the way that ID3v2 frames will be interpreted
* when
*
* \see ID3v2FrameFactory
*/
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
/*!
* Returns the block of data used by FLAC::Properties for parsing the
* stream properties.
*
* \deprecated This method will not be public in a future release.
*/
ByteVector streamInfoData(); // BIC: remove
/*!
* Returns the length of the audio-stream, used by FLAC::Properties for
* calculating the bitrate.
*
* \deprecated This method will not be public in a future release.
*/
long streamLength(); // BIC: remove
/*!
* Returns a list of pictures attached to the FLAC file.
*/
List<Picture *> pictureList();
/*!
* Removes an attached picture. If \a del is \c true the picture's memory
* will be freed; if it is \c false, it must be deleted by the user.
*/
void removePicture(Picture *picture, bool del = true);
/*!
* Remove all attached images.
*/
@@ -296,60 +215,21 @@ namespace TagLib {
*/
void addPicture(Picture *picture);
/*!
* This will remove the tags that match the OR-ed together TagTypes from
* the file. By default it removes all tags.
*
* \warning This will also invalidate pointers to the tags as their memory
* will be freed.
*
* \note In order to make the removal permanent save() still needs to be
* called.
*
* \note This won't remove the Vorbis comment block completely. The
* vendor ID will be preserved.
*/
void strip(int tags = AllTags);
/*!
* 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;
/*!
* Returns whether or not the given \a stream can be opened as a FLAC
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
void read(bool readProperties);
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void scan();
long findID3v2();
long findID3v1();
ByteVector xiphCommentData() const;
long findPaddingBreak(long nextPageOffset, long targetOffset, bool *isLast);
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
FilePrivate *d;
};
} // namespace FLAC
} // namespace TagLib
}
}
#endif

View File

@@ -23,14 +23,29 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include "flacmetadatablock.h"
using namespace TagLib;
class FLAC::MetadataBlock::MetadataBlockPrivate
class FLAC::MetadataBlock::MetadataBlockPrivate
{
public:
MetadataBlockPrivate() {}
};
FLAC::MetadataBlock::MetadataBlock() = default;
FLAC::MetadataBlock::MetadataBlock()
{
d = 0;
}
FLAC::MetadataBlock::~MetadataBlock()
{
}
FLAC::MetadataBlock::~MetadataBlock() = default;

View File

@@ -26,21 +26,20 @@
#ifndef TAGLIB_FLACMETADATABLOCK_H
#define TAGLIB_FLACMETADATABLOCK_H
#include "tlist.h"
#include "tbytevector.h"
#include "taglib_export.h"
namespace TagLib {
namespace FLAC {
//! FLAC metadata block
class TAGLIB_EXPORT MetadataBlock
{
public:
MetadataBlock();
virtual ~MetadataBlock();
MetadataBlock(const MetadataBlock &item) = delete;
MetadataBlock &operator=(const MetadataBlock &item) = delete;
enum BlockType {
StreamInfo = 0,
Padding,
@@ -62,10 +61,15 @@ namespace TagLib {
virtual ByteVector render() const = 0;
private:
MetadataBlock(const MetadataBlock &item);
MetadataBlock &operator=(const MetadataBlock &item);
class MetadataBlockPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<MetadataBlockPrivate> d;
MetadataBlockPrivate *d;
};
} // namespace FLAC
} // namespace TagLib
}
}
#endif

View File

@@ -23,37 +23,52 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "flacpicture.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "tdebug.h"
#include <taglib.h>
#include <tdebug.h>
#include "flacpicture.h"
using namespace TagLib;
class FLAC::Picture::PicturePrivate
class FLAC::Picture::PicturePrivate
{
public:
Type type { FLAC::Picture::Other };
PicturePrivate() :
type(FLAC::Picture::Other),
width(0),
height(0),
colorDepth(0),
numColors(0)
{}
Type type;
String mimeType;
String description;
int width { 0 };
int height { 0 };
int colorDepth { 0 };
int numColors { 0 };
int width;
int height;
int colorDepth;
int numColors;
ByteVector data;
};
FLAC::Picture::Picture() :
d(std::make_unique<PicturePrivate>())
FLAC::Picture::Picture()
{
d = new PicturePrivate;
}
FLAC::Picture::Picture(const ByteVector &data) :
d(std::make_unique<PicturePrivate>())
FLAC::Picture::Picture(const ByteVector &data)
{
d = new PicturePrivate;
parse(data);
}
FLAC::Picture::~Picture() = default;
FLAC::Picture::~Picture()
{
delete d;
}
int FLAC::Picture::code() const
{
@@ -67,10 +82,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
return false;
}
unsigned int pos = 0;
d->type = static_cast<FLAC::Picture::Type>(data.toUInt(pos));
int pos = 0;
d->type = FLAC::Picture::Type(data.mid(pos, 4).toUInt());
pos += 4;
unsigned int mimeTypeLength = data.toUInt(pos);
uint mimeTypeLength = data.mid(pos, 4).toUInt();
pos += 4;
if(pos + mimeTypeLength + 24 > data.size()) {
debug("Invalid picture block.");
@@ -78,7 +93,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
pos += mimeTypeLength;
unsigned int descriptionLength = data.toUInt(pos);
uint descriptionLength = data.mid(pos, 4).toUInt();
pos += 4;
if(pos + descriptionLength + 20 > data.size()) {
debug("Invalid picture block.");
@@ -86,15 +101,15 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->description = String(data.mid(pos, descriptionLength), String::UTF8);
pos += descriptionLength;
d->width = data.toUInt(pos);
d->width = data.mid(pos, 4).toUInt();
pos += 4;
d->height = data.toUInt(pos);
d->height = data.mid(pos, 4).toUInt();
pos += 4;
d->colorDepth = data.toUInt(pos);
d->colorDepth = data.mid(pos, 4).toUInt();
pos += 4;
d->numColors = data.toUInt(pos);
d->numColors = data.mid(pos, 4).toUInt();
pos += 4;
unsigned int dataLength = data.toUInt(pos);
uint dataLength = data.mid(pos, 4).toUInt();
pos += 4;
if(pos + dataLength > data.size()) {
debug("Invalid picture block.");
@@ -102,7 +117,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->data = data.mid(pos, dataLength);
return true;
return true;
}
ByteVector FLAC::Picture::render() const
@@ -203,3 +218,4 @@ void FLAC::Picture::setData(const ByteVector &data)
{
d->data = data;
}

View File

@@ -29,28 +29,68 @@
#include "tlist.h"
#include "tstring.h"
#include "tbytevector.h"
#include "tpicturetype.h"
#include "taglib_export.h"
#include "flacmetadatablock.h"
namespace TagLib {
namespace FLAC {
//! FLAC picture
class TAGLIB_EXPORT Picture : public MetadataBlock
{
public:
/*
/*!
* This describes the function or content of the picture.
*/
DECLARE_PICTURE_TYPE_ENUM(Type)
enum Type {
//! A type not enumerated below
Other = 0x00,
//! 32x32 PNG image that should be used as the file icon
FileIcon = 0x01,
//! File icon of a different size or format
OtherFileIcon = 0x02,
//! Front cover image of the album
FrontCover = 0x03,
//! Back cover image of the album
BackCover = 0x04,
//! Inside leaflet page of the album
LeafletPage = 0x05,
//! Image from the album itself
Media = 0x06,
//! Picture of the lead artist or soloist
LeadArtist = 0x07,
//! Picture of the artist or performer
Artist = 0x08,
//! Picture of the conductor
Conductor = 0x09,
//! Picture of the band or orchestra
Band = 0x0A,
//! Picture of the composer
Composer = 0x0B,
//! Picture of the lyricist or text writer
Lyricist = 0x0C,
//! Picture of the recording location or studio
RecordingLocation = 0x0D,
//! Picture of the artists during recording
DuringRecording = 0x0E,
//! Picture of the artists during performance
DuringPerformance = 0x0F,
//! Picture from a movie or video related to the track
MovieScreenCapture = 0x10,
//! Picture of a large, coloured fish
ColouredFish = 0x11,
//! Illustration related to the track
Illustration = 0x12,
//! Logo of the band or performer
BandLogo = 0x13,
//! Logo of the publisher (record company)
PublisherLogo = 0x14
};
Picture();
Picture(const ByteVector &data);
~Picture() override;
Picture(const Picture &item) = delete;
Picture &operator=(const Picture &item) = delete;
~Picture();
/*!
* Returns the type of the image.
@@ -72,7 +112,7 @@ namespace TagLib {
* Sets the mime type of the image. This should in most cases be
* "image/png" or "image/jpeg".
*/
void setMimeType(const String &mimeType);
void setMimeType(const String &m);
/*!
* Returns a text description of the image.
@@ -81,10 +121,10 @@ namespace TagLib {
String description() const;
/*!
* Sets a textual description of the image to \a description.
* Sets a textual description of the image to \a desc.
*/
void setDescription(const String &description);
void setDescription(const String &desc);
/*!
* Returns the width of the image.
@@ -94,7 +134,7 @@ namespace TagLib {
/*!
* Sets the width of the image.
*/
void setWidth(int width);
void setWidth(int w);
/*!
* Returns the height of the image.
@@ -104,7 +144,7 @@ namespace TagLib {
/*!
* Sets the height of the image.
*/
void setHeight(int height);
void setHeight(int h);
/*!
* Returns the color depth (in bits-per-pixel) of the image.
@@ -114,7 +154,7 @@ namespace TagLib {
/*!
* Sets the color depth (in bits-per-pixel) of the image.
*/
void setColorDepth(int colorDepth);
void setColorDepth(int depth);
/*!
* Returns the number of colors used on the image..
@@ -139,25 +179,30 @@ namespace TagLib {
/*!
* Returns the FLAC metadata block type.
*/
int code() const override;
int code() const;
/*!
* Render the content to the FLAC picture block format.
*/
ByteVector render() const override;
ByteVector render() const;
/*!
* Parse the picture data in the FLAC picture block format.
*/
bool parse(const ByteVector &data);
bool parse(const ByteVector &rawData);
private:
Picture(const Picture &item);
Picture &operator=(const Picture &item);
class PicturePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PicturePrivate> d;
PicturePrivate *d;
};
using PictureList = List<Picture>;
} // namespace FLAC
} // namespace TagLib
typedef List<Picture> PictureList;
}
}
#endif

View File

@@ -23,22 +23,35 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "flacproperties.h"
#include <tstring.h>
#include <tdebug.h>
#include "tstring.h"
#include "tdebug.h"
#include "flacproperties.h"
#include "flacfile.h"
using namespace TagLib;
class FLAC::Properties::PropertiesPrivate
{
public:
int length { 0 };
int bitrate { 0 };
int sampleRate { 0 };
int bitsPerSample { 0 };
int channels { 0 };
unsigned long long sampleFrames { 0 };
PropertiesPrivate(ByteVector d, long st, ReadStyle s) :
data(d),
streamLength(st),
style(s),
length(0),
bitrate(0),
sampleRate(0),
sampleWidth(0),
channels(0) {}
ByteVector data;
long streamLength;
ReadStyle style;
int length;
int bitrate;
int sampleRate;
int sampleWidth;
int channels;
ByteVector signature;
};
@@ -46,16 +59,24 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::Properties::Properties(const ByteVector &data, offset_t streamLength, ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : AudioProperties(style)
{
read(data, streamLength);
d = new PropertiesPrivate(data, streamLength, style);
read();
}
FLAC::Properties::~Properties() = default;
FLAC::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(file->streamInfoData(), file->streamLength(), style);
read();
}
int FLAC::Properties::lengthInMilliseconds() const
FLAC::Properties::~Properties()
{
delete d;
}
int FLAC::Properties::length() const
{
return d->length;
}
@@ -70,9 +91,9 @@ int FLAC::Properties::sampleRate() const
return d->sampleRate;
}
int FLAC::Properties::bitsPerSample() const
int FLAC::Properties::sampleWidth() const
{
return d->bitsPerSample;
return d->sampleWidth;
}
int FLAC::Properties::channels() const
@@ -80,11 +101,6 @@ int FLAC::Properties::channels() const
return d->channels;
}
unsigned long long FLAC::Properties::sampleFrames() const
{
return d->sampleFrames;
}
ByteVector FLAC::Properties::signature() const
{
return d->signature;
@@ -94,14 +110,14 @@ ByteVector FLAC::Properties::signature() const
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
void FLAC::Properties::read()
{
if(data.size() < 18) {
if(d->data.size() < 18) {
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
return;
}
unsigned int pos = 0;
int pos = 0;
// Minimum block size (in samples)
pos += 2;
@@ -115,28 +131,28 @@ void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
// Maximum frame size (in bytes)
pos += 3;
const unsigned int flags = data.toUInt(pos, true);
pos += 4;
d->sampleRate = flags >> 12;
d->channels = ((flags >> 9) & 7) + 1;
d->bitsPerSample = ((flags >> 4) & 31) + 1;
uint flags = d->data.mid(pos, 4).toUInt(true);
d->sampleRate = flags >> 12;
d->channels = ((flags >> 9) & 7) + 1;
d->sampleWidth = ((flags >> 4) & 31) + 1;
// The last 4 bits are the most significant 4 bits for the 36 bit
// stream length in samples. (Audio files measured in days)
const unsigned long long hi = flags & 0xf;
const unsigned long long lo = data.toUInt(pos, true);
uint highLength =d->sampleRate > 0 ? (((flags & 0xf) << 28) / d->sampleRate) << 4 : 0;
pos += 4;
d->sampleFrames = (hi << 32) | lo;
d->length = d->sampleRate > 0 ?
(d->data.mid(pos, 4).toUInt(true)) / d->sampleRate + highLength : 0;
pos += 4;
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
}
// Uncompressed bitrate:
if(data.size() >= pos + 16)
d->signature = data.mid(pos, 16);
//d->bitrate = ((d->sampleRate * d->channels) / 1000) * d->sampleWidth;
// Real bitrate:
d->bitrate = d->length > 0 ? ((d->streamLength * 8UL) / d->length) / 1000 : 0;
d->signature = d->data.mid(pos, 32);
}

View File

@@ -26,7 +26,6 @@
#ifndef TAGLIB_FLACPROPERTIES_H
#define TAGLIB_FLACPROPERTIES_H
#include "tbytevector.h"
#include "taglib_export.h"
#include "audioproperties.h"
@@ -34,10 +33,12 @@ namespace TagLib {
namespace FLAC {
class File;
//! An implementation of audio property reading for FLAC
/*!
* This reads the data from a FLAC stream found in the AudioProperties
* This reads the data from an FLAC stream found in the AudioProperties
* API.
*/
@@ -48,63 +49,50 @@ namespace TagLib {
* Create an instance of FLAC::Properties with the data read from the
* ByteVector \a data.
*/
Properties(const ByteVector &data, offset_t streamLength, ReadStyle style = Average);
// BIC: switch to const reference
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
/*!
* Create an instance of FLAC::Properties with the data read from the
* FLAC::File \a file.
*/
// BIC: remove
Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this FLAC::Properties instance.
*/
~Properties() override;
virtual ~Properties();
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
* Returns the sample width as read from the FLAC identification
* header.
*/
int lengthInMilliseconds() const override;
/*!
* Returns the average bit rate of the file in kb/s.
*/
int bitrate() const override;
/*!
* Returns the sample rate in Hz.
*/
int sampleRate() const override;
/*!
* Returns the number of audio channels.
*/
int channels() const override;
/*!
* Returns the number of bits per audio sample as read from the FLAC
* identification header.
*/
int bitsPerSample() const;
/*!
* Return the number of sample frames.
*/
unsigned long long sampleFrames() const;
int sampleWidth() const;
/*!
* Returns the MD5 signature of the uncompressed audio stream as read
* from the stream info header.
* from the stream info header header.
*/
ByteVector signature() const;
private:
void read(const ByteVector &data, offset_t streamLength);
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
PropertiesPrivate *d;
};
} // namespace FLAC
} // namespace TagLib
}
}
#endif

View File

@@ -23,25 +23,38 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include <tstring.h>
#include "flacunknownmetadatablock.h"
using namespace TagLib;
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
{
public:
int code { 0 };
UnknownMetadataBlockPrivate() : code(0) {}
int code;
ByteVector data;
};
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) :
d(std::make_unique<UnknownMetadataBlockPrivate>())
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data)
{
d = new UnknownMetadataBlockPrivate;
d->code = code;
//debug(String(data.toHex()));
d->data = data;
}
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock() = default;
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock()
{
delete d;
}
int FLAC::UnknownMetadataBlock::code() const
{
@@ -67,3 +80,4 @@ ByteVector FLAC::UnknownMetadataBlock::render() const
{
return d->data;
}

View File

@@ -32,21 +32,19 @@
#include "flacmetadatablock.h"
namespace TagLib {
namespace FLAC {
//! Unknown FLAC metadata block
class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock
{
public:
UnknownMetadataBlock(int code, const ByteVector &data);
~UnknownMetadataBlock() override;
UnknownMetadataBlock(const UnknownMetadataBlock &item) = delete;
UnknownMetadataBlock &operator=(const UnknownMetadataBlock &item) = delete;
UnknownMetadataBlock(int blockType, const ByteVector &data);
~UnknownMetadataBlock();
/*!
* Returns the FLAC metadata block type.
*/
int code() const override;
int code() const;
/*!
* Sets the FLAC metadata block type.
@@ -66,13 +64,18 @@ namespace TagLib {
/*!
* Render the content of the block.
*/
ByteVector render() const override;
ByteVector render() const;
private:
UnknownMetadataBlock(const MetadataBlock &item);
UnknownMetadataBlock &operator=(const MetadataBlock &item);
class UnknownMetadataBlockPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<UnknownMetadataBlockPrivate> d;
UnknownMetadataBlockPrivate *d;
};
} // namespace FLAC
} // namespace TagLib
}
}
#endif

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,17 +15,12 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#include "itfile.h"
#include "tstringlist.h"
#include "itfile.h"
#include "tdebug.h"
#include "modfileprivate.h"
@@ -36,7 +31,7 @@ class IT::File::FilePrivate
{
public:
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: properties(propertiesStyle)
: tag(), properties(propertiesStyle)
{
}
@@ -47,22 +42,23 @@ public:
IT::File::File(FileName file, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
Mod::FileBase(file),
d(std::make_unique<FilePrivate>(propertiesStyle))
d(new FilePrivate(propertiesStyle))
{
if(isOpen())
read(readProperties);
read(readProperties);
}
IT::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
Mod::FileBase(stream),
d(std::make_unique<FilePrivate>(propertiesStyle))
d(new FilePrivate(propertiesStyle))
{
if(isOpen())
read(readProperties);
read(readProperties);
}
IT::File::~File() = default;
IT::File::~File()
{
delete d;
}
Mod::Tag *IT::File::tag() const
{
@@ -87,9 +83,9 @@ bool IT::File::save()
seek(2, Current);
unsigned short length = 0;
unsigned short instrumentCount = 0;
unsigned short sampleCount = 0;
ushort length = 0;
ushort instrumentCount = 0;
ushort sampleCount = 0;
if(!readU16L(length) || !readU16L(instrumentCount) || !readU16L(sampleCount))
return false;
@@ -98,9 +94,9 @@ bool IT::File::save()
// write comment as instrument and sample names:
StringList lines = d->tag.comment().split("\n");
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + (static_cast<long>(i) << 2));
unsigned long instrumentOffset = 0;
for(ushort i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
ulong instrumentOffset = 0;
if(!readU32L(instrumentOffset))
return false;
@@ -109,28 +105,28 @@ bool IT::File::save()
if(i < lines.size())
writeString(lines[i], 25);
else
writeString(String(), 25);
writeString(String::null, 25);
writeByte(0);
}
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
unsigned long sampleOffset = 0;
for(ushort i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
ulong sampleOffset = 0;
if(!readU32L(sampleOffset))
return false;
seek(sampleOffset + 20);
if(static_cast<unsigned int>(i + instrumentCount) < lines.size())
if((i + instrumentCount) < lines.size())
writeString(lines[i + instrumentCount], 25);
else
writeString(String(), 25);
writeString(String::null, 25);
writeByte(0);
}
// write rest as message:
StringList messageLines;
for(unsigned int i = instrumentCount + sampleCount; i < lines.size(); ++ i)
for(uint i = instrumentCount + sampleCount; i < lines.size(); ++ i)
messageLines.append(lines[i]);
ByteVector message = messageLines.toString("\r").data(String::Latin1);
@@ -138,17 +134,17 @@ bool IT::File::save()
// terminating NUL but it does not hurt to add one:
if(message.size() > 7999)
message.resize(7999);
message.append(static_cast<char>(0));
message.append((char)0);
unsigned short special = 0;
unsigned short messageLength = 0;
unsigned long messageOffset = 0;
ushort special = 0;
ushort messageLength = 0;
ulong messageOffset = 0;
seek(46);
if(!readU16L(special))
return false;
auto fileSize = static_cast<unsigned long>(File::length());
long fileSize = this->length();
if(special & Properties::MessageAttached) {
seek(54);
if(!readU16L(messageLength) || !readU32L(messageOffset))
@@ -164,10 +160,10 @@ bool IT::File::save()
writeU16L(special | 0x1);
}
if(messageOffset + messageLength >= fileSize) {
if((messageOffset + messageLength) >= fileSize) {
// append new message
seek(54);
writeU16L(static_cast<unsigned short>(message.size()));
writeU16L(message.size());
writeU32L(messageOffset);
seek(messageOffset);
writeBlock(message);
@@ -199,7 +195,7 @@ void IT::File::read(bool)
READ_U16L_AS(length);
READ_U16L_AS(instrumentCount);
READ_U16L_AS(sampleCount);
d->properties.setInstrumentCount(instrumentCount);
d->properties.setSampleCount(sampleCount);
READ_U16L(d->properties.setPatternCount);
@@ -225,7 +221,7 @@ void IT::File::read(bool)
seek(messageOffset);
ByteVector messageBytes = readBlock(messageLength);
READ_ASSERT(messageBytes.size() == messageLength);
int index = messageBytes.find(static_cast<char>(0));
int index = messageBytes.find((char) 0);
if(index > -1)
messageBytes.resize(index, 0);
messageBytes.replace('\r', '\n');
@@ -243,15 +239,14 @@ void IT::File::read(bool)
// I don't count disabled and muted channels.
// But this always gives 64 channels for all my files anyway.
// Strangely VLC does report other values. I wonder how VLC
// gets its values.
if(static_cast<unsigned char>(pannings[i]) < 128 && volumes[i] > 0)
++channels;
// gets it's values.
if(pannings[i] < 128 && volumes[i] > 0) ++ channels;
}
d->properties.setChannels(channels);
// real length might be shorter because of skips and terminator
unsigned short realLength = 0;
for(unsigned short i = 0; i < length; ++ i) {
ushort realLength = 0;
for(ushort i = 0; i < length; ++ i) {
READ_BYTE_AS(order);
if(order == 255) break;
if(order != 254) ++ realLength;
@@ -263,10 +258,10 @@ void IT::File::read(bool)
// in the instrument/sample names and more characters
// afterwards. The spec does not mention such a case.
// Currently I just discard anything after a nil, but
// e.g. VLC seems to interpret a nil as a space. I
// e.g. VLC seems to interprete a nil as a space. I
// don't know what is the proper behaviour.
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + (static_cast<long>(i) << 2));
for(ushort i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
READ_U32L_AS(instrumentOffset);
seek(instrumentOffset);
@@ -280,11 +275,11 @@ void IT::File::read(bool)
READ_STRING_AS(instrumentName, 26);
comment.append(instrumentName);
}
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
for(ushort i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
READ_U32L_AS(sampleOffset);
seek(sampleOffset);
ByteVector sampleMagic = readBlock(4);
@@ -310,11 +305,11 @@ void IT::File::read(bool)
READ_BYTE_AS(vibratoRate);
READ_BYTE_AS(vibratoType);
*/
comment.append(sampleName);
}
if(!message.isEmpty())
if(message.size() > 0)
comment.append(message);
d->tag.setComment(comment.toString("\n"));
d->tag.setTrackerName("Impulse Tracker");

View File

@@ -23,73 +23,48 @@
#define TAGLIB_ITFILE_H
#include "tfile.h"
#include "taglib_export.h"
#include "audioproperties.h"
#include "taglib_export.h"
#include "modfilebase.h"
#include "modtag.h"
#include "itproperties.h"
namespace TagLib {
//! An implementation of Impulse Tracker metadata
/*!
* This is an implementation of Impulse Tracker metadata.
*/
namespace IT {
//! An implementation of TagLib::File with IT specific methods
/*!
* This implements and provides an interface for IT 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 IT files.
*/
class TAGLIB_EXPORT File : public Mod::FileBase {
public:
/*!
* Constructs an Impulse Tracker file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
* Contructs a Impulse Tracker file from \a file. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Constructs an Impulse Tracker 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.
* Contructs a Impulse Tracker file from \a stream. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
File(IOStream *stram, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Destroys this instance of the File.
*/
~File() override;
virtual ~File();
File(const File &) = delete;
File &operator=(const File &) = delete;
Mod::Tag *tag() const override;
Mod::Tag *tag() const;
/*!
* Returns the IT::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
IT::Properties *audioProperties() const override;
IT::Properties *audioProperties() const;
/*!
* Save the file.
@@ -97,16 +72,18 @@ namespace TagLib {
*
* \note Saving Impulse Tracker tags is not supported.
*/
bool save() override;
bool save();
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties);
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
FilePrivate *d;
};
} // namespace IT
} // namespace TagLib
}
}
#endif

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,12 +15,8 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#include "itproperties.h"
@@ -31,37 +27,74 @@ using namespace IT;
class IT::Properties::PropertiesPrivate
{
public:
int channels { 0 };
unsigned short lengthInPatterns { 0 };
unsigned short instrumentCount { 0 };
unsigned short sampleCount { 0 };
unsigned short patternCount { 0 };
unsigned short version { 0 };
unsigned short compatibleVersion { 0 };
unsigned short flags { 0 };
unsigned short special { 0 };
unsigned char globalVolume { 0 };
unsigned char mixVolume { 0 };
unsigned char tempo { 0 };
unsigned char bpmSpeed { 0 };
unsigned char panningSeparation { 0 };
unsigned char pitchWheelDepth { 0 };
PropertiesPrivate() :
channels(0),
lengthInPatterns(0),
instrumentCount(0),
sampleCount(0),
patternCount(0),
version(0),
compatibleVersion(0),
flags(0),
special(0),
globalVolume(0),
mixVolume(0),
tempo(0),
bpmSpeed(0),
panningSeparation(0),
pitchWheelDepth(0)
{
}
int channels;
ushort lengthInPatterns;
ushort instrumentCount;
ushort sampleCount;
ushort patternCount;
ushort version;
ushort compatibleVersion;
ushort flags;
ushort special;
uchar globalVolume;
uchar mixVolume;
uchar tempo;
uchar bpmSpeed;
uchar panningSeparation;
uchar pitchWheelDepth;
};
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle),
d(std::make_unique<PropertiesPrivate>())
d(new PropertiesPrivate)
{
}
IT::Properties::~Properties() = default;
IT::Properties::~Properties()
{
delete d;
}
int IT::Properties::length() const
{
return 0;
}
int IT::Properties::bitrate() const
{
return 0;
}
int IT::Properties::sampleRate() const
{
return 0;
}
int IT::Properties::channels() const
{
return d->channels;
}
unsigned short IT::Properties::lengthInPatterns() const
ushort IT::Properties::lengthInPatterns() const
{
return d->lengthInPatterns;
}
@@ -71,67 +104,67 @@ bool IT::Properties::stereo() const
return d->flags & Stereo;
}
unsigned short IT::Properties::instrumentCount() const
ushort IT::Properties::instrumentCount() const
{
return d->instrumentCount;
}
unsigned short IT::Properties::sampleCount() const
ushort IT::Properties::sampleCount() const
{
return d->sampleCount;
}
unsigned short IT::Properties::patternCount() const
ushort IT::Properties::patternCount() const
{
return d->patternCount;
}
unsigned short IT::Properties::version() const
ushort IT::Properties::version() const
{
return d->version;
}
unsigned short IT::Properties::compatibleVersion() const
ushort IT::Properties::compatibleVersion() const
{
return d->compatibleVersion;
}
unsigned short IT::Properties::flags() const
ushort IT::Properties::flags() const
{
return d->flags;
}
unsigned short IT::Properties::special() const
ushort IT::Properties::special() const
{
return d->special;
}
unsigned char IT::Properties::globalVolume() const
uchar IT::Properties::globalVolume() const
{
return d->globalVolume;
}
unsigned char IT::Properties::mixVolume() const
uchar IT::Properties::mixVolume() const
{
return d->mixVolume;
}
unsigned char IT::Properties::tempo() const
uchar IT::Properties::tempo() const
{
return d->tempo;
}
unsigned char IT::Properties::bpmSpeed() const
uchar IT::Properties::bpmSpeed() const
{
return d->bpmSpeed;
}
unsigned char IT::Properties::panningSeparation() const
uchar IT::Properties::panningSeparation() const
{
return d->panningSeparation;
}
unsigned char IT::Properties::pitchWheelDepth() const
uchar IT::Properties::pitchWheelDepth() const
{
return d->pitchWheelDepth;
}
@@ -141,72 +174,72 @@ void IT::Properties::setChannels(int channels)
d->channels = channels;
}
void IT::Properties::setLengthInPatterns(unsigned short lengthInPatterns)
void IT::Properties::setLengthInPatterns(ushort lengthInPatterns)
{
d->lengthInPatterns = lengthInPatterns;
}
void IT::Properties::setInstrumentCount(unsigned short instrumentCount)
void IT::Properties::setInstrumentCount(ushort instrumentCount)
{
d->instrumentCount = instrumentCount;
}
void IT::Properties::setSampleCount(unsigned short sampleCount)
void IT::Properties::setSampleCount(ushort sampleCount)
{
d->sampleCount = sampleCount;
}
void IT::Properties::setPatternCount(unsigned short patternCount)
void IT::Properties::setPatternCount(ushort patternCount)
{
d->patternCount = patternCount;
}
void IT::Properties::setFlags(unsigned short flags)
void IT::Properties::setFlags(ushort flags)
{
d->flags = flags;
}
void IT::Properties::setSpecial(unsigned short special)
void IT::Properties::setSpecial(ushort special)
{
d->special = special;
}
void IT::Properties::setCompatibleVersion(unsigned short compatibleVersion)
void IT::Properties::setCompatibleVersion(ushort compatibleVersion)
{
d->compatibleVersion = compatibleVersion;
}
void IT::Properties::setVersion(unsigned short version)
void IT::Properties::setVersion(ushort version)
{
d->version = version;
}
void IT::Properties::setGlobalVolume(unsigned char globalVolume)
void IT::Properties::setGlobalVolume(uchar globalVolume)
{
d->globalVolume = globalVolume;
}
void IT::Properties::setMixVolume(unsigned char mixVolume)
void IT::Properties::setMixVolume(uchar mixVolume)
{
d->mixVolume = mixVolume;
}
void IT::Properties::setTempo(unsigned char tempo)
void IT::Properties::setTempo(uchar tempo)
{
d->tempo = tempo;
}
void IT::Properties::setBpmSpeed(unsigned char bpmSpeed)
void IT::Properties::setBpmSpeed(uchar bpmSpeed)
{
d->bpmSpeed = bpmSpeed;
}
void IT::Properties::setPanningSeparation(unsigned char panningSeparation)
void IT::Properties::setPanningSeparation(uchar panningSeparation)
{
d->panningSeparation = panningSeparation;
}
void IT::Properties::setPitchWheelDepth(unsigned char pitchWheelDepth)
void IT::Properties::setPitchWheelDepth(uchar pitchWheelDepth)
{
d->pitchWheelDepth = pitchWheelDepth;
}

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,23 +15,20 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#ifndef TAGLIB_ITPROPERTIES_H
#define TAGLIB_ITPROPERTIES_H
#include "taglib.h"
#include "audioproperties.h"
namespace TagLib {
namespace IT {
//! An implementation of audio property reading for IT
class TAGLIB_EXPORT Properties : public AudioProperties {
friend class File;
public:
/*! Flag bits. */
enum {
@@ -52,51 +49,55 @@ namespace TagLib {
};
Properties(AudioProperties::ReadStyle propertiesStyle);
~Properties() override;
virtual ~Properties();
int length() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
int channels() const override;
unsigned short lengthInPatterns() const;
bool stereo() const;
unsigned short instrumentCount() const;
unsigned short sampleCount() const;
unsigned short patternCount() const;
unsigned short version() const;
unsigned short compatibleVersion() const;
unsigned short flags() const;
unsigned short special() const;
unsigned char globalVolume() const;
unsigned char mixVolume() const;
unsigned char tempo() const;
unsigned char bpmSpeed() const;
unsigned char panningSeparation() const;
unsigned char pitchWheelDepth() const;
ushort lengthInPatterns() const;
bool stereo() const;
ushort instrumentCount() const;
ushort sampleCount() const;
ushort patternCount() const;
ushort version() const;
ushort compatibleVersion() const;
ushort flags() const;
ushort special() const;
uchar globalVolume() const;
uchar mixVolume() const;
uchar tempo() const;
uchar bpmSpeed() const;
uchar panningSeparation() const;
uchar pitchWheelDepth() const;
protected:
void setChannels(int channels);
void setLengthInPatterns(unsigned short lengthInPatterns);
void setInstrumentCount(unsigned short instrumentCount);
void setSampleCount(unsigned short sampleCount);
void setPatternCount(unsigned short patternCount);
void setVersion(unsigned short version);
void setCompatibleVersion(unsigned short compatibleVersion);
void setFlags(unsigned short flags);
void setSpecial(unsigned short special);
void setGlobalVolume(unsigned char globalVolume);
void setMixVolume(unsigned char mixVolume);
void setTempo(unsigned char tempo);
void setBpmSpeed(unsigned char bpmSpeed);
void setPanningSeparation(unsigned char panningSeparation);
void setPitchWheelDepth(unsigned char pitchWheelDepth);
void setLengthInPatterns(ushort lengthInPatterns);
void setInstrumentCount(ushort instrumentCount);
void setSampleCount (ushort sampleCount);
void setPatternCount(ushort patternCount);
void setVersion (ushort version);
void setCompatibleVersion(ushort compatibleVersion);
void setFlags (ushort flags);
void setSpecial (ushort special);
void setGlobalVolume(uchar globalVolume);
void setMixVolume (uchar mixVolume);
void setTempo (uchar tempo);
void setBpmSpeed (uchar bpmSpeed);
void setPanningSeparation(uchar panningSeparation);
void setPitchWheelDepth (uchar pitchWheelDepth);
private:
Properties(const Properties&);
Properties &operator=(const Properties&);
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
PropertiesPrivate *d;
};
} // namespace IT
} // namespace TagLib
}
}
#endif

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,19 +15,13 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#include "modfile.h"
#include "tstringlist.h"
#include "tdebug.h"
#include "tpropertymap.h"
#include "modfileprivate.h"
using namespace TagLib;
@@ -48,22 +42,23 @@ public:
Mod::File::File(FileName file, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
Mod::FileBase(file),
d(std::make_unique<FilePrivate>(propertiesStyle))
d(new FilePrivate(propertiesStyle))
{
if(isOpen())
read(readProperties);
read(readProperties);
}
Mod::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
Mod::FileBase(stream),
d(std::make_unique<FilePrivate>(propertiesStyle))
d(new FilePrivate(propertiesStyle))
{
if(isOpen())
read(readProperties);
read(readProperties);
}
Mod::File::~File() = default;
Mod::File::~File()
{
delete d;
}
Mod::Tag *Mod::File::tag() const
{
@@ -75,16 +70,6 @@ Mod::Properties *Mod::File::audioProperties() const
return &d->properties;
}
PropertyMap Mod::File::properties() const
{
return d->tag.properties();
}
PropertyMap Mod::File::setProperties(const PropertyMap &properties)
{
return d->tag.setProperties(properties);
}
bool Mod::File::save()
{
if(readOnly()) {
@@ -94,14 +79,14 @@ bool Mod::File::save()
seek(0);
writeString(d->tag.title(), 20);
StringList lines = d->tag.comment().split("\n");
unsigned int n = std::min(lines.size(), d->properties.instrumentCount());
for(unsigned int i = 0; i < n; ++ i) {
uint n = std::min(lines.size(), d->properties.instrumentCount());
for(uint i = 0; i < n; ++ i) {
writeString(lines[i], 22);
seek(8, Current);
}
for(unsigned int i = n; i < d->properties.instrumentCount(); ++ i) {
writeString(String(), 22);
for(uint i = n; i < d->properties.instrumentCount(); ++ i) {
writeString(String::null, 22);
seek(8, Current);
}
return true;
@@ -116,8 +101,8 @@ void Mod::File::read(bool)
ByteVector modId = readBlock(4);
READ_ASSERT(modId.size() == 4);
int channels = 4;
unsigned int instruments = 31;
int channels = 4;
uint instruments = 31;
if(modId == "M.K." || modId == "M!K!" || modId == "M&K!" || modId == "N.T.") {
d->tag.setTrackerName("ProTracker");
channels = 4;
@@ -160,30 +145,25 @@ void Mod::File::read(bool)
seek(0);
READ_STRING(d->tag.setTitle, 20);
offset_t pos = 20;
StringList comment;
for(unsigned int i = 0; i < instruments; ++ i) {
for(uint i = 0; i < instruments; ++ i) {
READ_STRING_AS(instrumentName, 22);
// skip unused data
pos += 22 + 2 + 1 + 1 + 2 + 2;
seek(pos);
// value in words, * 2 (<< 1) for bytes:
READ_U16B_AS(sampleLength);
// // value in words, * 2 (<< 1) for bytes:
// READ_U16B_AS(sampleLength);
READ_BYTE_AS(fineTuneByte);
int fineTune = fineTuneByte & 0xF;
// > 7 means negative value
if(fineTune > 7) fineTune -= 16;
// READ_BYTE_AS(fineTuneByte);
// int fineTune = fineTuneByte & 0xF;
// // > 7 means negative value
// if(fineTune > 7) fineTune -= 16;
READ_BYTE_AS(volume);
if(volume > 64) volume = 64;
// volume in decibels: 20 * log10(volume / 64)
// READ_BYTE_AS(volume);
// if(volume > 64) volume = 64;
// // volume in decibels: 20 * log10(volume / 64)
// // value in words, * 2 (<< 1) for bytes:
// READ_U16B_AS(repeatStart);
// // value in words, * 2 (<< 1) for bytes:
// READ_U16B_AS(repeatLength);
// value in words, * 2 (<< 1) for bytes:
READ_U16B_AS(repeatStart);
// value in words, * 2 (<< 1) for bytes:
READ_U16B_AS(repatLength);
comment.append(instrumentName);
}

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,113 +15,75 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#ifndef TAGLIB_MODFILE_H
#define TAGLIB_MODFILE_H
#include "tfile.h"
#include "taglib_export.h"
#include "audioproperties.h"
#include "taglib_export.h"
#include "modfilebase.h"
#include "modtag.h"
#include "modproperties.h"
namespace TagLib {
//! An implementation of Protracker metadata
/*!
* This is an implementation of Protracker metadata.
*/
namespace Mod {
//! An implementation of TagLib::File with Mod specific methods
class TAGLIB_EXPORT File : public TagLib::Mod::FileBase {
public:
/*!
* Contructs a Protracker file from \a file. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* This implements and provides an interface for Mod 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 Mod files.
*/
/*!
* Contructs a Protracker file from \a stream. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
class TAGLIB_EXPORT File : public TagLib::Mod::FileBase
{
public:
/*!
* Constructs a Protracker file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Constructs a Protracker 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,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
Mod::Tag *tag() const;
/*!
* Destroys this instance of the File.
*/
~File() override;
/*!
* Returns the Mod::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Mod::Properties *audioProperties() const;
File(const File &) = delete;
File &operator=(const File &) = delete;
/*!
* Save the file.
* This is the same as calling save(AllTags);
*
* \note Saving Protracker tags is not supported.
*/
bool save();
Mod::Tag *tag() const override;
private:
File(const File &);
File &operator=(const File &);
/*!
* Implements the unified property interface -- export function.
* Forwards to Mod::Tag::properties().
*/
PropertyMap properties() const override;
void read(bool readProperties);
/*!
* Implements the unified property interface -- import function.
* Forwards to Mod::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns the Mod::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Mod::Properties *audioProperties() const override;
/*!
* Save the file.
* This is the same as calling save(AllTags);
*
* \note Saving Protracker tags is not supported.
*/
bool save() override;
private:
void read(bool readProperties);
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
class FilePrivate;
FilePrivate *d;
};
} // namespace Mod
} // namespace TagLib
}
}
#endif

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,25 +15,16 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#include "tdebug.h"
#include "modfilebase.h"
using namespace TagLib;
using namespace Mod;
class Mod::FileBase::FileBasePrivate
{
};
Mod::FileBase::~FileBase() = default;
Mod::FileBase::FileBase(FileName file) : TagLib::File(file)
{
}
@@ -42,55 +33,55 @@ Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
{
}
void Mod::FileBase::writeString(const String &s, unsigned long size, char padding)
void Mod::FileBase::writeString(const String &s, ulong size, char padding)
{
ByteVector data(s.data(String::Latin1));
data.resize(static_cast<unsigned int>(size), padding);
data.resize(size, padding);
writeBlock(data);
}
bool Mod::FileBase::readString(String &s, unsigned long size)
bool Mod::FileBase::readString(String &s, ulong size)
{
ByteVector data(readBlock(size));
if(data.size() < size) return false;
int index = data.find(static_cast<char>(0));
int index = data.find((char) 0);
if(index > -1)
{
data.resize(index);
}
data.replace('\xff', ' ');
data.replace((char) 0xff, ' ');
s = data;
return true;
}
void Mod::FileBase::writeByte(unsigned char byte)
void Mod::FileBase::writeByte(uchar byte)
{
ByteVector data(1, byte);
writeBlock(data);
}
void Mod::FileBase::writeU16L(unsigned short number)
void Mod::FileBase::writeU16L(ushort number)
{
writeBlock(ByteVector::fromShort(number, false));
}
void Mod::FileBase::writeU32L(unsigned long number)
void Mod::FileBase::writeU32L(ulong number)
{
writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(number), false));
writeBlock(ByteVector::fromUInt(number, false));
}
void Mod::FileBase::writeU16B(unsigned short number)
void Mod::FileBase::writeU16B(ushort number)
{
writeBlock(ByteVector::fromShort(number, true));
}
void Mod::FileBase::writeU32B(unsigned long number)
void Mod::FileBase::writeU32B(ulong number)
{
writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(number), true));
writeBlock(ByteVector::fromUInt(number, true));
}
bool Mod::FileBase::readByte(unsigned char &byte)
bool Mod::FileBase::readByte(uchar &byte)
{
ByteVector data(readBlock(1));
if(data.size() < 1) return false;
@@ -98,7 +89,7 @@ bool Mod::FileBase::readByte(unsigned char &byte)
return true;
}
bool Mod::FileBase::readU16L(unsigned short &number)
bool Mod::FileBase::readU16L(ushort &number)
{
ByteVector data(readBlock(2));
if(data.size() < 2) return false;
@@ -106,14 +97,14 @@ bool Mod::FileBase::readU16L(unsigned short &number)
return true;
}
bool Mod::FileBase::readU32L(unsigned long &number) {
bool Mod::FileBase::readU32L(ulong &number) {
ByteVector data(readBlock(4));
if(data.size() < 4) return false;
number = data.toUInt(false);
return true;
}
bool Mod::FileBase::readU16B(unsigned short &number)
bool Mod::FileBase::readU16B(ushort &number)
{
ByteVector data(readBlock(2));
if(data.size() < 2) return false;
@@ -121,7 +112,7 @@ bool Mod::FileBase::readU16B(unsigned short &number)
return true;
}
bool Mod::FileBase::readU32B(unsigned long &number) {
bool Mod::FileBase::readU32B(ulong &number) {
ByteVector data(readBlock(4));
if(data.size() < 4) return false;
number = data.toUInt(true);

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,12 +15,8 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#ifndef TAGLIB_MODFILEBASE_H
@@ -28,42 +24,35 @@
#include "taglib.h"
#include "tfile.h"
#include "tstring.h"
#include "tlist.h"
#include "taglib_export.h"
#include <algorithm>
namespace TagLib {
namespace Mod {
//! Base class for module files
class TAGLIB_EXPORT FileBase : public TagLib::File
{
public:
~FileBase() override;
FileBase(const FileBase &) = delete;
FileBase& operator=(const FileBase &) = delete;
protected:
FileBase(FileName file);
FileBase(IOStream *stream);
void writeString(const String &s, unsigned long size, char padding = 0);
void writeByte(unsigned char byte);
void writeU16L(unsigned short number);
void writeU32L(unsigned long number);
void writeU16B(unsigned short number);
void writeU32B(unsigned long number);
void writeString(const String &s, ulong size, char padding = 0);
void writeByte(uchar byte);
void writeU16L(ushort number);
void writeU32L(ulong number);
void writeU16B(ushort number);
void writeU32B(ulong number);
bool readString(String &s, unsigned long size);
bool readByte(unsigned char &byte);
bool readU16L(unsigned short &number);
bool readU32L(unsigned long &number);
bool readU16B(unsigned short &number);
bool readU32B(unsigned long &number);
private:
class FileBasePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FileBasePrivate> d;
bool readString(String &s, ulong size);
bool readByte(uchar &byte);
bool readU16L(ushort &number);
bool readU32L(ulong &number);
bool readU16B(ushort &number);
bool readU32B(ulong &number);
};
} // namespace Mod
} // namespace TagLib
}
}
#endif

View File

@@ -23,45 +23,45 @@
#define TAGLIB_MODFILEPRIVATE_H
// some helper-macros only used internally by (s3m|it|xm)file.cpp
#define READ_ASSERT(cond) \
do { \
if(!(cond)) { \
setValid(false); \
return; \
} \
} while(0)
#define READ_ASSERT(cond) \
if(!(cond)) \
{ \
setValid(false); \
return; \
}
#define READ(setter, type, read) \
do { \
type number; \
READ_ASSERT(read(number)); \
setter(number); \
} while(0)
#define READ(setter,type,read) \
{ \
type number; \
READ_ASSERT(read(number)); \
setter(number); \
}
#define READ_BYTE(setter) READ(setter, unsigned char, readByte)
#define READ_U16L(setter) READ(setter, unsigned short, readU16L)
#define READ_U32L(setter) READ(setter, unsigned long, readU32L)
#define READ_U16B(setter) READ(setter, unsigned short, readU16B)
#define READ_U32B(setter) READ(setter, unsigned long, readU32B)
#define READ_BYTE(setter) READ(setter,uchar,readByte)
#define READ_U16L(setter) READ(setter,ushort,readU16L)
#define READ_U32L(setter) READ(setter,ulong,readU32L)
#define READ_U16B(setter) READ(setter,ushort,readU16B)
#define READ_U32B(setter) READ(setter,ulong,readU32B)
#define READ_STRING(setter, size) \
do { \
String s; \
READ_ASSERT(readString(s, size)); \
setter(s); \
} while(0)
#define READ_STRING(setter,size) \
{ \
String s; \
READ_ASSERT(readString(s, size)); \
setter(s); \
}
#define READ_AS(type, name, read) \
type name = 0; \
READ_ASSERT(read(name))
#define READ_AS(type,name,read) \
type name = 0; \
READ_ASSERT(read(name));
#define READ_BYTE_AS(name) READ_AS(unsigned char, name, readByte)
#define READ_U16L_AS(name) READ_AS(unsigned short, name, readU16L)
#define READ_U32L_AS(name) READ_AS(unsigned long, name, readU32L)
#define READ_U16B_AS(name) READ_AS(unsigned short, name, readU16B)
#define READ_U32B_AS(name) READ_AS(unsigned long, name, readU32B)
#define READ_BYTE_AS(name) READ_AS(uchar,name,readByte)
#define READ_U16L_AS(name) READ_AS(ushort,name,readU16L)
#define READ_U32L_AS(name) READ_AS(ulong,name,readU32L)
#define READ_U16B_AS(name) READ_AS(ushort,name,readU16B)
#define READ_U32B_AS(name) READ_AS(ulong,name,readU32B)
#define READ_STRING_AS(name,size) \
String name; \
READ_ASSERT(readString(name, size));
#define READ_STRING_AS(name, size) \
String name; \
READ_ASSERT(readString(name, size))
#endif

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,12 +15,8 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#include "modproperties.h"
@@ -31,18 +27,33 @@ using namespace Mod;
class Mod::Properties::PropertiesPrivate
{
public:
int channels { 0 };
unsigned int instrumentCount { 0 };
unsigned char lengthInPatterns { 0 };
PropertiesPrivate() :
channels(0),
instrumentCount(0),
lengthInPatterns(0)
{
}
int channels;
uint instrumentCount;
uchar lengthInPatterns;
};
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle),
d(std::make_unique<PropertiesPrivate>())
d(new PropertiesPrivate)
{
}
Mod::Properties::~Properties() = default;
Mod::Properties::~Properties()
{
delete d;
}
int Mod::Properties::length() const
{
return 0;
}
int Mod::Properties::bitrate() const
{
@@ -59,12 +70,12 @@ int Mod::Properties::channels() const
return d->channels;
}
unsigned int Mod::Properties::instrumentCount() const
uint Mod::Properties::instrumentCount() const
{
return d->instrumentCount;
}
unsigned char Mod::Properties::lengthInPatterns() const
uchar Mod::Properties::lengthInPatterns() const
{
return d->lengthInPatterns;
}
@@ -74,12 +85,12 @@ void Mod::Properties::setChannels(int channels)
d->channels = channels;
}
void Mod::Properties::setInstrumentCount(unsigned int instrumentCount)
void Mod::Properties::setInstrumentCount(uint instrumentCount)
{
d->instrumentCount = instrumentCount;
}
void Mod::Properties::setLengthInPatterns(unsigned char lengthInPatterns)
void Mod::Properties::setLengthInPatterns(uchar lengthInPatterns)
{
d->lengthInPatterns = lengthInPatterns;
}

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,48 +15,46 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#ifndef TAGLIB_MODPROPERTIES_H
#define TAGLIB_MODPROPERTIES_H
#include "taglib.h"
#include "audioproperties.h"
namespace TagLib {
namespace Mod {
//! An implementation of audio property reading for Mod
class TAGLIB_EXPORT Properties : public AudioProperties
{
class TAGLIB_EXPORT Properties : public AudioProperties {
friend class File;
public:
Properties(AudioProperties::ReadStyle propertiesStyle);
~Properties() override;
virtual ~Properties();
int length() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
int bitrate() const override;
int sampleRate() const override;
int channels() const override;
unsigned int instrumentCount() const;
unsigned char lengthInPatterns() const;
uint instrumentCount() const;
uchar lengthInPatterns() const;
protected:
void setChannels(int channels);
void setInstrumentCount(unsigned int instrumentCount);
void setLengthInPatterns(unsigned char lengthInPatterns);
void setInstrumentCount(uint sampleCount);
void setLengthInPatterns(uchar lengthInPatterns);
private:
Properties(const Properties&);
Properties &operator=(const Properties&);
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
PropertiesPrivate *d;
};
} // namespace Mod
} // namespace TagLib
}
}
#endif

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,38 +15,36 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#include "modtag.h"
#include <utility>
#include "tstringlist.h"
#include "tpropertymap.h"
using namespace TagLib;
using namespace Mod;
class Mod::Tag::TagPrivate
{
public:
TagPrivate()
{
}
String title;
String comment;
String trackerName;
};
Mod::Tag::Tag() :
d(std::make_unique<TagPrivate>())
Mod::Tag::Tag() : TagLib::Tag()
{
d = new TagPrivate;
}
Mod::Tag::~Tag() = default;
Mod::Tag::~Tag()
{
delete d;
}
String Mod::Tag::title() const
{
@@ -55,12 +53,12 @@ String Mod::Tag::title() const
String Mod::Tag::artist() const
{
return String();
return String::null;
}
String Mod::Tag::album() const
{
return String();
return String::null;
}
String Mod::Tag::comment() const
@@ -70,15 +68,15 @@ String Mod::Tag::comment() const
String Mod::Tag::genre() const
{
return String();
return String::null;
}
unsigned int Mod::Tag::year() const
uint Mod::Tag::year() const
{
return 0;
}
unsigned int Mod::Tag::track() const
uint Mod::Tag::track() const
{
return 0;
}
@@ -110,11 +108,11 @@ void Mod::Tag::setGenre(const String &)
{
}
void Mod::Tag::setYear(unsigned int)
void Mod::Tag::setYear(uint)
{
}
void Mod::Tag::setTrack(unsigned int)
void Mod::Tag::setTrack(uint)
{
}
@@ -122,47 +120,3 @@ void Mod::Tag::setTrackerName(const String &trackerName)
{
d->trackerName = trackerName;
}
PropertyMap Mod::Tag::properties() const
{
PropertyMap properties;
properties["TITLE"] = d->title;
properties["COMMENT"] = d->comment;
if(!d->trackerName.isEmpty())
properties["TRACKERNAME"] = d->trackerName;
return properties;
}
PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap props(origProps);
props.removeEmpty();
StringList oneValueSet;
if(props.contains("TITLE")) {
d->title = props["TITLE"].front();
oneValueSet.append("TITLE");
} else
d->title.clear();
if(props.contains("COMMENT")) {
d->comment = props["COMMENT"].front();
oneValueSet.append("COMMENT");
} else
d->comment.clear();
if(props.contains("TRACKERNAME")) {
d->trackerName = props["TRACKERNAME"].front();
oneValueSet.append("TRACKERNAME");
} else
d->trackerName.clear();
// for each tag that has been set above, remove the first entry in the corresponding
// value list. The others will be returned as unsupported by this format.
for(const auto &entry : std::as_const(oneValueSet)) {
if(props[entry].size() == 1)
props.erase(entry);
else
props[entry].erase(props[entry].begin());
}
return props;
}

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,12 +15,8 @@
* *
* 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/ *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301 USA *
***************************************************************************/
#ifndef TAGLIB_MODTAG_H
@@ -30,9 +26,6 @@
namespace TagLib {
namespace Mod {
//! A module file tag implementation
/*!
* Tags for module files (Mod, S3M, IT, XM).
*
@@ -49,80 +42,77 @@ namespace TagLib {
{
public:
Tag();
~Tag() override;
Tag(const Tag &) = delete;
Tag &operator=(const Tag &) = delete;
virtual ~Tag();
/*!
* Returns the track name; if no track name is present in the tag
* an empty string will be returned.
* String::null will be returned.
*/
String title() const override;
String title() const;
/*!
* Not supported by module files. Therefore always returns an empty string.
* Not supported by module files. Therefore always returns String::null.
*/
String artist() const override;
String artist() const;
/*!
* Not supported by module files. Therefore always returns an empty string.
* Not supported by module files. Therefore always returns String::null.
*/
String album() const override;
String album() const;
/*!
* Returns the track comment derived from the instrument/sample/pattern
* names; if no comment is present in the tag an empty string will be
* names; if no comment is present in the tag String::null will be
* returned.
*/
String comment() const override;
String comment() const;
/*!
* Not supported by module files. Therefore always returns an empty string.
* Not supported by module files. Therefore always returns String::null.
*/
String genre() const override;
String genre() const;
/*!
* Not supported by module files. Therefore always returns 0.
*/
unsigned int year() const override;
uint year() const;
/*!
* Not supported by module files. Therefore always returns 0.
*/
unsigned int track() const override;
uint track() const;
/*!
* Returns the name of the tracker used to create/edit the module file.
* Only XM files store this tag to the file as such, for other formats
* (Mod, S3M, IT) this is derived from the file type or the flavour of
* the file type. Therefore only XM files might have an empty
* tracker name.
* (String::null) tracker name.
*/
String trackerName() const;
/*!
* Sets the title to \a title. If \a title is an empty string then this
* Sets the title to \a title. If \a title is String::null then this
* value will be cleared.
*
* The length limits per file type are (1 character = 1 byte):
* The length limits per file type are (1 characetr = 1 byte):
* Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20
* characters.
*/
void setTitle(const String &title) override;
void setTitle(const String &title);
/*!
* Not supported by module files and therefore ignored.
*/
void setArtist(const String &artist) override;
void setArtist(const String &artist);
/*!
* Not supported by module files and therefore ignored.
*/
void setAlbum(const String &album) override;
void setAlbum(const String &album);
/*!
* Sets the comment to \a comment. If \a comment is an empty string then
* Sets the comment to \a comment. If \a comment is String::null then
* this value will be cleared.
*
* Note that module file formats don't actually support a comment tag.
@@ -131,63 +121,50 @@ namespace TagLib {
* module file is fixed to the number of instruments/patterns/samples.
*
* Also note that the instrument/pattern/sample name length is limited
* and thus the line length in comments are limited. Too big comments
* an thus the line length in comments are limited. Too big comments
* will be truncated.
*
* The line length limits per file type are (1 character = 1 byte):
* The line length limits per file type are (1 characetr = 1 byte):
* Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22
* characters.
*/
void setComment(const String &comment) override;
void setComment(const String &comment);
/*!
* Not supported by module files and therefore ignored.
*/
void setGenre(const String &genre) override;
void setGenre(const String &genre);
/*!
* Not supported by module files and therefore ignored.
*/
void setYear(unsigned int year) override;
void setYear(uint year);
/*!
* Not supported by module files and therefore ignored.
*/
void setTrack(unsigned int track) override;
void setTrack(uint track);
/*!
* Sets the tracker name to \a trackerName. If \a trackerName is
* an empty string then this value will be cleared.
*
* String::null then this value will be cleared.
*
* Note that only XM files support this tag. Setting the
* tracker name for other module file formats will be ignored.
*
*
* The length of this tag is limited to 20 characters (1 character
* = 1 byte).
*/
void setTrackerName(const String &trackerName);
/*!
* Implements the unified property interface -- export function.
* Since the module tag is very limited, the exported map is as well.
*/
PropertyMap properties() const override;
/*!
* Implements the unified property interface -- import function.
* Because of the limitations of the module file tag, any tags besides
* COMMENT, TITLE and, if it is an XM file, TRACKERNAME, will be
* returned. Additionally, if the map contains tags with multiple values,
* all but the first will be contained in the returned map of unsupported
* properties.
*/
PropertyMap setProperties(const PropertyMap &) override;
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<TagPrivate> d;
TagPrivate *d;
};
} // namespace Mod
} // namespace TagLib
}
}
#endif

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