mirror of
https://github.com/taglib/taglib.git
synced 2026-06-15 10:49:25 -04:00
Compare commits
106 Commits
experiment
...
v1.12
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8b39449c3 | ||
|
|
bd254654bc | ||
|
|
fbc3f9bbec | ||
|
|
99ad01e12f | ||
|
|
83aa01c6af | ||
|
|
4c14571647 | ||
|
|
f4b476a620 | ||
|
|
f8d78a61f7 | ||
|
|
cf6c68bafc | ||
|
|
f7c24930cd | ||
|
|
310c3bc043 | ||
|
|
e6c03c6de8 | ||
|
|
2c29fbeabb | ||
|
|
4828a3b925 | ||
|
|
794a2a0b2b | ||
|
|
f32d27973f | ||
|
|
f74b166435 | ||
|
|
c28dc78060 | ||
|
|
89fb62cfb1 | ||
|
|
59a2994f4e | ||
|
|
f3bdd416da | ||
|
|
39f86d02e7 | ||
|
|
1a2e1d08ac | ||
|
|
3f3b48353c | ||
|
|
54f5c66b65 | ||
|
|
3749dd7b75 | ||
|
|
d602ae483e | ||
|
|
5374cb1ac4 | ||
|
|
8a461ccedc | ||
|
|
17c2220588 | ||
|
|
271c004b30 | ||
|
|
25d9bd1814 | ||
|
|
b0bb7f8c0f | ||
|
|
4d3ab73d2e | ||
|
|
ae867cbd8c | ||
|
|
563fbaf82a | ||
|
|
e5ad041e42 | ||
|
|
9ca7b0c33a | ||
|
|
a00b3499b4 | ||
|
|
d84e86da9c | ||
|
|
741ed4e4bf | ||
|
|
e4813f4996 | ||
|
|
1332d44ff6 | ||
|
|
3d71ea1ad2 | ||
|
|
02c8995273 | ||
|
|
7e92af6e8b | ||
|
|
7ec1127f3e | ||
|
|
30d839538d | ||
|
|
8f6b6ac055 | ||
|
|
a5f11697f7 | ||
|
|
1721354627 | ||
|
|
ac7e5303a6 | ||
|
|
8ba246cdbe | ||
|
|
6656528f18 | ||
|
|
bad2cea122 | ||
|
|
61d5bcfd5b | ||
|
|
2dd6931eee | ||
|
|
91b00b17b2 | ||
|
|
e116824380 | ||
|
|
2db13ad8cf | ||
|
|
cd767738fc | ||
|
|
43bc11541d | ||
|
|
6806dc4cf2 | ||
|
|
ec9c49b964 | ||
|
|
872cafb0b9 | ||
|
|
47342f6974 | ||
|
|
5763d042a6 | ||
|
|
cd9e6b7502 | ||
|
|
54508df30b | ||
|
|
dcf0331eb1 | ||
|
|
96155f35fa | ||
|
|
ebd3373d6d | ||
|
|
f3ecfa11bb | ||
|
|
7082b9f66b | ||
|
|
de25bc6111 | ||
|
|
074f30e3fa | ||
|
|
f1b40de66b | ||
|
|
0b99cd9bac | ||
|
|
4668cf0f93 | ||
|
|
c05fa78406 | ||
|
|
b8dc105ae3 | ||
|
|
fced0f46e9 | ||
|
|
dc0f667a4c | ||
|
|
085180e9a4 | ||
|
|
ef1312d622 | ||
|
|
86c0428475 | ||
|
|
3c78c4cfc9 | ||
|
|
c8bb6271e5 | ||
|
|
84f7462526 | ||
|
|
2f23892182 | ||
|
|
2918602ad0 | ||
|
|
c146cd7e92 | ||
|
|
b124e621fe | ||
|
|
044fba2921 | ||
|
|
e72a98903f | ||
|
|
79bc9ccf8e | ||
|
|
850a3565a4 | ||
|
|
18d424995f | ||
|
|
d04db70bc6 | ||
|
|
ba7adc2bc2 | ||
|
|
79bb1428c0 | ||
|
|
7470f92a67 | ||
|
|
6455671ece | ||
|
|
660748210f | ||
|
|
1cf176af61 | ||
|
|
2d90d938fe |
@@ -12,6 +12,9 @@ compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
arch:
|
||||
- ppc64le
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
|
||||
2
AUTHORS
2
AUTHORS
@@ -16,6 +16,8 @@ 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!
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
|
||||
|
||||
project(taglib)
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
cmake_policy(SET CMP0022 OLD)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
if(DEFINED ENABLE_STATIC)
|
||||
@@ -94,9 +90,9 @@ endif()
|
||||
# 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 18)
|
||||
set(TAGLIB_SOVERSION_CURRENT 19)
|
||||
set(TAGLIB_SOVERSION_REVISION 0)
|
||||
set(TAGLIB_SOVERSION_AGE 17)
|
||||
set(TAGLIB_SOVERSION_AGE 18)
|
||||
|
||||
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
|
||||
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
|
||||
@@ -104,8 +100,12 @@ math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
|
||||
if(${ZLIB_FOUND})
|
||||
set(ZLIB_LIBRARIES_FLAGS -lz)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config")
|
||||
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 "${BIN_INSTALL_DIR}")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -37,17 +37,6 @@ endif()
|
||||
# Determine which kind of atomic operations your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic_int x(1);
|
||||
++x;
|
||||
--x;
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_ATOMIC)
|
||||
|
||||
if(NOT HAVE_STD_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
@@ -56,8 +45,8 @@ if(NOT HAVE_STD_ATOMIC)
|
||||
}
|
||||
" HAVE_GCC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
int main() {
|
||||
volatile int32_t x;
|
||||
@@ -67,8 +56,8 @@ if(NOT HAVE_STD_ATOMIC)
|
||||
}
|
||||
" HAVE_MAC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <windows.h>
|
||||
int main() {
|
||||
volatile LONG x;
|
||||
@@ -78,8 +67,8 @@ if(NOT HAVE_STD_ATOMIC)
|
||||
}
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
@@ -88,7 +77,6 @@ if(NOT HAVE_STD_ATOMIC)
|
||||
return 0;
|
||||
}
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
80
INSTALL.md
80
INSTALL.md
@@ -38,7 +38,7 @@ For a 10.6 Snow Leopard static library with both 32-bit and 64-bit code, use:
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
|
||||
-DCMAKE_INSTALL_PREFIX="<folder you want to install to>"
|
||||
|
||||
After `make`, and `make install`, add `libtag.` to your XCode project, and add
|
||||
the include folder to the project's User Header Search Paths.
|
||||
@@ -52,7 +52,7 @@ This means you need to adjust things to suit your system, especially paths.
|
||||
Tested with:
|
||||
* Microsoft Visual Studio 2010, 2015, 2017
|
||||
* Microsoft C++ Build Tools 2015, 2017 (standalone packages not requiring Visual Studio)
|
||||
* Gcc by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
|
||||
* GCC by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
|
||||
* MinGW32-4.8.0
|
||||
|
||||
Requirements:
|
||||
@@ -69,15 +69,15 @@ Optional:
|
||||
Useful configuration options used with CMake (GUI and/or command line):
|
||||
Any of the `ZLIB_` variables may be used at the command line, `ZLIB_ROOT` is only
|
||||
available on the command line.
|
||||
|
||||
| option | description |
|
||||
---------------------| ------------|
|
||||
`ZLIB_ROOT=` | Where to find ZLib's root directory. Assumes parent of: `\include` and `\lib.`|
|
||||
`ZLIB_INCLUDE_DIR=` | Where to find ZLib's Include directory.|
|
||||
`ZLIB_LIBRARY=` | Where to find ZLib's Library.
|
||||
`ZLIB_SOURCE=` | Where to find ZLib's Source Code. Alternative to `ZLIB_INCLUDE_DIR` and `ZLIB_LIBRARY`.
|
||||
`CMAKE_INSTALL_PREFIX=` | Where to install Taglib. |
|
||||
`CMAKE_BUILD_TYPE=` | Release, Debug, etc ... (Not available in MSVC) |
|
||||
|
||||
| option | description |
|
||||
| ----------------------- | ----------- |
|
||||
| `ZLIB_ROOT=` | Where to find ZLib's root directory. Assumes parent of: `\include` and `\lib.`|
|
||||
| `ZLIB_INCLUDE_DIR=` | Where to find ZLib's Include directory.|
|
||||
| `ZLIB_LIBRARY=` | Where to find ZLib's Library.
|
||||
| `ZLIB_SOURCE=` | Where to find ZLib's Source Code. Alternative to `ZLIB_INCLUDE_DIR` and `ZLIB_LIBRARY`.
|
||||
| `CMAKE_INSTALL_PREFIX=` | Where to install Taglib. |
|
||||
| `CMAKE_BUILD_TYPE=` | Release, Debug, etc ... (Not available in MSVC) |
|
||||
|
||||
The easiest way is at the command prompt (Visual C++ command prompt for MSVS users – batch file and/or shortcuts are your friends).
|
||||
|
||||
@@ -85,11 +85,11 @@ The easiest way is at the command prompt (Visual C++ command prompt for MSVS use
|
||||
|
||||
Replace "GENERATOR" with your needs.
|
||||
* For MSVS: `Visual Studio XX YYYY`, e.g. `Visual Studio 14 2015`.
|
||||
|
||||
|
||||
**Note**: As Visual Studio 2017 supports CMake, you can skip this step and open the taglib
|
||||
folder in VS instead.
|
||||
* For MinGW: `MinGW Makefiles`
|
||||
|
||||
|
||||
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
|
||||
|
||||
Or use the CMake GUI:
|
||||
@@ -126,13 +126,13 @@ The easiest way is at the command prompt (Visual C++ command prompt for MSVS use
|
||||
C:\GitRoot\taglib> gmake
|
||||
|
||||
OR (Depending on MinGW install)
|
||||
|
||||
|
||||
C:\GitRoot\taglib> mingw32-make
|
||||
|
||||
|
||||
|
||||
3. **Install the project**
|
||||
|
||||
|
||||
(Change `install` to `uninstall` to uninstall the project)
|
||||
* MSVS:
|
||||
|
||||
@@ -140,7 +140,7 @@ The easiest way is at the command prompt (Visual C++ command prompt for MSVS use
|
||||
OR (Depending on MSVC version or personal choice)
|
||||
|
||||
C:\GitRoot\taglib> devenv install.vcxproj
|
||||
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open project.
|
||||
2. Open Solution Explorer.
|
||||
@@ -151,7 +151,7 @@ The easiest way is at the command prompt (Visual C++ command prompt for MSVS use
|
||||
|
||||
C:\GitRoot\taglib> gmake install
|
||||
OR (Depending on MinGW install)
|
||||
|
||||
|
||||
C:\GitRoot\taglib> mingw32-make install
|
||||
|
||||
|
||||
@@ -167,9 +167,53 @@ 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. To build the tests, include
|
||||
the option `-DBUILD_TESTS=on` when running cmake.
|
||||
the option `-DBUILD_TESTS=ON -DBUILD_SHARED_LIBS=OFF` when running cmake.
|
||||
|
||||
The test suite has a custom target in the build system, so you can run
|
||||
the tests using make:
|
||||
|
||||
make check
|
||||
|
||||
Windows MinGW:
|
||||
|
||||
* Get cppunit from https://www.freedesktop.org/wiki/Software/cppunit/
|
||||
* Build it for MinGW:
|
||||
- `./autogen.sh`
|
||||
- `./configure --disable-shared`
|
||||
- `mingw32-make; mingw32-make install DESTDIR=/path/to/install/dir`
|
||||
* Build TagLib with testing enabled:
|
||||
- ```
|
||||
cmake -G "MinGW Makefiles" -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=OFF \
|
||||
-DCPPUNIT_INCLUDE_DIR=/path/to/cppunit/include \
|
||||
-DCPPUNIT_LIBRARIES=/path/to/cppunit/lib/libcppunit.a \
|
||||
-DCPPUNIT_INSTALLED_VERSION=1.15.1
|
||||
```
|
||||
- `mingw32-make`
|
||||
- `mingw32-make check`
|
||||
|
||||
Windows MSVS:
|
||||
|
||||
* Get cppunit from https://www.freedesktop.org/wiki/Software/cppunit/
|
||||
* Build it in Visual Studio:
|
||||
- Open examples/examples2008.sln, convert all, do not overwrite.
|
||||
- Set architecture to x64, build configuration, e.g. Debug DLL.
|
||||
- It may fail, but the needed libraries should be available in src\cppunit\DebugDll.
|
||||
* Build TagLib with testing enabled:
|
||||
- ```
|
||||
cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON
|
||||
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
|
||||
-DCPPUNIT_INCLUDE_DIR=\path\to\cppunit\include
|
||||
-DCPPUNIT_LIBRARIES=\path\to\cppunit\DebugDll\cppunitd_dll.lib
|
||||
-DCPPUNIT_INSTALLED_VERSION=1.15.1
|
||||
```
|
||||
- `msbuild all_build.vcxproj /p:Configuration=Debug`
|
||||
- `path %path%;\path\to\cppunit\DebugDll`
|
||||
- `tests\Debug\test_runner.exe`
|
||||
|
||||
macOS:
|
||||
|
||||
* Install cppunit using a package manager, e.g. `brew install cppunit`
|
||||
* Build TagLib with testing enabled:
|
||||
- `cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=OFF`
|
||||
- `make`
|
||||
- `make check`
|
||||
|
||||
23
NEWS
23
NEWS
@@ -1,24 +1,37 @@
|
||||
============================
|
||||
TagLib 1.12 (Feb 16, 2021)
|
||||
==========================
|
||||
|
||||
* Added support for DSF and DSDIFF files.
|
||||
* 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)
|
||||
@@ -245,7 +258,7 @@ 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.
|
||||
conversion to a number was successful.
|
||||
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
|
||||
compressed frames). (BUG:231075)
|
||||
|
||||
@@ -253,7 +266,7 @@ TagLib 1.6.2 (Apr 9, 2010)
|
||||
==========================
|
||||
|
||||
* Read Vorbis Comments from the first FLAC metadata block, if there are
|
||||
multipe ones. (BUG:211089)
|
||||
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.
|
||||
@@ -288,7 +301,7 @@ TagLib 1.6 (Sep 13, 2009)
|
||||
* 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
|
||||
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determine if
|
||||
TagLib was built with MP4/ASF support.
|
||||
|
||||
1.6 RC1:
|
||||
|
||||
14
README.md
14
README.md
@@ -4,13 +4,13 @@
|
||||
|
||||
### TagLib Audio Metadata Library
|
||||
|
||||
http://taglib.org/
|
||||
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
|
||||
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
|
||||
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE,
|
||||
DSF, DFF, and ASF files.
|
||||
and ASF files.
|
||||
|
||||
TagLib is distributed under the [GNU Lesser General Public License][]
|
||||
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
|
||||
@@ -18,9 +18,9 @@ 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.
|
||||
|
||||
[ID3v2]: http://www.id3.org
|
||||
[Ogg Vorbis]: http://vorbis.com/
|
||||
[ID3v2]: https://id3.org/
|
||||
[Ogg Vorbis]: https://xiph.org/vorbis/
|
||||
[FLAC]: https://xiph.org/flac/
|
||||
[GNU Lesser General Public License]: http://www.gnu.org/licenses/lgpl.html
|
||||
[Mozilla Public License]: http://www.mozilla.org/MPL/MPL-1.1.html
|
||||
[GNU Lesser General Public License]: https://www.gnu.org/licenses/lgpl.html
|
||||
[Mozilla Public License]: https://www.mozilla.org/MPL/MPL-1.1.html
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
|
||||
|
||||
/* Defined if your compiler supports some atomic operations */
|
||||
#cmakedefine HAVE_STD_ATOMIC 1
|
||||
#cmakedefine HAVE_GCC_ATOMIC 1
|
||||
#cmakedefine HAVE_MAC_ATOMIC 1
|
||||
#cmakedefine HAVE_WIN_ATOMIC 1
|
||||
|
||||
@@ -5,6 +5,7 @@ include_directories(
|
||||
${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/
|
||||
)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <id3v2tag.h>
|
||||
#include <id3v2frame.h>
|
||||
#include <id3v2header.h>
|
||||
#include <commentsframe.h>
|
||||
|
||||
#include <id3v1tag.h>
|
||||
|
||||
@@ -65,8 +66,15 @@ int main(int argc, char *argv[])
|
||||
<< endl;
|
||||
|
||||
ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
|
||||
for(; it != id3v2tag->frameList().end(); it++)
|
||||
cout << (*it)->frameID() << " - \"" << (*it)->toString() << "\"" << endl;
|
||||
for(; it != id3v2tag->frameList().end(); it++) {
|
||||
cout << (*it)->frameID();
|
||||
|
||||
if(ID3v2::CommentsFrame *comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
|
||||
if(!comment->description().isEmpty())
|
||||
cout << " [" << comment->description() << "]";
|
||||
|
||||
cout << " - \"" << (*it)->toString() << "\"" << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid id3v2 tag" << endl;
|
||||
|
||||
@@ -14,10 +14,10 @@ EOH
|
||||
exit 1;
|
||||
}
|
||||
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=${CMAKE_INSTALL_PREFIX}
|
||||
libdir=${LIB_INSTALL_DIR}
|
||||
includedir=${INCLUDE_INSTALL_DIR}
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||
libdir=@LIB_INSTALL_DIR@
|
||||
includedir=@INCLUDE_INSTALL_DIR@
|
||||
|
||||
flags=""
|
||||
|
||||
@@ -29,13 +29,13 @@ while test $# -gt 0
|
||||
do
|
||||
case $1 in
|
||||
--libs)
|
||||
flags="$flags -L$libdir -ltag"
|
||||
flags="$flags -L$libdir -ltag @ZLIB_LIBRARIES_FLAGS@"
|
||||
;;
|
||||
--cflags)
|
||||
flags="$flags -I$includedir/taglib"
|
||||
flags="$flags -I$includedir -I$includedir/taglib"
|
||||
;;
|
||||
--version)
|
||||
echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
echo @TAGLIB_LIB_VERSION_STRING@
|
||||
;;
|
||||
--prefix)
|
||||
echo $prefix
|
||||
|
||||
@@ -28,7 +28,7 @@ goto theend
|
||||
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
|
||||
:doit
|
||||
if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag
|
||||
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR} -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
|
||||
|
||||
|
||||
@@ -7,5 +7,5 @@ Name: TagLib
|
||||
Description: Audio meta-data library
|
||||
Requires:
|
||||
Version: @TAGLIB_LIB_VERSION_STRING@
|
||||
Libs: -L${libdir} -ltag
|
||||
Cflags: -I${includedir}/taglib
|
||||
Libs: -L${libdir} -ltag @ZLIB_LIBRARIES_FLAGS@
|
||||
Cflags: -I${includedir} -I${includedir}/taglib
|
||||
|
||||
@@ -24,9 +24,7 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
|
||||
${CMAKE_SOURCE_DIR}/3rdparty
|
||||
${taglib_SOURCE_DIR}/3rdparty
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
@@ -63,6 +61,7 @@ set(tag_HDRS
|
||||
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
|
||||
@@ -140,11 +139,6 @@ set(tag_HDRS
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
dsf/dsffile.h
|
||||
dsf/dsfproperties.h
|
||||
dsdiff/dsdifffile.h
|
||||
dsdiff/dsdiffproperties.h
|
||||
dsdiff/dsdiffdiintag.h
|
||||
)
|
||||
|
||||
set(mpeg_SRCS
|
||||
@@ -300,17 +294,6 @@ set(xm_SRCS
|
||||
xm/xmproperties.cpp
|
||||
)
|
||||
|
||||
set(dsf_SRCS
|
||||
dsf/dsffile.cpp
|
||||
dsf/dsfproperties.cpp
|
||||
)
|
||||
|
||||
set(dsdiff_SRCS
|
||||
dsdiff/dsdifffile.cpp
|
||||
dsdiff/dsdiffproperties.cpp
|
||||
dsdiff/dsdiffdiintag.cpp
|
||||
)
|
||||
|
||||
set(toolkit_SRCS
|
||||
toolkit/tstring.cpp
|
||||
toolkit/tstringlist.cpp
|
||||
@@ -342,7 +325,7 @@ 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}
|
||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
|
||||
${zlib_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
@@ -352,6 +335,7 @@ set(tag_LIB_SRCS
|
||||
)
|
||||
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
|
||||
set_property(TARGET tag PROPERTY CXX_STANDARD 98)
|
||||
|
||||
if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
|
||||
target_link_libraries(tag ${ZLIB_LIBRARIES})
|
||||
@@ -366,8 +350,7 @@ set_target_properties(tag PROPERTIES
|
||||
PUBLIC_HEADER "${tag_HDRS}"
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden
|
||||
)
|
||||
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
endif()
|
||||
|
||||
if(BUILD_FRAMEWORK)
|
||||
|
||||
@@ -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 i
|
||||
tems 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
|
||||
items 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
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Properties(File *file, ReadStyle style = Average);
|
||||
TAGLIB_DEPRECATED Properties(File *file, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
@@ -76,7 +76,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -215,7 +215,9 @@ namespace
|
||||
{"DATE", "YEAR" },
|
||||
{"ALBUMARTIST", "ALBUM ARTIST"},
|
||||
{"DISCNUMBER", "DISC" },
|
||||
{"REMIXER", "MIXARTIST" }};
|
||||
{"REMIXER", "MIXARTIST" },
|
||||
{"RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS" },
|
||||
{"RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE" }};
|
||||
const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace
|
||||
{ "WM/AlbumTitle", "ALBUM" },
|
||||
{ "WM/AlbumArtist", "ALBUMARTIST" },
|
||||
{ "WM/Composer", "COMPOSER" },
|
||||
{ "WM/Writer", "WRITER" },
|
||||
{ "WM/Writer", "LYRICIST" },
|
||||
{ "WM/Conductor", "CONDUCTOR" },
|
||||
{ "WM/ModifiedBy", "REMIXER" },
|
||||
{ "WM/Year", "DATE" },
|
||||
@@ -243,11 +243,17 @@ namespace
|
||||
{ "WM/TitleSortOrder", "TITLESORT" },
|
||||
{ "WM/Script", "SCRIPT" },
|
||||
{ "WM/Language", "LANGUAGE" },
|
||||
{ "WM/ARTISTS", "ARTISTS" },
|
||||
{ "ASIN", "ASIN" },
|
||||
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "MusicBrainz/Album Release Country", "RELEASECOUNTRY" },
|
||||
{ "MusicBrainz/Album Status", "RELEASESTATUS" },
|
||||
{ "MusicBrainz/Album Type", "RELEASETYPE" },
|
||||
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID" },
|
||||
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "MusicIP/PUID", "MUSICIP_PUID" },
|
||||
{ "Acoustid/Id", "ACOUSTID_ID" },
|
||||
|
||||
@@ -160,6 +160,7 @@ namespace TagLib {
|
||||
* Returns a reference to the item list map. This is an AttributeListMap of
|
||||
* all of the items in the tag.
|
||||
*/
|
||||
// BIC: return by value
|
||||
const AttributeListMap &attributeListMap() const;
|
||||
|
||||
/*!
|
||||
|
||||
@@ -38,8 +38,6 @@
|
||||
#include "vorbisproperties.h"
|
||||
#include "wavproperties.h"
|
||||
#include "wavpackproperties.h"
|
||||
#include "dsfproperties.h"
|
||||
#include "dsdiffproperties.h"
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
@@ -75,10 +73,6 @@ using namespace TagLib;
|
||||
return dynamic_cast<const Vorbis::Properties*>(this)->function_name(); \
|
||||
else if(dynamic_cast<const WavPack::Properties*>(this)) \
|
||||
return dynamic_cast<const WavPack::Properties*>(this)->function_name(); \
|
||||
else if(dynamic_cast<const DSF::Properties*>(this)) \
|
||||
return dynamic_cast<const DSF::Properties*>(this)->function_name(); \
|
||||
else if(dynamic_cast<const DSDIFF::Properties*>(this)) \
|
||||
return dynamic_cast<const DSDIFF::Properties*>(this)->function_name(); \
|
||||
else \
|
||||
return (default_value);
|
||||
|
||||
|
||||
@@ -1,162 +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 "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace DSDIFF::DIIN;
|
||||
|
||||
class DSDIFF::DIIN::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
String title;
|
||||
String artist;
|
||||
};
|
||||
|
||||
DSDIFF::DIIN::Tag::Tag() : TagLib::Tag()
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if(title.isNull() || title.isEmpty())
|
||||
d->title = String();
|
||||
else
|
||||
d->title = title;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setArtist(const String &artist)
|
||||
{
|
||||
if(artist.isNull() || artist.isEmpty())
|
||||
d->artist = String();
|
||||
else
|
||||
d->artist = artist;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setAlbum(const String &)
|
||||
{
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setComment(const String &)
|
||||
{
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setGenre(const String &)
|
||||
{
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setYear(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
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 properties(origProps);
|
||||
properties.removeEmpty();
|
||||
StringList oneValueSet;
|
||||
|
||||
if(properties.contains("TITLE")) {
|
||||
d->title = properties["TITLE"].front();
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
d->title = String();
|
||||
|
||||
if(properties.contains("ARTIST")) {
|
||||
d->artist = properties["ARTIST"].front();
|
||||
oneValueSet.append("ARTIST");
|
||||
} else
|
||||
d->artist = String();
|
||||
|
||||
// for each tag that has been set above, remove the first entry in the corresponding
|
||||
// value list. The others will be returned as unsupported by this format.
|
||||
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
|
||||
if(properties[*it].size() == 1)
|
||||
properties.erase(*it);
|
||||
else
|
||||
properties[*it].erase(properties[*it].begin());
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -1,150 +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();
|
||||
virtual ~Tag();
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
String title() const;
|
||||
|
||||
/*!
|
||||
* Returns the artist name; if no artist name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
String artist() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String album() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String comment() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String genre() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
unsigned int year() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
unsigned int track() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a title. If \a title is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
void setTitle(const String &title);
|
||||
|
||||
/*!
|
||||
* Sets the artist to \a artist. If \a artist is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
void setArtist(const String &artist);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setAlbum(const String &album);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setComment(const String &comment);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setGenre(const String &genre);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setYear(unsigned int year);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setTrack(unsigned int track);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Since the DIIN tag is very limited, the exported map is as well.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* 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 &);
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,812 +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 <tbytevector.h>
|
||||
#include <tdebug.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "tagunion.h"
|
||||
#include "dsdifffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
struct Chunk64
|
||||
{
|
||||
ByteVector name;
|
||||
unsigned long long offset;
|
||||
unsigned long long size;
|
||||
char padding;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
enum {
|
||||
ID3v2Index = 0,
|
||||
DIINIndex = 1
|
||||
};
|
||||
enum {
|
||||
PROPChunk = 0,
|
||||
DIINChunk = 1
|
||||
};
|
||||
}
|
||||
|
||||
class DSDIFF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
endianness(BigEndian),
|
||||
size(0),
|
||||
isID3InPropChunk(false),
|
||||
duplicateID3V2chunkIndex(-1),
|
||||
properties(0),
|
||||
id3v2TagChunkID("ID3 "),
|
||||
hasID3v2(false),
|
||||
hasDiin(false)
|
||||
{
|
||||
childChunkIndex[ID3v2Index] = -1;
|
||||
childChunkIndex[DIINIndex] = -1;
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete properties;
|
||||
}
|
||||
|
||||
Endianness endianness;
|
||||
ByteVector type;
|
||||
unsigned long long size;
|
||||
ByteVector format;
|
||||
std::vector<Chunk64> chunks;
|
||||
std::vector<Chunk64> childChunks[2];
|
||||
int childChunkIndex[2];
|
||||
bool isID3InPropChunk; // Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
|
||||
int duplicateID3V2chunkIndex; // 2 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.
|
||||
|
||||
Properties *properties;
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
ByteVector id3v2TagChunkID;
|
||||
|
||||
bool hasID3v2;
|
||||
bool hasDiin;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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) : TagLib::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->endianness = BigEndian;
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSDIFF::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->endianness = BigEndian;
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSDIFF::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
TagLib::Tag *DSDIFF::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
ID3v2::Tag *DSDIFF::File::ID3v2Tag() const
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->hasID3v2;
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag() const
|
||||
{
|
||||
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasDIINTag() const
|
||||
{
|
||||
return d->hasDiin;
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::File::properties() const
|
||||
{
|
||||
if(d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
|
||||
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
if(d->hasID3v2)
|
||||
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(unsupported);
|
||||
|
||||
if(d->hasDiin)
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
|
||||
}
|
||||
|
||||
DSDIFF::Properties *DSDIFF::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
}
|
||||
|
||||
bool DSDIFF::File::save()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// First: save ID3V2 chunk
|
||||
ID3v2::Tag *id3v2Tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
if(d->isID3InPropChunk) {
|
||||
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
|
||||
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(), PROPChunk);
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
|
||||
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render());
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setRootChunkData(d->id3v2TagChunkID, ByteVector());
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Second: save the DIIN chunk
|
||||
if(d->hasDiin) {
|
||||
DSDIFF::DIIN::Tag *diinTag = d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
|
||||
|
||||
if(!diinTag->title().isNull() && !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().isNull() && !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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
|
||||
{
|
||||
if(data.isNull() || data.isEmpty()) {
|
||||
// Null data: remove chunk
|
||||
// Update global size
|
||||
unsigned long long removedChunkTotalSize = d->chunks[i].size + d->chunks[i].padding + 12;
|
||||
d->size -= removedChunkTotalSize;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
removeBlock(d->chunks[i].offset - 12, removedChunkTotalSize);
|
||||
|
||||
// Update the internal offsets
|
||||
for(unsigned long r = i + 1; r < d->chunks.size(); r++)
|
||||
d->chunks[r].offset = d->chunks[r - 1].offset + 12
|
||||
+ d->chunks[r - 1].size + d->chunks[r - 1].padding;
|
||||
|
||||
d->chunks.erase(d->chunks.begin() + i);
|
||||
}
|
||||
else {
|
||||
// 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,
|
||||
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.size() == 0) {
|
||||
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == name) {
|
||||
setRootChunkData(i, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
unsigned int i = d->chunks.size() - 1;
|
||||
unsigned 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
|
||||
writeChunk(name,
|
||||
data,
|
||||
offset,
|
||||
std::max<unsigned long long>(0, length() - offset),
|
||||
(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::setChildChunkData(unsigned int i,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
{
|
||||
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(data.isNull() || data.isEmpty()) {
|
||||
// Null data: remove chunk
|
||||
// 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;
|
||||
i++;
|
||||
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
|
||||
for(i = d->childChunkIndex[childChunkNum] + 1; 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;
|
||||
|
||||
childChunks.erase(childChunks.begin() + i);
|
||||
}
|
||||
else {
|
||||
// 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,
|
||||
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)
|
||||
{
|
||||
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(childChunks.size() == 0) {
|
||||
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < childChunks.size(); i++) {
|
||||
if(childChunks[i].name == name) {
|
||||
setChildChunkData(i, data, childChunkNum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not attempt to remove a non existing chunk
|
||||
if(data.isNull() || data.isEmpty())
|
||||
return;
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
unsigned int i = childChunks.size() - 1;
|
||||
unsigned long offset = childChunks[i].offset + childChunks[i].size + childChunks[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);
|
||||
// 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,
|
||||
std::max<unsigned long long>(0, nextRootChunkIdx - offset),
|
||||
(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);
|
||||
}
|
||||
|
||||
static bool isValidChunkID(const ByteVector &name)
|
||||
{
|
||||
if(name.size() != 4)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(name[i] < 32 || name[i] > 127)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void 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 childchunks structure as well
|
||||
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
|
||||
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[PROPChunk];
|
||||
if(childChunksToUpdate.size() > 0) {
|
||||
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)) {
|
||||
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[DIINChunk];
|
||||
if(childChunksToUpdate.size() > 0) {
|
||||
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;
|
||||
long uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
else
|
||||
chunk.padding = 1;
|
||||
}
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
unsigned long long lengthDSDSamplesTimeChannels = 0; // For DSD uncompressed
|
||||
unsigned long long audioDataSizeinBytes = 0; // For computing bitrate
|
||||
unsigned long dstNumFrames = 0; // For DST compressed frames
|
||||
unsigned short dstFrameRate = 0; // For DST compressed frames
|
||||
|
||||
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);
|
||||
break; // Found the wanted one, no need to look at the others
|
||||
}
|
||||
|
||||
seek(dstChunkSize, Current);
|
||||
|
||||
// Check padding
|
||||
long uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
}
|
||||
}
|
||||
}
|
||||
else 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;
|
||||
seek(d->chunks[i].offset + 4); // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
|
||||
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;
|
||||
long uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
else
|
||||
chunk.padding = 1;
|
||||
}
|
||||
d->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;
|
||||
long uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
else
|
||||
chunk.padding = 1;
|
||||
}
|
||||
d->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->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;
|
||||
continue; // ID3V2 tag has already been found at root level
|
||||
}
|
||||
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset));
|
||||
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);
|
||||
unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
if(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);
|
||||
unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
if(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 = (unsigned long long)dstNumFrames
|
||||
* (unsigned long long)sampleRate / (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 = (audioDataSizeinBytes*8*sampleRate) / lengthDSDSamplesTimeChannels / 1000;
|
||||
|
||||
d->properties = new Properties(sampleRate,
|
||||
channels,
|
||||
lengthDSDSamplesTimeChannels,
|
||||
bitrate,
|
||||
propertiesStyle);
|
||||
}
|
||||
|
||||
if(!ID3v2Tag()) {
|
||||
d->tag.access<ID3v2::Tag>(ID3v2Index, true);
|
||||
d->isID3InPropChunk = false; // By default, ID3 chunk is at root level
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,260 +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 "rifffile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "dsdiffproperties.h"
|
||||
#include "dsdiffdiintag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of DSDIFF metadata
|
||||
|
||||
/*!
|
||||
* This is 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 http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
|
||||
* DSDIFF standard does not explictly specify the ID3V2 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:
|
||||
/*!
|
||||
* Constructs an DSDIFF file from \a file. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs an DSDIFF file from \a stream. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* 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()
|
||||
*/
|
||||
virtual Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* 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()
|
||||
*/
|
||||
virtual ID3v2::Tag *ID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the DSDIFF DIIN Tag for this file
|
||||
*
|
||||
*/
|
||||
DSDIFF::DIIN::Tag *DIINTag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This method forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the AIFF::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
|
||||
* will duplicate its content into the other tag. This returns 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 paramaterized save call below.
|
||||
*
|
||||
* \see save(int tags)
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Save the file. This will attempt to save all of the tag types that are
|
||||
* specified by OR-ing together TagTypes values. The save() method above
|
||||
* uses AllTags. This returns true if saving was successful.
|
||||
*
|
||||
* This strips all tags not included in the mask, but does not modify them
|
||||
* in memory, so later calls to save() which make use of these tags will
|
||||
* remain valid. This also strips empty tags.
|
||||
*/
|
||||
bool save(int tags);
|
||||
|
||||
/*!
|
||||
* 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 & Artist tag.
|
||||
*
|
||||
* \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 };
|
||||
|
||||
File(FileName file, Endianness endianness);
|
||||
File(IOStream *stream, Endianness endianness);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
/*!
|
||||
* Sets the data for the 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 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 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;
|
||||
FilePrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,120 +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 <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "dsdiffproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSDIFF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
sampleWidth(0),
|
||||
sampleCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int sampleWidth;
|
||||
unsigned long long sampleCount;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSDIFF::Properties::Properties(const unsigned int sampleRate,
|
||||
const unsigned short channels,
|
||||
const unsigned long long samplesCount,
|
||||
const int bitrate,
|
||||
ReadStyle style) : AudioProperties(style)
|
||||
{
|
||||
d = new PropertiesPrivate;
|
||||
|
||||
d->channels = channels;
|
||||
d->sampleCount = samplesCount;
|
||||
d->sampleWidth = 1;
|
||||
d->sampleRate = sampleRate;
|
||||
d->bitrate = bitrate;
|
||||
d->length = d->sampleRate > 0
|
||||
? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5)
|
||||
: 0;
|
||||
}
|
||||
|
||||
DSDIFF::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 - 2018 by 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 <tbytevector.h>
|
||||
#include <tdebug.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "dsffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
||||
|
||||
class DSF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
properties(0),
|
||||
tag(0)
|
||||
{
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete properties;
|
||||
delete tag;
|
||||
}
|
||||
|
||||
long long fileSize;
|
||||
long long metadataOffset;
|
||||
Properties *properties;
|
||||
ID3v2::Tag *tag;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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 ");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSF::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
ID3v2::Tag *DSF::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool DSF::File::save()
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("DSF::File::save() -- File is read only.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isValid()) {
|
||||
debug("DSF::File::save() -- Trying to save invalid 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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void DSF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||
{
|
||||
// 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 chunkSize = readBlock(8).toLongLong(false);
|
||||
|
||||
// Integrity check
|
||||
if(28 != chunkSize) {
|
||||
debug("DSF::File::read() -- File is corrupted, wrong chunk size");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->fileSize = readBlock(8).toLongLong(false);
|
||||
|
||||
// File is malformed or corrupted
|
||||
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;
|
||||
}
|
||||
|
||||
chunkSize = readBlock(8).toLongLong(false);
|
||||
|
||||
d->properties = new Properties(readBlock(chunkSize), propertiesStyle);
|
||||
|
||||
// Skip the data chunk
|
||||
|
||||
// A metadata offset of 0 indicates the absence of an ID3v2 tag
|
||||
if(0 == d->metadataOffset)
|
||||
d->tag = new ID3v2::Tag();
|
||||
else
|
||||
d->tag = new ID3v2::Tag(this, d->metadataOffset);
|
||||
}
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 - 2018 by 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 "tfile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "dsfproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of DSF metadata
|
||||
|
||||
/*!
|
||||
* This is implementation of DSF metadata.
|
||||
*
|
||||
* This supports an ID3v2 tag as well as properties from the file.
|
||||
*/
|
||||
|
||||
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:
|
||||
/*!
|
||||
* Contructs an DSF 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);
|
||||
|
||||
/*!
|
||||
* Contructs an DSF 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);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file.
|
||||
*/
|
||||
ID3v2::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This method forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the DSF::AudioProperties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Saves the file.
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* 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:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by 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 <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "dsfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
formatVersion(0),
|
||||
formatID(0),
|
||||
channelType(0),
|
||||
channelNum(0),
|
||||
samplingFrequency(0),
|
||||
bitsPerSample(0),
|
||||
sampleCount(0),
|
||||
blockSizePerChannel(0),
|
||||
bitrate(0),
|
||||
length(0)
|
||||
{
|
||||
}
|
||||
|
||||
// Nomenclature is from DSF file format specification
|
||||
unsigned int formatVersion;
|
||||
unsigned int formatID;
|
||||
unsigned int channelType;
|
||||
unsigned int channelNum;
|
||||
unsigned int samplingFrequency;
|
||||
unsigned int bitsPerSample;
|
||||
long long sampleCount;
|
||||
unsigned int blockSizePerChannel;
|
||||
|
||||
// Computed
|
||||
unsigned int bitrate;
|
||||
unsigned int length;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) : TagLib::AudioProperties(style)
|
||||
{
|
||||
d = new PropertiesPrivate;
|
||||
read(data);
|
||||
}
|
||||
|
||||
DSF::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int DSF::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int DSF::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// DSF specific
|
||||
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>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5) : 0;
|
||||
}
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by 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 "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSF {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for DSF
|
||||
|
||||
/*!
|
||||
* This reads the data from a DSF stream found in the AudioProperties
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of DSF::AudioProperties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
Properties(const ByteVector &data, ReadStyle style);
|
||||
|
||||
/*!
|
||||
* Destroys this DSF::AudioProperties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
virtual int length() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
|
||||
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:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read(const ByteVector &data);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tfilestream.h>
|
||||
#include <tstring.h>
|
||||
@@ -52,8 +54,6 @@
|
||||
#include "s3mfile.h"
|
||||
#include "itfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -67,6 +67,13 @@ namespace
|
||||
File *detectByResolvers(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(::strlen(fileName) == 0 && ::wcslen(fileName) == 0)
|
||||
return 0;
|
||||
#else
|
||||
if(::strlen(fileName) == 0)
|
||||
return 0;
|
||||
#endif
|
||||
ResolverList::ConstIterator it = fileTypeResolvers.begin();
|
||||
for(; it != fileTypeResolvers.end(); ++it) {
|
||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
@@ -137,10 +144,6 @@ namespace
|
||||
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "DFF" || ext == "DSDIFF")
|
||||
return new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "DSF")
|
||||
return new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -180,10 +183,6 @@ namespace
|
||||
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(APE::File::isSupported(stream))
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(DSDIFF::File::isSupported(stream))
|
||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(DSF::File::isSupported(stream))
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
// isSupported() only does a quick check, so double check the file here.
|
||||
|
||||
@@ -265,10 +264,6 @@ namespace
|
||||
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "DFF" || ext == "DSDIFF")
|
||||
return new DSDIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "DSF")
|
||||
return new DSF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -376,6 +371,7 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("ogg");
|
||||
l.append("flac");
|
||||
l.append("oga");
|
||||
l.append("opus");
|
||||
l.append("mp3");
|
||||
l.append("mpc");
|
||||
l.append("wv");
|
||||
@@ -392,6 +388,8 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("asf");
|
||||
l.append("aif");
|
||||
l.append("aiff");
|
||||
l.append("afc");
|
||||
l.append("aifc");
|
||||
l.append("wav");
|
||||
l.append("ape");
|
||||
l.append("mod");
|
||||
@@ -401,9 +399,6 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("s3m");
|
||||
l.append("it");
|
||||
l.append("xm");
|
||||
l.append("dsf");
|
||||
l.append("dff");
|
||||
l.append("dsdiff"); // alias for "dff"
|
||||
|
||||
return l;
|
||||
}
|
||||
@@ -477,7 +472,11 @@ void FileRef::parse(FileName fileName, bool readAudioProperties,
|
||||
void FileRef::parse(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
// User-defined resolvers won't work with a stream.
|
||||
// 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.
|
||||
|
||||
|
||||
@@ -166,8 +166,8 @@ namespace TagLib {
|
||||
* \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 \class Tag.
|
||||
* Use tag returning methods of appropriate subclasses of \class File instead.
|
||||
* \warning Do not cast it to any subclasses of Tag.
|
||||
* Use tag returning methods of appropriate subclasses of File instead.
|
||||
*
|
||||
* \see File::tag()
|
||||
*/
|
||||
|
||||
@@ -187,16 +187,24 @@ bool FLAC::File::save()
|
||||
|
||||
// Replace metadata blocks
|
||||
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
MetadataBlock *commentBlock =
|
||||
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end();) {
|
||||
if((*it)->code() == MetadataBlock::VorbisComment) {
|
||||
// Set the new Vorbis Comment block
|
||||
// Remove the old Vorbis Comment block
|
||||
delete *it;
|
||||
d->blocks.erase(it);
|
||||
break;
|
||||
it = d->blocks.erase(it);
|
||||
continue;
|
||||
}
|
||||
if(commentBlock && (*it)->code() == MetadataBlock::Picture) {
|
||||
// Set the new Vorbis Comment block before the first picture block
|
||||
d->blocks.insert(it, commentBlock);
|
||||
commentBlock = 0;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
d->blocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
|
||||
if(commentBlock)
|
||||
d->blocks.append(commentBlock);
|
||||
|
||||
// Render data for the metadata blocks
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ namespace TagLib {
|
||||
* \see ID3v2FrameFactory
|
||||
* \deprecated This value should be passed in via the constructor
|
||||
*/
|
||||
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
TAGLIB_DEPRECATED void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
|
||||
/*!
|
||||
* Returns the block of data used by FLAC::Properties for parsing the
|
||||
@@ -248,7 +248,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated Always returns an empty vector.
|
||||
*/
|
||||
ByteVector streamInfoData(); // BIC: remove
|
||||
TAGLIB_DEPRECATED ByteVector streamInfoData(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns the length of the audio-stream, used by FLAC::Properties for
|
||||
@@ -256,7 +256,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated Always returns zero.
|
||||
*/
|
||||
long streamLength(); // BIC: remove
|
||||
TAGLIB_DEPRECATED long streamLength(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns a list of pictures attached to the FLAC file.
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -120,7 +120,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
int sampleWidth() const;
|
||||
TAGLIB_DEPRECATED int sampleWidth() const;
|
||||
|
||||
/*!
|
||||
* Return the number of sample frames.
|
||||
|
||||
@@ -277,7 +277,7 @@ 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 interprete a nil as a space. I
|
||||
// e.g. VLC seems to interpret 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 + ((long)i << 2));
|
||||
|
||||
@@ -31,6 +31,27 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Calculate the total bytes used by audio data, used to calculate the bitrate
|
||||
long long calculateMdatLength(const MP4::AtomList &list)
|
||||
{
|
||||
long long totalLength = 0;
|
||||
for(MP4::AtomList::ConstIterator it = list.begin(); it != list.end(); ++it) {
|
||||
long length = (*it)->length;
|
||||
if(length == 0)
|
||||
return 0; // for safety, see checkValid() in mp4file.cpp
|
||||
|
||||
if((*it)->name == "mdat")
|
||||
totalLength += length;
|
||||
|
||||
totalLength += calculateMdatLength((*it)->children);
|
||||
}
|
||||
|
||||
return totalLength;
|
||||
}
|
||||
}
|
||||
|
||||
class MP4::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
@@ -175,11 +196,11 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
unit = data.toLongLong(28U);
|
||||
length = data.toLongLong(36U);
|
||||
unit = data.toUInt(28U);
|
||||
length = data.toLongLong(32U);
|
||||
}
|
||||
else {
|
||||
if(data.size() < 24 + 4) {
|
||||
if(data.size() < 24 + 8) {
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
@@ -213,7 +234,14 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
pos += 3;
|
||||
}
|
||||
pos += 10;
|
||||
d->bitrate = static_cast<int>((data.toUInt(pos) + 500) / 1000.0 + 0.5);
|
||||
const unsigned int bitrateValue = data.toUInt(pos);
|
||||
if(bitrateValue != 0 || d->length <= 0) {
|
||||
d->bitrate = static_cast<int>((bitrateValue + 500) / 1000.0 + 0.5);
|
||||
}
|
||||
else {
|
||||
d->bitrate = static_cast<int>(
|
||||
(calculateMdatLength(atoms->atoms) * 8) / d->length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,6 +252,13 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
d->channels = data.at(73);
|
||||
d->bitrate = static_cast<int>(data.toUInt(80U) / 1000.0 + 0.5);
|
||||
d->sampleRate = data.toUInt(84U);
|
||||
|
||||
if(d->bitrate == 0 && d->length > 0) {
|
||||
// There are files which do not contain a nominal bitrate, e.g. those
|
||||
// generated by refalac64.exe. Calculate the bitrate from the audio
|
||||
// data size (mdat atoms) and the duration.
|
||||
d->bitrate = (calculateMdatLength(atoms->atoms) * 8) / d->length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -74,9 +74,20 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
|
||||
atom->name == "hdvd" || atom->name == "shwm") {
|
||||
parseBool(atom);
|
||||
}
|
||||
else if(atom->name == "tmpo" || atom->name == "rate" || atom->name == "\251mvi" || atom->name == "\251mvc") {
|
||||
else if(atom->name == "tmpo" || atom->name == "\251mvi" || atom->name == "\251mvc") {
|
||||
parseInt(atom);
|
||||
}
|
||||
else if(atom->name == "rate") {
|
||||
AtomDataList data = parseData2(atom);
|
||||
if(!data.isEmpty()) {
|
||||
AtomData val = data[0];
|
||||
if (val.type == TypeUTF8) {
|
||||
addItem(atom->name, StringList(String(val.data, String::UTF8)));
|
||||
} else {
|
||||
addItem(atom->name, (int)(val.data.toShort()));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" ||
|
||||
atom->name == "sfID" || atom->name == "atID" || atom->name == "geID" ||
|
||||
atom->name == "cmID") {
|
||||
@@ -294,7 +305,7 @@ MP4::Tag::parseCovr(const MP4::Atom *atom)
|
||||
const int length = static_cast<int>(data.toUInt(pos));
|
||||
if(length < 12) {
|
||||
debug("MP4: Too short atom");
|
||||
break;;
|
||||
break;
|
||||
}
|
||||
|
||||
const ByteVector name = data.mid(pos + 4, 4);
|
||||
@@ -480,9 +491,19 @@ MP4::Tag::save()
|
||||
name == "shwm") {
|
||||
data.append(renderBool(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "tmpo" || name == "rate" || name == "\251mvi" || name == "\251mvc") {
|
||||
else if(name == "tmpo" || name == "\251mvi" || name == "\251mvc") {
|
||||
data.append(renderInt(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if (name == "rate") {
|
||||
const MP4::Item& item = it->second;
|
||||
StringList value = item.toStringList();
|
||||
if (value.isEmpty()) {
|
||||
data.append(renderInt(name.data(String::Latin1), item));
|
||||
}
|
||||
else {
|
||||
data.append(renderText(name.data(String::Latin1), item));
|
||||
}
|
||||
}
|
||||
else if(name == "tvsn" || name == "tves" || name == "cnID" ||
|
||||
name == "sfID" || name == "atID" || name == "geID" ||
|
||||
name == "cmID") {
|
||||
@@ -754,43 +775,63 @@ MP4::Tag::track() const
|
||||
void
|
||||
MP4::Tag::setTitle(const String &value)
|
||||
{
|
||||
d->items["\251nam"] = StringList(value);
|
||||
setTextItem("\251nam", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setArtist(const String &value)
|
||||
{
|
||||
d->items["\251ART"] = StringList(value);
|
||||
setTextItem("\251ART", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setAlbum(const String &value)
|
||||
{
|
||||
d->items["\251alb"] = StringList(value);
|
||||
setTextItem("\251alb", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setComment(const String &value)
|
||||
{
|
||||
d->items["\251cmt"] = StringList(value);
|
||||
setTextItem("\251cmt", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setGenre(const String &value)
|
||||
{
|
||||
d->items["\251gen"] = StringList(value);
|
||||
setTextItem("\251gen", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setTextItem(const String &key, const String &value)
|
||||
{
|
||||
if (!value.isEmpty()) {
|
||||
d->items[key] = StringList(value);
|
||||
} else {
|
||||
d->items.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setYear(unsigned int value)
|
||||
{
|
||||
d->items["\251day"] = StringList(String::number(value));
|
||||
if (value == 0) {
|
||||
d->items.erase("\251day");
|
||||
}
|
||||
else {
|
||||
d->items["\251day"] = StringList(String::number(value));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setTrack(unsigned int value)
|
||||
{
|
||||
d->items["trkn"] = MP4::Item(value, 0);
|
||||
if (value == 0) {
|
||||
d->items.erase("trkn");
|
||||
}
|
||||
else {
|
||||
d->items["trkn"] = MP4::Item(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool MP4::Tag::isEmpty() const
|
||||
@@ -854,6 +895,17 @@ namespace
|
||||
{ "soco", "COMPOSERSORT" },
|
||||
{ "sosn", "SHOWSORT" },
|
||||
{ "shwm", "SHOWWORKMOVEMENT" },
|
||||
{ "pgap", "GAPLESSPLAYBACK" },
|
||||
{ "pcst", "PODCAST" },
|
||||
{ "catg", "PODCASTCATEGORY" },
|
||||
{ "desc", "PODCASTDESC" },
|
||||
{ "egid", "PODCASTID" },
|
||||
{ "purl", "PODCASTURL" },
|
||||
{ "tves", "TVEPISODE" },
|
||||
{ "tven", "TVEPISODEID" },
|
||||
{ "tvnn", "TVNETWORK" },
|
||||
{ "tvsn", "TVSEASON" },
|
||||
{ "tvsh", "TVSHOW" },
|
||||
{ "\251wrk", "WORK" },
|
||||
{ "\251mvn", "MOVEMENTNAME" },
|
||||
{ "\251mvi", "MOVEMENTNUMBER" },
|
||||
@@ -863,7 +915,13 @@ namespace
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Release Track Id", "MUSICBRAINZ_RELEASETRACKID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Release Country", "RELEASECOUNTRY" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Status", "RELEASESTATUS" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Type", "RELEASETYPE" },
|
||||
{ "----:com.apple.iTunes:ARTISTS", "ARTISTS" },
|
||||
{ "----:com.apple.iTunes:originaldate", "ORIGINALDATE" },
|
||||
{ "----:com.apple.iTunes:ASIN", "ASIN" },
|
||||
{ "----:com.apple.iTunes:LABEL", "LABEL" },
|
||||
{ "----:com.apple.iTunes:LYRICIST", "LYRICIST" },
|
||||
@@ -911,10 +969,12 @@ PropertyMap MP4::Tag::properties() const
|
||||
}
|
||||
props[key] = value;
|
||||
}
|
||||
else if(key == "BPM" || key == "MOVEMENTNUMBER" || key == "MOVEMENTCOUNT") {
|
||||
else if(key == "BPM" || key == "MOVEMENTNUMBER" || key == "MOVEMENTCOUNT" ||
|
||||
key == "TVEPISODE" || key == "TVSEASON") {
|
||||
props[key] = String::number(it->second.toInt());
|
||||
}
|
||||
else if(key == "COMPILATION" || key == "SHOWWORKMOVEMENT") {
|
||||
else if(key == "COMPILATION" || key == "SHOWWORKMOVEMENT" ||
|
||||
key == "GAPLESSPLAYBACK" || key == "PODCAST") {
|
||||
props[key] = String::number(it->second.toBool());
|
||||
}
|
||||
else {
|
||||
@@ -966,11 +1026,15 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
|
||||
d->items[name] = MP4::Item(first, second);
|
||||
}
|
||||
}
|
||||
else if((it->first == "BPM" || it->first == "MOVEMENTNUMBER" || it->first == "MOVEMENTCOUNT") && !it->second.isEmpty()) {
|
||||
else if((it->first == "BPM" || it->first == "MOVEMENTNUMBER" ||
|
||||
it->first == "MOVEMENTCOUNT" || it->first == "TVEPISODE" ||
|
||||
it->first == "TVSEASON") && !it->second.isEmpty()) {
|
||||
int value = it->second.front().toInt();
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
else if((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT") && !it->second.isEmpty()) {
|
||||
else if((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT" ||
|
||||
it->first == "GAPLESSPLAYBACK" || it->first == "PODCAST") &&
|
||||
!it->second.isEmpty()) {
|
||||
bool value = (it->second.front().toInt() != 0);
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* \deprecated
|
||||
*/
|
||||
typedef TagLib::Map<String, Item> ItemListMap;
|
||||
TAGLIB_DEPRECATED typedef TagLib::Map<String, Item> ItemListMap;
|
||||
typedef TagLib::Map<String, Item> ItemMap;
|
||||
|
||||
class TAGLIB_EXPORT Tag: public TagLib::Tag
|
||||
@@ -74,7 +74,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* \deprecated Use the item() and setItem() API instead
|
||||
*/
|
||||
ItemMap &itemListMap();
|
||||
TAGLIB_DEPRECATED ItemMap &itemListMap();
|
||||
|
||||
/*!
|
||||
* Returns a string-keyed map of the MP4::Items for this tag.
|
||||
@@ -106,6 +106,13 @@ namespace TagLib {
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Sets the value of \a key to \a value, overwriting any previous value.
|
||||
* If \a value is empty, the item is removed.
|
||||
*/
|
||||
void setTextItem(const String &key, const String &value);
|
||||
|
||||
private:
|
||||
AtomDataList parseData2(const Atom *atom, int expectedFlags = -1,
|
||||
bool freeForm = false);
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace TagLib {
|
||||
* \deprecated
|
||||
* \see strip
|
||||
*/
|
||||
void remove(int tags = AllTags);
|
||||
TAGLIB_DEPRECATED void remove(int tags = AllTags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
||||
|
||||
@@ -49,18 +49,17 @@ public:
|
||||
albumGain(0),
|
||||
albumPeak(0) {}
|
||||
|
||||
int version;
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int version;
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
unsigned int totalFrames;
|
||||
unsigned int sampleFrames;
|
||||
unsigned int trackGain;
|
||||
unsigned int trackPeak;
|
||||
unsigned int albumGain;
|
||||
unsigned int albumPeak;
|
||||
String flags;
|
||||
int trackGain;
|
||||
int trackPeak;
|
||||
int albumGain;
|
||||
int albumPeak;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -312,9 +311,9 @@ void MPC::Properties::readSV7(const ByteVector &data, long streamLength)
|
||||
const unsigned int gapless = data.toUInt(5, false);
|
||||
|
||||
d->trackGain = data.toShort(14, false);
|
||||
d->trackPeak = data.toShort(12, false);
|
||||
d->trackPeak = data.toUShort(12, false);
|
||||
d->albumGain = data.toShort(18, false);
|
||||
d->albumPeak = data.toShort(16, false);
|
||||
d->albumPeak = data.toUShort(16, false);
|
||||
|
||||
// convert gain info
|
||||
if(d->trackGain != 0) {
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace
|
||||
L"Ambient",
|
||||
L"Trip-Hop",
|
||||
L"Vocal",
|
||||
L"Jazz+Funk",
|
||||
L"Jazz-Funk",
|
||||
L"Fusion",
|
||||
L"Trance",
|
||||
L"Classical",
|
||||
@@ -111,16 +111,16 @@ namespace
|
||||
L"Rock & Roll",
|
||||
L"Hard Rock",
|
||||
L"Folk",
|
||||
L"Folk/Rock",
|
||||
L"Folk Rock",
|
||||
L"National Folk",
|
||||
L"Swing",
|
||||
L"Fusion",
|
||||
L"Bebob",
|
||||
L"Fast Fusion",
|
||||
L"Bebop",
|
||||
L"Latin",
|
||||
L"Revival",
|
||||
L"Celtic",
|
||||
L"Bluegrass",
|
||||
L"Avantgarde",
|
||||
L"Avant-garde",
|
||||
L"Gothic Rock",
|
||||
L"Progressive Rock",
|
||||
L"Psychedelic Rock",
|
||||
@@ -155,15 +155,15 @@ namespace
|
||||
L"Drum Solo",
|
||||
L"A Cappella",
|
||||
L"Euro-House",
|
||||
L"Dance Hall",
|
||||
L"Dancehall",
|
||||
L"Goa",
|
||||
L"Drum & Bass",
|
||||
L"Club-House",
|
||||
L"Hardcore",
|
||||
L"Hardcore Techno",
|
||||
L"Terror",
|
||||
L"Indie",
|
||||
L"BritPop",
|
||||
L"Negerpunk",
|
||||
L"Britpop",
|
||||
L"Worldbeat",
|
||||
L"Polsk Punk",
|
||||
L"Beat",
|
||||
L"Christian Gangsta Rap",
|
||||
@@ -261,5 +261,26 @@ int ID3v1::genreIndex(const String &name)
|
||||
return i;
|
||||
}
|
||||
|
||||
// If the name was not found, try the names which have been changed
|
||||
static const struct {
|
||||
const wchar_t *genre;
|
||||
int code;
|
||||
} fixUpGenres[] = {
|
||||
{ L"Jazz+Funk", 29 },
|
||||
{ L"Folk/Rock", 81 },
|
||||
{ L"Bebob", 85 },
|
||||
{ L"Avantgarde", 90 },
|
||||
{ L"Dance Hall", 125 },
|
||||
{ L"Hardcore", 129 },
|
||||
{ L"BritPop", 132 },
|
||||
{ L"Negerpunk", 133 }
|
||||
};
|
||||
static const int fixUpGenresSize =
|
||||
sizeof(fixUpGenres) / sizeof(fixUpGenres[0]);
|
||||
for(int i = 0; i < fixUpGenresSize; ++i) {
|
||||
if(name == fixUpGenres[i].genre)
|
||||
return fixUpGenres[i].code;
|
||||
}
|
||||
|
||||
return 255;
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ void ChapterFrame::parseFields(const ByteVector &data)
|
||||
return;
|
||||
|
||||
while(embPos < size - header()->size()) {
|
||||
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), (d->tagHeader != 0));
|
||||
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader);
|
||||
|
||||
if(!frame)
|
||||
return;
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace TagLib {
|
||||
* \a startTime, end time \a endTime, start offset \a startOffset,
|
||||
* end offset \a endOffset and optionally a list of embedded frames,
|
||||
* whose ownership will then be taken over by this Frame, in
|
||||
* \a embeededFrames;
|
||||
* \a embeddedFrames;
|
||||
*
|
||||
* All times are in milliseconds.
|
||||
*/
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "podcastframe.h"
|
||||
#include <tpropertymap.h>
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace ID3v2;
|
||||
@@ -55,6 +56,13 @@ String PodcastFrame::toString() const
|
||||
return String();
|
||||
}
|
||||
|
||||
PropertyMap PodcastFrame::asProperties() const
|
||||
{
|
||||
PropertyMap map;
|
||||
map.insert("PODCAST", StringList());
|
||||
return map;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace TagLib {
|
||||
*/
|
||||
virtual String toString() const;
|
||||
|
||||
PropertyMap asProperties() const;
|
||||
|
||||
protected:
|
||||
// Reimplementations.
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ PrivateFrame::PrivateFrame(const ByteVector &data) :
|
||||
Frame(data),
|
||||
d(new PrivateFramePrivate())
|
||||
{
|
||||
setData(data);
|
||||
Frame::setData(data);
|
||||
}
|
||||
|
||||
PrivateFrame::~PrivateFrame()
|
||||
|
||||
@@ -131,12 +131,12 @@ namespace TagLib {
|
||||
/*!
|
||||
* \deprecated Always returns master volume.
|
||||
*/
|
||||
ChannelType channelType() const;
|
||||
TAGLIB_DEPRECATED ChannelType channelType() const;
|
||||
|
||||
/*!
|
||||
* \deprecated This method no longer has any effect.
|
||||
*/
|
||||
void setChannelType(ChannelType t);
|
||||
TAGLIB_DEPRECATED void setChannelType(ChannelType t);
|
||||
|
||||
/*
|
||||
* There was a terrible API goof here, and while this can't be changed to
|
||||
@@ -155,7 +155,7 @@ namespace TagLib {
|
||||
* available and returns 0 if the specified channel does not exist.
|
||||
*
|
||||
* \see setVolumeAdjustmentIndex()
|
||||
* \see volumeAjustment()
|
||||
* \see volumeAdjustment()
|
||||
*/
|
||||
short volumeAdjustmentIndex(ChannelType type = MasterVolume) const;
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace TagLib {
|
||||
* By default this sets the value for the master volume.
|
||||
*
|
||||
* \see volumeAdjustmentIndex()
|
||||
* \see setVolumeAjustment()
|
||||
* \see setVolumeAdjustment()
|
||||
*/
|
||||
void setVolumeAdjustmentIndex(short index, ChannelType type = MasterVolume);
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
namespace {
|
||||
|
||||
// These functions are needed to try to aim for backward compatibility with
|
||||
// an API that previously (unreasonably) required null bytes to be appeneded
|
||||
// an API that previously (unreasonably) required null bytes to be appended
|
||||
// at the end of identifiers explicitly by the API user.
|
||||
|
||||
// BIC: remove these
|
||||
@@ -168,7 +168,8 @@ void TableOfContentsFrame::removeChildElement(const ByteVector &cE)
|
||||
if(it == d->childElements.end())
|
||||
it = d->childElements.find(cE + ByteVector("\0"));
|
||||
|
||||
d->childElements.erase(it);
|
||||
if(it != d->childElements.end())
|
||||
d->childElements.erase(it);
|
||||
}
|
||||
|
||||
const FrameListMap &TableOfContentsFrame::embeddedFrameListMap() const
|
||||
@@ -196,11 +197,14 @@ void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del)
|
||||
{
|
||||
// remove the frame from the frame list
|
||||
FrameList::Iterator it = d->embeddedFrameList.find(frame);
|
||||
d->embeddedFrameList.erase(it);
|
||||
if(it != d->embeddedFrameList.end())
|
||||
d->embeddedFrameList.erase(it);
|
||||
|
||||
// ...and from the frame list map
|
||||
it = d->embeddedFrameListMap[frame->frameID()].find(frame);
|
||||
d->embeddedFrameListMap[frame->frameID()].erase(it);
|
||||
FrameList &mappedList = d->embeddedFrameListMap[frame->frameID()];
|
||||
it = mappedList.find(frame);
|
||||
if(it != mappedList.end())
|
||||
mappedList.erase(it);
|
||||
|
||||
// ...and delete as desired
|
||||
if(del)
|
||||
@@ -304,7 +308,7 @@ void TableOfContentsFrame::parseFields(const ByteVector &data)
|
||||
return;
|
||||
|
||||
while(embPos < size - header()->size()) {
|
||||
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), (d->tagHeader != 0));
|
||||
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader);
|
||||
|
||||
if(!frame)
|
||||
return;
|
||||
|
||||
@@ -63,7 +63,10 @@ TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const Property
|
||||
TextIdentificationFrame *frame = new TextIdentificationFrame("TIPL");
|
||||
StringList l;
|
||||
for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it){
|
||||
l.append(it->first);
|
||||
const String role = involvedPeopleMap()[it->first];
|
||||
if(role.isEmpty()) // should not happen
|
||||
continue;
|
||||
l.append(role);
|
||||
l.append(it->second.toString(",")); // comma-separated list of names
|
||||
}
|
||||
frame->setText(l);
|
||||
@@ -277,7 +280,7 @@ PropertyMap TextIdentificationFrame::makeTIPLProperties() const
|
||||
break;
|
||||
}
|
||||
if(!found){
|
||||
// invalid involved role -> mark whole frame as unsupported in order to be consisten with writing
|
||||
// invalid involved role -> mark whole frame as unsupported in order to be consistent with writing
|
||||
map.clear();
|
||||
map.unsupportedData().append(frameID());
|
||||
return map;
|
||||
|
||||
24
taglib/mpeg/id3v2/id3v2.h
Normal file
24
taglib/mpeg/id3v2/id3v2.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef TAGLIB_ID3V2_H
|
||||
#define TAGLIB_ID3V2_H
|
||||
|
||||
namespace TagLib {
|
||||
//! An ID3v2 implementation
|
||||
|
||||
/*!
|
||||
* This is a relatively complete and flexible framework for working with ID3v2
|
||||
* tags.
|
||||
*
|
||||
* \see ID3v2::Tag
|
||||
*/
|
||||
namespace ID3v2 {
|
||||
/*!
|
||||
* Used to specify which version of the ID3 standard to use when saving tags.
|
||||
*/
|
||||
enum Version {
|
||||
v3 = 3, //<! ID3v2.3
|
||||
v4 = 4 //<! ID3v2.4
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -38,7 +38,7 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* This class implements ID3v2 extended headers. It attempts to follow,
|
||||
* both semantically and programatically, the structure specified in
|
||||
* both semantically and programmatically, the structure specified in
|
||||
* the ID3v2 standard. The API is based on the properties of ID3v2 extended
|
||||
* headers specified there. If any of the terms used in this documentation
|
||||
* are unclear please check the specification in the linked section.
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "frames/commentsframe.h"
|
||||
#include "frames/uniquefileidentifierframe.h"
|
||||
#include "frames/unknownframe.h"
|
||||
#include "frames/podcastframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace ID3v2;
|
||||
@@ -111,8 +112,8 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
|
||||
// check if the key is contained in the key<=>frameID mapping
|
||||
ByteVector frameID = keyToFrameID(key);
|
||||
if(!frameID.isEmpty()) {
|
||||
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number) are in fact text frames.
|
||||
if(frameID[0] == 'T' || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN"){ // text frame
|
||||
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number), GRP1 (Grouping) are in fact text frames.
|
||||
if(frameID[0] == 'T' || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN" || frameID == "GRP1"){ // text frame
|
||||
TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8);
|
||||
frame->setText(values);
|
||||
return frame;
|
||||
@@ -120,6 +121,8 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
|
||||
UrlLinkFrame* frame = new UrlLinkFrame(frameID);
|
||||
frame->setUrl(values.front());
|
||||
return frame;
|
||||
} else if(frameID == "PCST") {
|
||||
return new PodcastFrame();
|
||||
}
|
||||
}
|
||||
if(key == "MUSICBRAINZ_TRACKID" && values.size() == 1) {
|
||||
@@ -344,7 +347,7 @@ namespace
|
||||
{ "TEXT", "LYRICIST" },
|
||||
{ "TFLT", "FILETYPE" },
|
||||
//{ "TIPL", "INVOLVEDPEOPLE" }, handled separately
|
||||
{ "TIT1", "CONTENTGROUP" },
|
||||
{ "TIT1", "CONTENTGROUP" }, // 'Work' in iTunes
|
||||
{ "TIT2", "TITLE"},
|
||||
{ "TIT3", "SUBTITLE" },
|
||||
{ "TKEY", "INITIALKEY" },
|
||||
@@ -369,6 +372,7 @@ namespace
|
||||
{ "TRSN", "RADIOSTATION" },
|
||||
{ "TRSO", "RADIOSTATIONOWNER" },
|
||||
{ "TSOA", "ALBUMSORT" },
|
||||
{ "TSOC", "COMPOSERSORT" },
|
||||
{ "TSOP", "ARTISTSORT" },
|
||||
{ "TSOT", "TITLESORT" },
|
||||
{ "TSO2", "ALBUMARTISTSORT" }, // non-standard, used by iTunes
|
||||
@@ -394,6 +398,7 @@ namespace
|
||||
{ "WFED", "PODCASTURL" },
|
||||
{ "MVNM", "MOVEMENTNAME" },
|
||||
{ "MVIN", "MOVEMENTNUMBER" },
|
||||
{ "GRP1", "GROUPING" },
|
||||
};
|
||||
const size_t frameTranslationSize = sizeof(frameTranslation) / sizeof(frameTranslation[0]);
|
||||
|
||||
@@ -401,7 +406,11 @@ namespace
|
||||
{ "MUSICBRAINZ ALBUM ID", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "MUSICBRAINZ ARTIST ID", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "MUSICBRAINZ ALBUM ARTIST ID", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "MUSICBRAINZ ALBUM RELEASE COUNTRY", "RELEASECOUNTRY" },
|
||||
{ "MUSICBRAINZ ALBUM STATUS", "RELEASESTATUS" },
|
||||
{ "MUSICBRAINZ ALBUM TYPE", "RELEASETYPE" },
|
||||
{ "MUSICBRAINZ RELEASE GROUP ID", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "MUSICBRAINZ RELEASE TRACK ID", "MUSICBRAINZ_RELEASETRACKID" },
|
||||
{ "MUSICBRAINZ WORK ID", "MUSICBRAINZ_WORKID" },
|
||||
{ "ACOUSTID ID", "ACOUSTID_ID" },
|
||||
{ "ACOUSTID FINGERPRINT", "ACOUSTID_FINGERPRINT" },
|
||||
@@ -416,7 +425,7 @@ namespace
|
||||
{"TYER", "TDRC"}, // 2.3 -> 2.4
|
||||
{"TIME", "TDRC"}, // 2.3 -> 2.4
|
||||
};
|
||||
const size_t deprecatedFramesSize = sizeof(deprecatedFrames) / sizeof(deprecatedFrames[0]);;
|
||||
const size_t deprecatedFramesSize = sizeof(deprecatedFrames) / sizeof(deprecatedFrames[0]);
|
||||
}
|
||||
|
||||
String Frame::frameIDToKey(const ByteVector &id)
|
||||
@@ -476,8 +485,8 @@ PropertyMap Frame::asProperties() const
|
||||
// workaround until this function is virtual
|
||||
if(id == "TXXX")
|
||||
return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties();
|
||||
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number) are in fact text frames.
|
||||
else if(id[0] == 'T' || id == "WFED" || id == "MVNM" || id == "MVIN")
|
||||
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number), GRP1 (Grouping) are in fact text frames.
|
||||
else if(id[0] == 'T' || id == "WFED" || id == "MVNM" || id == "MVIN" || id == "GRP1")
|
||||
return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties();
|
||||
else if(id == "WXXX")
|
||||
return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties();
|
||||
@@ -489,6 +498,8 @@ PropertyMap Frame::asProperties() const
|
||||
return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties();
|
||||
else if(id == "UFID")
|
||||
return dynamic_cast< const UniqueFileIdentifierFrame* >(this)->asProperties();
|
||||
else if(id == "PCST")
|
||||
return dynamic_cast< const PodcastFrame* >(this)->asProperties();
|
||||
PropertyMap m;
|
||||
m.unsupportedData().append(id);
|
||||
return m;
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace TagLib {
|
||||
* split between a collection of frames (which are in turn split into fields
|
||||
* (Structure, <a href="id3v2-structure.html#4">4</a>)
|
||||
* (<a href="id3v2-frames.html">Frames</a>). This class provides an API for
|
||||
* gathering information about and modifying ID3v2 frames. Funtionallity
|
||||
* gathering information about and modifying ID3v2 frames. Functionality
|
||||
* specific to a given frame type is handed in one of the many subclasses.
|
||||
*/
|
||||
|
||||
@@ -89,14 +89,15 @@ namespace TagLib {
|
||||
* non-binary compatible release this will be made into a non-static
|
||||
* member that checks the internal ID3v2 version.
|
||||
*/
|
||||
static unsigned int headerSize(); // BIC: remove and make non-static
|
||||
static unsigned int headerSize(); // BIC: make non-static
|
||||
|
||||
/*!
|
||||
* Returns the size of the frame header for the given ID3v2 version.
|
||||
*
|
||||
* \deprecated Please see the explanation above.
|
||||
*/
|
||||
static unsigned int headerSize(unsigned int version); // BIC: remove and make non-static
|
||||
// BIC: remove
|
||||
static unsigned int headerSize(unsigned int version);
|
||||
|
||||
/*!
|
||||
* Sets the data that will be used as the frame. Since the length is not
|
||||
@@ -224,7 +225,7 @@ namespace TagLib {
|
||||
* This is useful for reading strings sequentially.
|
||||
*/
|
||||
String readStringField(const ByteVector &data, String::Type encoding,
|
||||
int *positon = 0);
|
||||
int *position = 0);
|
||||
|
||||
/*!
|
||||
* Checks a the list of string values to see if they can be used with the
|
||||
@@ -255,7 +256,7 @@ namespace TagLib {
|
||||
|
||||
|
||||
/*!
|
||||
* Parses the contents of this frame as PropertyMap. If that fails, the returend
|
||||
* Parses the contents of this frame as PropertyMap. If that fails, the returned
|
||||
* PropertyMap will be empty, and its unsupportedData() will contain this frame's
|
||||
* ID.
|
||||
* BIC: Will be a virtual function in future releases.
|
||||
@@ -334,7 +335,7 @@ namespace TagLib {
|
||||
* \deprecated Please use the constructor below that accepts a version
|
||||
* number.
|
||||
*/
|
||||
Header(const ByteVector &data, bool synchSafeInts);
|
||||
TAGLIB_DEPRECATED Header(const ByteVector &data, bool synchSafeInts);
|
||||
|
||||
/*!
|
||||
* Construct a Frame Header based on \a data. \a data must at least
|
||||
@@ -356,7 +357,7 @@ namespace TagLib {
|
||||
* \deprecated Please use the version below that accepts an ID3v2 version
|
||||
* number.
|
||||
*/
|
||||
void setData(const ByteVector &data, bool synchSafeInts);
|
||||
TAGLIB_DEPRECATED void setData(const ByteVector &data, bool synchSafeInts);
|
||||
|
||||
/*!
|
||||
* Sets the data for the Header. \a version should indicate the ID3v2
|
||||
@@ -411,6 +412,7 @@ namespace TagLib {
|
||||
* removed in the next binary incompatible release (2.0) and will be
|
||||
* replaced with a non-static method that checks the frame version.
|
||||
*/
|
||||
// BIC: make non-static
|
||||
static unsigned int size();
|
||||
|
||||
/*!
|
||||
@@ -419,6 +421,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated Please see the explanation in the version above.
|
||||
*/
|
||||
// BIC: remove
|
||||
static unsigned int size(unsigned int version);
|
||||
|
||||
/*!
|
||||
@@ -502,7 +505,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* \deprecated
|
||||
*/
|
||||
bool frameAlterPreservation() const;
|
||||
TAGLIB_DEPRECATED bool frameAlterPreservation() const;
|
||||
|
||||
private:
|
||||
Header(const Header &);
|
||||
|
||||
@@ -60,22 +60,24 @@ namespace
|
||||
|
||||
for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
|
||||
String s = *it;
|
||||
int end = s.find(")");
|
||||
int offset = 0;
|
||||
int end = 0;
|
||||
|
||||
if(s.startsWith("(") && end > 0) {
|
||||
while(s.length() > offset && s[offset] == '(' &&
|
||||
(end = s.find(")", offset + 1)) > offset) {
|
||||
// "(12)Genre"
|
||||
String text = s.substr(end + 1);
|
||||
const String genreCode = s.substr(offset + 1, end - 1);
|
||||
s = s.substr(end + 1);
|
||||
bool ok;
|
||||
int number = s.substr(1, end - 1).toInt(&ok);
|
||||
if(ok && number >= 0 && number <= 255 && !(ID3v1::genre(number) == text))
|
||||
newfields.append(s.substr(1, end - 1));
|
||||
if(!text.isEmpty())
|
||||
newfields.append(text);
|
||||
int number = genreCode.toInt(&ok);
|
||||
if((ok && number >= 0 && number <= 255 &&
|
||||
!(ID3v1::genre(number) == s)) ||
|
||||
genreCode == "RX" || genreCode == "CR")
|
||||
newfields.append(genreCode);
|
||||
}
|
||||
else {
|
||||
if(!s.isEmpty())
|
||||
// "Genre" or "12"
|
||||
newfields.append(s);
|
||||
}
|
||||
}
|
||||
|
||||
if(newfields.isEmpty())
|
||||
@@ -126,6 +128,11 @@ Frame *FrameFactory::createFrame(const ByteVector &data, unsigned int version) c
|
||||
}
|
||||
|
||||
Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) const
|
||||
{
|
||||
return createFrame(origData, const_cast<const Header *>(tagHeader));
|
||||
}
|
||||
|
||||
Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHeader) const
|
||||
{
|
||||
ByteVector data = origData;
|
||||
unsigned int version = tagHeader->majorVersion();
|
||||
@@ -198,8 +205,8 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
|
||||
|
||||
// Text Identification (frames 4.2)
|
||||
|
||||
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number) are in fact text frames.
|
||||
if(frameID.startsWith("T") || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN") {
|
||||
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number), GRP1 (Grouping) are in fact text frames.
|
||||
if(frameID.startsWith("T") || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN" || frameID == "GRP1") {
|
||||
|
||||
TextIdentificationFrame *f = frameID != "TXXX"
|
||||
? new TextIdentificationFrame(data, header)
|
||||
@@ -277,7 +284,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
|
||||
return f;
|
||||
}
|
||||
|
||||
// Synchronised lyrics/text (frames 4.9)
|
||||
// Synchronized lyrics/text (frames 4.9)
|
||||
|
||||
if(frameID == "SYLT") {
|
||||
SynchronizedLyricsFrame *f = new SynchronizedLyricsFrame(data, header);
|
||||
@@ -459,6 +466,7 @@ namespace
|
||||
{ "WFD", "WFED" },
|
||||
{ "MVN", "MVNM" },
|
||||
{ "MVI", "MVIN" },
|
||||
{ "GP1", "GRP1" },
|
||||
};
|
||||
const size_t frameConversion2Size = sizeof(frameConversion2) / sizeof(frameConversion2[0]);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace TagLib {
|
||||
*
|
||||
* Reimplementing this factory is the key to adding support for frame types
|
||||
* not directly supported by TagLib to your application. To do so you would
|
||||
* subclass this factory reimplement createFrame(). Then by setting your
|
||||
* subclass this factory and reimplement createFrame(). Then by setting your
|
||||
* factory to be the default factory in ID3v2::Tag constructor you can
|
||||
* implement behavior that will allow for new ID3v2::Frame subclasses (also
|
||||
* provided by you) to be used.
|
||||
@@ -74,7 +74,7 @@ namespace TagLib {
|
||||
* \deprecated Please use the method below that accepts a ID3v2::Header
|
||||
* instance in new code.
|
||||
*/
|
||||
Frame *createFrame(const ByteVector &data, bool synchSafeInts) const;
|
||||
TAGLIB_DEPRECATED Frame *createFrame(const ByteVector &data, bool synchSafeInts) const;
|
||||
|
||||
/*!
|
||||
* Create a frame based on \a data. \a version should indicate the ID3v2
|
||||
@@ -84,14 +84,19 @@ namespace TagLib {
|
||||
* \deprecated Please use the method below that accepts a ID3v2::Header
|
||||
* instance in new code.
|
||||
*/
|
||||
Frame *createFrame(const ByteVector &data, unsigned int version = 4) const;
|
||||
TAGLIB_DEPRECATED Frame *createFrame(const ByteVector &data, unsigned int version = 4) const;
|
||||
|
||||
/*!
|
||||
* \deprecated
|
||||
*/
|
||||
// BIC: remove
|
||||
Frame *createFrame(const ByteVector &data, Header *tagHeader) const;
|
||||
/*!
|
||||
* Create a frame based on \a data. \a tagHeader should be a valid
|
||||
* ID3v2::Header instance.
|
||||
*/
|
||||
// BIC: make virtual
|
||||
Frame *createFrame(const ByteVector &data, Header *tagHeader) const;
|
||||
Frame *createFrame(const ByteVector &data, const Header *tagHeader) const;
|
||||
|
||||
/*!
|
||||
* After a tag has been read, this tries to rebuild some of them
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
#include "id3v2.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
|
||||
@@ -54,6 +54,16 @@ namespace
|
||||
|
||||
const long MinPaddingSize = 1024;
|
||||
const long MaxPaddingSize = 1024 * 1024;
|
||||
|
||||
bool contains(const char **a, const ByteVector &v)
|
||||
{
|
||||
for(int i = 0; a[i]; i++)
|
||||
{
|
||||
if(v == a[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ID3v2::Tag::TagPrivate
|
||||
@@ -250,13 +260,24 @@ void ID3v2::Tag::setComment(const String &s)
|
||||
return;
|
||||
}
|
||||
|
||||
if(!d->frameListMap["COMM"].isEmpty())
|
||||
d->frameListMap["COMM"].front()->setText(s);
|
||||
else {
|
||||
CommentsFrame *f = new CommentsFrame(d->factory->defaultTextEncoding());
|
||||
addFrame(f);
|
||||
f->setText(s);
|
||||
const FrameList &comments = d->frameListMap["COMM"];
|
||||
|
||||
if(!comments.isEmpty()) {
|
||||
for(FrameList::ConstIterator it = comments.begin(); it != comments.end(); ++it) {
|
||||
CommentsFrame *frame = dynamic_cast<CommentsFrame *>(*it);
|
||||
if(frame && frame->description().isEmpty()) {
|
||||
(*it)->setText(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
comments.front()->setText(s);
|
||||
return;
|
||||
}
|
||||
|
||||
CommentsFrame *f = new CommentsFrame(d->factory->defaultTextEncoding());
|
||||
addFrame(f);
|
||||
f->setText(s);
|
||||
}
|
||||
|
||||
void ID3v2::Tag::setGenre(const String &s)
|
||||
@@ -462,19 +483,19 @@ PropertyMap ID3v2::Tag::setProperties(const PropertyMap &origProps)
|
||||
|
||||
ByteVector ID3v2::Tag::render() const
|
||||
{
|
||||
return render(4);
|
||||
return render(ID3v2::v4);
|
||||
}
|
||||
|
||||
void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const
|
||||
{
|
||||
#ifdef NO_ITUNES_HACKS
|
||||
const char *unsupportedFrames[] = {
|
||||
static const char *unsupportedFrames[] = {
|
||||
"ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG",
|
||||
"TMOO", "TPRO", "TSOA", "TSOT", "TSST", "TSOP", 0
|
||||
};
|
||||
#else
|
||||
// iTunes writes and reads TSOA, TSOT, TSOP to ID3v2.3.
|
||||
const char *unsupportedFrames[] = {
|
||||
static const char *unsupportedFrames[] = {
|
||||
"ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG",
|
||||
"TMOO", "TPRO", "TSST", 0
|
||||
};
|
||||
@@ -483,60 +504,62 @@ void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const
|
||||
ID3v2::TextIdentificationFrame *frameTDRC = 0;
|
||||
ID3v2::TextIdentificationFrame *frameTIPL = 0;
|
||||
ID3v2::TextIdentificationFrame *frameTMCL = 0;
|
||||
ID3v2::TextIdentificationFrame *frameTCON = 0;
|
||||
|
||||
for(FrameList::ConstIterator it = d->frameList.begin(); it != d->frameList.end(); it++) {
|
||||
ID3v2::Frame *frame = *it;
|
||||
ByteVector frameID = frame->header()->frameID();
|
||||
for(int i = 0; unsupportedFrames[i]; i++) {
|
||||
if(frameID == unsupportedFrames[i]) {
|
||||
debug("A frame that is not supported in ID3v2.3 \'"
|
||||
+ String(frameID) + "\' has been discarded");
|
||||
frame = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(contains(unsupportedFrames, frameID))
|
||||
{
|
||||
debug("A frame that is not supported in ID3v2.3 \'" + String(frameID) +
|
||||
"\' has been discarded");
|
||||
continue;
|
||||
}
|
||||
if(frame && frameID == "TDOR") {
|
||||
|
||||
if(frameID == "TDOR")
|
||||
frameTDOR = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
|
||||
frame = 0;
|
||||
}
|
||||
if(frame && frameID == "TDRC") {
|
||||
else if(frameID == "TDRC")
|
||||
frameTDRC = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
|
||||
frame = 0;
|
||||
}
|
||||
if(frame && frameID == "TIPL") {
|
||||
else if(frameID == "TIPL")
|
||||
frameTIPL = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
|
||||
frame = 0;
|
||||
}
|
||||
if(frame && frameID == "TMCL") {
|
||||
else if(frameID == "TMCL")
|
||||
frameTMCL = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
|
||||
frame = 0;
|
||||
}
|
||||
if(frame) {
|
||||
else if(frame && frameID == "TCON")
|
||||
frameTCON = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame);
|
||||
else
|
||||
frames->append(frame);
|
||||
}
|
||||
}
|
||||
|
||||
if(frameTDOR) {
|
||||
String content = frameTDOR->toString();
|
||||
|
||||
if(content.size() >= 4) {
|
||||
ID3v2::TextIdentificationFrame *frameTORY = new ID3v2::TextIdentificationFrame("TORY", String::Latin1);
|
||||
ID3v2::TextIdentificationFrame *frameTORY =
|
||||
new ID3v2::TextIdentificationFrame("TORY", String::Latin1);
|
||||
frameTORY->setText(content.substr(0, 4));
|
||||
frames->append(frameTORY);
|
||||
newFrames->append(frameTORY);
|
||||
}
|
||||
}
|
||||
|
||||
if(frameTDRC) {
|
||||
String content = frameTDRC->toString();
|
||||
if(content.size() >= 4) {
|
||||
ID3v2::TextIdentificationFrame *frameTYER = new ID3v2::TextIdentificationFrame("TYER", String::Latin1);
|
||||
ID3v2::TextIdentificationFrame *frameTYER =
|
||||
new ID3v2::TextIdentificationFrame("TYER", String::Latin1);
|
||||
frameTYER->setText(content.substr(0, 4));
|
||||
frames->append(frameTYER);
|
||||
newFrames->append(frameTYER);
|
||||
if(content.size() >= 10 && content[4] == '-' && content[7] == '-') {
|
||||
ID3v2::TextIdentificationFrame *frameTDAT = new ID3v2::TextIdentificationFrame("TDAT", String::Latin1);
|
||||
ID3v2::TextIdentificationFrame *frameTDAT =
|
||||
new ID3v2::TextIdentificationFrame("TDAT", String::Latin1);
|
||||
frameTDAT->setText(content.substr(8, 2) + content.substr(5, 2));
|
||||
frames->append(frameTDAT);
|
||||
newFrames->append(frameTDAT);
|
||||
if(content.size() >= 16 && content[10] == 'T' && content[13] == ':') {
|
||||
ID3v2::TextIdentificationFrame *frameTIME = new ID3v2::TextIdentificationFrame("TIME", String::Latin1);
|
||||
ID3v2::TextIdentificationFrame *frameTIME =
|
||||
new ID3v2::TextIdentificationFrame("TIME", String::Latin1);
|
||||
frameTIME->setText(content.substr(11, 2) + content.substr(14, 2));
|
||||
frames->append(frameTIME);
|
||||
newFrames->append(frameTIME);
|
||||
@@ -544,9 +567,13 @@ void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(frameTIPL || frameTMCL) {
|
||||
ID3v2::TextIdentificationFrame *frameIPLS = new ID3v2::TextIdentificationFrame("IPLS", String::Latin1);
|
||||
ID3v2::TextIdentificationFrame *frameIPLS =
|
||||
new ID3v2::TextIdentificationFrame("IPLS", String::Latin1);
|
||||
|
||||
StringList people;
|
||||
|
||||
if(frameTMCL) {
|
||||
StringList v24People = frameTMCL->fieldList();
|
||||
for(unsigned int i = 0; i + 1 < v24People.size(); i += 2) {
|
||||
@@ -561,24 +588,53 @@ void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const
|
||||
people.append(v24People[i+1]);
|
||||
}
|
||||
}
|
||||
|
||||
frameIPLS->setText(people);
|
||||
frames->append(frameIPLS);
|
||||
newFrames->append(frameIPLS);
|
||||
}
|
||||
|
||||
if(frameTCON) {
|
||||
StringList genres = frameTCON->fieldList();
|
||||
String combined;
|
||||
String genreText;
|
||||
const bool hasMultipleGenres = genres.size() > 1;
|
||||
|
||||
// If there are multiple genres, add them as multiple references to ID3v1
|
||||
// genres if such a reference exists. The first genre for which no ID3v1
|
||||
// genre number exists can be finally added as a refinement.
|
||||
for(StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it) {
|
||||
bool ok = false;
|
||||
int number = it->toInt(&ok);
|
||||
if((ok && number >= 0 && number <= 255) || *it == "RX" || *it == "CR")
|
||||
combined += '(' + *it + ')';
|
||||
else if(hasMultipleGenres && (number = ID3v1::genreIndex(*it)) != 255)
|
||||
combined += '(' + String::number(number) + ')';
|
||||
else if(genreText.isEmpty())
|
||||
genreText = *it;
|
||||
}
|
||||
if(!genreText.isEmpty())
|
||||
combined += genreText;
|
||||
|
||||
frameTCON = new ID3v2::TextIdentificationFrame("TCON", String::Latin1);
|
||||
frameTCON->setText(combined);
|
||||
frames->append(frameTCON);
|
||||
newFrames->append(frameTCON);
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ID3v2::Tag::render(int version) const
|
||||
{
|
||||
return render(version == 3 ? v3 : v4);
|
||||
}
|
||||
|
||||
ByteVector ID3v2::Tag::render(Version version) const
|
||||
{
|
||||
// We need to render the "tag data" first so that we have to correct size to
|
||||
// render in the tag's header. The "tag data" -- everything that is included
|
||||
// in ID3v2::Header::tagSize() -- includes the extended header, frames and
|
||||
// padding, but does not include the tag's header or footer.
|
||||
|
||||
if(version != 3 && version != 4) {
|
||||
debug("Unknown ID3v2 version, using ID3v2.4");
|
||||
version = 4;
|
||||
}
|
||||
|
||||
// TODO: Render the extended header.
|
||||
|
||||
// Downgrade the frames that ID3v2.3 doesn't support.
|
||||
@@ -587,7 +643,7 @@ ByteVector ID3v2::Tag::render(int version) const
|
||||
newFrames.setAutoDelete(true);
|
||||
|
||||
FrameList frameList;
|
||||
if(version == 4) {
|
||||
if(version == v4) {
|
||||
frameList = d->frameList;
|
||||
}
|
||||
else {
|
||||
@@ -601,7 +657,7 @@ ByteVector ID3v2::Tag::render(int version) const
|
||||
// Loop through the frames rendering them and adding them to the tagData.
|
||||
|
||||
for(FrameList::ConstIterator it = frameList.begin(); it != frameList.end(); it++) {
|
||||
(*it)->header()->setVersion(version);
|
||||
(*it)->header()->setVersion(version == v3 ? 3 : 4);
|
||||
if((*it)->header()->frameID().size() != 4) {
|
||||
debug("An ID3v2 frame of unsupported or unknown type \'"
|
||||
+ String((*it)->header()->frameID()) + "\' has been discarded");
|
||||
|
||||
@@ -33,21 +33,13 @@
|
||||
#include "tmap.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
#include "id3v2.h"
|
||||
#include "id3v2framefactory.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
class File;
|
||||
|
||||
//! An ID3v2 implementation
|
||||
|
||||
/*!
|
||||
* This is a relatively complete and flexible framework for working with ID3v2
|
||||
* tags.
|
||||
*
|
||||
* \see ID3v2::Tag
|
||||
*/
|
||||
|
||||
namespace ID3v2 {
|
||||
|
||||
class Header;
|
||||
@@ -201,7 +193,7 @@ namespace TagLib {
|
||||
* prone to change my mind, so this gets to stay around until near a
|
||||
* release.
|
||||
*/
|
||||
Footer *footer() const;
|
||||
TAGLIB_DEPRECATED Footer *footer() const;
|
||||
|
||||
/*!
|
||||
* Returns a reference to the frame list map. This is an FrameListMap of
|
||||
@@ -310,7 +302,7 @@ namespace TagLib {
|
||||
* - otherwise, the key "LYRICS:<description>" is used;
|
||||
* - if the frame ID is "TIPL" (involved peoples list), and if all the
|
||||
* roles defined in the frame are known in TextIdentificationFrame::involvedPeopleMap(),
|
||||
* then "<role>=<name>" will be contained in the returned obejct for each
|
||||
* then "<role>=<name>" will be contained in the returned object for each
|
||||
* - if the frame ID is "TMCL" (musician credit list), then
|
||||
* "PERFORMER:<instrument>=<name>" will be contained in the returned
|
||||
* PropertyMap for each defined musician
|
||||
@@ -345,14 +337,18 @@ namespace TagLib {
|
||||
*/
|
||||
ByteVector render() const;
|
||||
|
||||
/*!
|
||||
* \deprecated
|
||||
*/
|
||||
TAGLIB_DEPRECATED ByteVector render(int version) const;
|
||||
|
||||
/*!
|
||||
* Render the tag back to binary data, suitable to be written to disk.
|
||||
*
|
||||
* The \a version parameter specifies the version of the rendered
|
||||
* ID3v2 tag. It can be either 4 or 3.
|
||||
* The \a version parameter specifies whether ID3v2.4 (default) or ID3v2.3
|
||||
* should be used.
|
||||
*/
|
||||
// BIC: combine with the above method
|
||||
ByteVector render(int version) const;
|
||||
ByteVector render(Version version) const;
|
||||
|
||||
/*!
|
||||
* Gets the current string handler that decides how the "Latin-1" data
|
||||
@@ -396,6 +392,9 @@ namespace TagLib {
|
||||
*/
|
||||
void setTextFrame(const ByteVector &id, const String &value);
|
||||
|
||||
/*!
|
||||
* Downgrade frames from ID3v2.4 (used internally and by default) to ID3v2.3.
|
||||
*/
|
||||
void downgradeFrames(FrameList *existingFrames, FrameList *newFrames) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -109,8 +109,8 @@ bool MPEG::File::isSupported(IOStream *stream)
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true, &headerOffset);
|
||||
|
||||
if(buffer.isEmpty())
|
||||
return false;
|
||||
|
||||
return false;
|
||||
|
||||
const long originalPosition = stream->tell();
|
||||
AdapterFile file(stream);
|
||||
|
||||
@@ -200,20 +200,30 @@ bool MPEG::File::save()
|
||||
|
||||
bool MPEG::File::save(int tags)
|
||||
{
|
||||
return save(tags, true);
|
||||
return save(tags, StripOthers);
|
||||
}
|
||||
|
||||
bool MPEG::File::save(int tags, bool stripOthers)
|
||||
{
|
||||
return save(tags, stripOthers, 4);
|
||||
return save(tags, stripOthers ? StripOthers : StripNone, ID3v2::v4);
|
||||
}
|
||||
|
||||
bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version)
|
||||
{
|
||||
return save(tags, stripOthers, id3v2Version, true);
|
||||
return save(tags,
|
||||
stripOthers ? StripOthers : StripNone,
|
||||
id3v2Version == 3 ? ID3v2::v3 : ID3v2::v4);
|
||||
}
|
||||
|
||||
bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags)
|
||||
{
|
||||
return save(tags,
|
||||
stripOthers ? StripOthers : StripNone,
|
||||
id3v2Version == 3 ? ID3v2::v3 : ID3v2::v4,
|
||||
duplicateTags ? Duplicate : DoNotDuplicate);
|
||||
}
|
||||
|
||||
bool MPEG::File::save(int tags, StripTags strip, ID3v2::Version version, DuplicateTags duplicate)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("MPEG::File::save() -- File is read only.");
|
||||
@@ -222,22 +232,22 @@ bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplica
|
||||
|
||||
// Create the tags if we've been asked to.
|
||||
|
||||
if(duplicateTags) {
|
||||
if(duplicate == Duplicate) {
|
||||
|
||||
// Copy the values from the tag that does exist into the new tag,
|
||||
// except if the existing tag is to be stripped.
|
||||
|
||||
if((tags & ID3v2) && ID3v1Tag() && !(stripOthers && !(tags & ID3v1)))
|
||||
if((tags & ID3v2) && ID3v1Tag() && !(strip == StripOthers && !(tags & ID3v1)))
|
||||
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
|
||||
|
||||
if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2)))
|
||||
if((tags & ID3v1) && d->tag[ID3v2Index] && !(strip == StripOthers && !(tags & ID3v2)))
|
||||
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
|
||||
}
|
||||
|
||||
// Remove all the tags not going to be saved.
|
||||
|
||||
if(stripOthers)
|
||||
strip(~tags, false);
|
||||
if(strip == StripOthers)
|
||||
File::strip(~tags, false);
|
||||
|
||||
if(ID3v2 & tags) {
|
||||
|
||||
@@ -248,7 +258,7 @@ bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplica
|
||||
if(d->ID3v2Location < 0)
|
||||
d->ID3v2Location = 0;
|
||||
|
||||
const ByteVector data = ID3v2Tag()->render(id3v2Version);
|
||||
const ByteVector data = ID3v2Tag()->render(version);
|
||||
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
|
||||
|
||||
if(d->APELocation >= 0)
|
||||
@@ -263,7 +273,7 @@ bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplica
|
||||
|
||||
// ID3v2 tag is empty. Remove the old one.
|
||||
|
||||
strip(ID3v2, false);
|
||||
File::strip(ID3v2, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +297,7 @@ bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplica
|
||||
|
||||
// ID3v1 tag is empty. Remove the old one.
|
||||
|
||||
strip(ID3v1, false);
|
||||
File::strip(ID3v1, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +326,7 @@ bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplica
|
||||
|
||||
// APE tag is empty. Remove the old one.
|
||||
|
||||
strip(APE, false);
|
||||
File::strip(APE, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
|
||||
#include "mpegproperties.h"
|
||||
|
||||
#include "id3v2.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace ID3v2 { class Tag; class FrameFactory; }
|
||||
@@ -191,49 +193,39 @@ namespace TagLib {
|
||||
bool save(int tags);
|
||||
|
||||
/*!
|
||||
* Save the file. This will attempt to save all of the tag types that are
|
||||
* specified by OR-ing together TagTypes values. The save() method above
|
||||
* uses AllTags. This returns true if saving was successful.
|
||||
*
|
||||
* If \a stripOthers is true this strips all tags not included in the mask,
|
||||
* but does not modify them in memory, so later calls to save() which make
|
||||
* use of these tags will remain valid. This also strips empty tags.
|
||||
* \deprecated
|
||||
*/
|
||||
// BIC: combine with the above method
|
||||
bool save(int tags, bool stripOthers);
|
||||
TAGLIB_DEPRECATED bool save(int tags, bool stripOthers);
|
||||
|
||||
/*!
|
||||
* \deprecated
|
||||
*/
|
||||
// BIC: combine with the above method
|
||||
TAGLIB_DEPRECATED bool save(int tags, bool stripOthers, int id3v2Version);
|
||||
|
||||
/*!
|
||||
* \deprecated
|
||||
*/
|
||||
// BIC: combine with the above method
|
||||
TAGLIB_DEPRECATED bool save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags);
|
||||
|
||||
/*!
|
||||
* Save the file. This will attempt to save all of the tag types that are
|
||||
* specified by OR-ing together TagTypes values. The save() method above
|
||||
* uses AllTags. This returns true if saving was successful.
|
||||
* specified by OR-ing together TagTypes values.
|
||||
*
|
||||
* If \a stripOthers is true this strips all tags not included in the mask,
|
||||
* but does not modify them in memory, so later calls to save() which make
|
||||
* use of these tags will remain valid. This also strips empty tags.
|
||||
* \a strip can be set to strip all tags except those in \a tags. Those
|
||||
* tags will not be modified in memory, and thus remain valid.
|
||||
*
|
||||
* The \a id3v2Version parameter specifies the version of the saved
|
||||
* ID3v2 tag. It can be either 4 or 3.
|
||||
* \a version specifies the ID3v2 version to be used for writing tags. By
|
||||
* default, the latest standard, ID3v2.4 is used.
|
||||
*
|
||||
* If \a duplicate is set to DuplicateTags and at least one tag -- ID3v1
|
||||
* or ID3v2 -- exists this will duplicate its content into the other tag.
|
||||
*/
|
||||
// BIC: combine with the above method
|
||||
bool save(int tags, bool stripOthers, int id3v2Version);
|
||||
|
||||
/*!
|
||||
* Save the file. This will attempt to save all of the tag types that are
|
||||
* specified by OR-ing together TagTypes values. The save() method above
|
||||
* uses AllTags. This returns true if saving was successful.
|
||||
*
|
||||
* If \a stripOthers is true this strips all tags not included in the mask,
|
||||
* but does not modify them in memory, so later calls to save() which make
|
||||
* use of these tags will remain valid. This also strips empty tags.
|
||||
*
|
||||
* The \a id3v2Version parameter specifies the version of the saved
|
||||
* ID3v2 tag. It can be either 4 or 3.
|
||||
*
|
||||
* If \a duplicateTags is true and at least one tag -- ID3v1 or ID3v2 --
|
||||
* exists this will duplicate its content into the other tag.
|
||||
*/
|
||||
// BIC: combine with the above method
|
||||
bool save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags);
|
||||
bool save(int tags, StripTags strip,
|
||||
ID3v2::Version version = ID3v2::v4,
|
||||
DuplicateTags duplicate = Duplicate);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v2 tag of the file.
|
||||
@@ -325,7 +317,7 @@ namespace TagLib {
|
||||
* \see ID3v2FrameFactory
|
||||
* \deprecated This value should be passed in via the constructor
|
||||
*/
|
||||
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
TAGLIB_DEPRECATED void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
|
||||
/*!
|
||||
* Returns the position in the file of the first MPEG frame.
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Header(const ByteVector &data);
|
||||
TAGLIB_DEPRECATED Header(const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Parses an MPEG header based on \a file and \a offset.
|
||||
|
||||
@@ -200,13 +200,14 @@ void MPEG::Properties::read(File *file)
|
||||
const long lastFrameOffset = file->lastFrameOffset();
|
||||
if(lastFrameOffset < 0) {
|
||||
debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream.");
|
||||
return;
|
||||
}
|
||||
|
||||
const Header lastHeader(file, lastFrameOffset, false);
|
||||
const long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength();
|
||||
if(streamLength > 0)
|
||||
d->length = static_cast<int>(streamLength * 8.0 / d->bitrate + 0.5);
|
||||
else
|
||||
{
|
||||
const Header lastHeader(file, lastFrameOffset, false);
|
||||
const long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength();
|
||||
if (streamLength > 0)
|
||||
d->length = static_cast<int>(streamLength * 8.0 / d->bitrate + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
d->sampleRate = firstHeader.sampleRate();
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -111,8 +111,8 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated Always returns 0.
|
||||
*/
|
||||
static int xingHeaderOffset(TagLib::MPEG::Header::Version v,
|
||||
TagLib::MPEG::Header::ChannelMode c);
|
||||
TAGLIB_DEPRECATED static int xingHeaderOffset(TagLib::MPEG::Header::Version v,
|
||||
TagLib::MPEG::Header::ChannelMode c);
|
||||
|
||||
private:
|
||||
XingHeader(const XingHeader &);
|
||||
|
||||
@@ -305,4 +305,10 @@ Ogg::Page::Page(const ByteVectorList &packets,
|
||||
}
|
||||
d->packets = packets;
|
||||
d->header.setPacketSizes(packetSizes);
|
||||
|
||||
// https://xiph.org/ogg/doc/framing.html, absolute granule position:
|
||||
// A special value of '-1' (in two's complement) indicates that no packets
|
||||
// finish on this page.
|
||||
if(!lastPacketCompleted && packets.size() <= 1)
|
||||
d->header.setAbsoluteGranularPosition(-1);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated Always returns null.
|
||||
*/
|
||||
Page* getCopyWithNewPageSequenceNumber(int sequenceNumber);
|
||||
TAGLIB_DEPRECATED Page *getCopyWithNewPageSequenceNumber(int sequenceNumber);
|
||||
|
||||
/*!
|
||||
* Returns the index of the first packet wholly or partially contained in
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* A special value of containing the position of the packet to be
|
||||
* interpreted by the codec. It is only supported here so that it may be
|
||||
* coppied from one page to another.
|
||||
* copied from one page to another.
|
||||
*
|
||||
* \see absoluteGranularPosition()
|
||||
*/
|
||||
|
||||
@@ -163,8 +163,14 @@ void Opus::Properties::read(File *file)
|
||||
|
||||
if(frameCount > 0) {
|
||||
const double length = frameCount * 1000.0 / 48000.0;
|
||||
long fileLengthWithoutOverhead = file->length();
|
||||
// Ignore the two mandatory header packets, see "3. Packet Organization"
|
||||
// in https://tools.ietf.org/html/rfc7845.html
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
fileLengthWithoutOverhead -= file->packet(i).size();
|
||||
}
|
||||
d->length = static_cast<int>(length + 0.5);
|
||||
d->bitrate = static_cast<int>(file->length() * 8.0 / length + 0.5);
|
||||
d->bitrate = static_cast<int>(fileLengthWithoutOverhead * 8.0 / length + 0.5);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -131,6 +131,7 @@ void Speex::File::read(bool readProperties)
|
||||
|
||||
if(!speexHeaderData.startsWith("Speex ")) {
|
||||
debug("Speex::File::read() -- invalid Speex identification header");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -182,8 +182,14 @@ void Speex::Properties::read(File *file)
|
||||
|
||||
if(frameCount > 0) {
|
||||
const double length = frameCount * 1000.0 / d->sampleRate;
|
||||
long fileLengthWithoutOverhead = file->length();
|
||||
// Ignore the two header packets, see "Ogg file format" in
|
||||
// https://www.speex.org/docs/manual/speex-manual/node8.html
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
fileLengthWithoutOverhead -= file->packet(i).size();
|
||||
}
|
||||
d->length = static_cast<int>(length + 0.5);
|
||||
d->bitrate = static_cast<int>(file->length() * 8.0 / length + 0.5);
|
||||
d->bitrate = static_cast<int>(fileLengthWithoutOverhead * 8.0 / length + 0.5);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -186,9 +186,14 @@ void Vorbis::Properties::read(File *file)
|
||||
|
||||
if(frameCount > 0) {
|
||||
const double length = frameCount * 1000.0 / d->sampleRate;
|
||||
|
||||
long fileLengthWithoutOverhead = file->length();
|
||||
// Ignore the three initial header packets, see "1.3.1. Decode Setup" in
|
||||
// https://xiph.org/vorbis/doc/Vorbis_I_spec.html
|
||||
for (unsigned int i = 0; i < 3; ++i) {
|
||||
fileLengthWithoutOverhead -= file->packet(i).size();
|
||||
}
|
||||
d->length = static_cast<int>(length + 0.5);
|
||||
d->bitrate = static_cast<int>(file->length() * 8.0 / length + 0.5);
|
||||
d->bitrate = static_cast<int>(fileLengthWithoutOverhead * 8.0 / length + 0.5);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace TagLib {
|
||||
* \deprecated Using this method may lead to a linkage error.
|
||||
*/
|
||||
// BIC: remove and merge with below
|
||||
void removeField(const String &key, const String &value = String::null);
|
||||
TAGLIB_DEPRECATED void removeField(const String &key, const String &value = String());
|
||||
|
||||
/*!
|
||||
* Remove all the fields specified by \a key.
|
||||
|
||||
@@ -117,6 +117,11 @@ RIFF::AIFF::Properties *RIFF::AIFF::File::audioProperties() const
|
||||
}
|
||||
|
||||
bool RIFF::AIFF::File::save()
|
||||
{
|
||||
return save(ID3v2::v4);
|
||||
}
|
||||
|
||||
bool RIFF::AIFF::File::save(ID3v2::Version version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("RIFF::AIFF::File::save() -- File is read only.");
|
||||
@@ -135,7 +140,7 @@ bool RIFF::AIFF::File::save()
|
||||
}
|
||||
|
||||
if(tag() && !tag()->isEmpty()) {
|
||||
setChunkData("ID3 ", d->tag->render());
|
||||
setChunkData("ID3 ", d->tag->render(version));
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,6 +119,11 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Save using a specific ID3v2 version (e.g. v3)
|
||||
*/
|
||||
bool save(ID3v2::Version version);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Properties(const ByteVector &data, ReadStyle style);
|
||||
TAGLIB_DEPRECATED Properties(const ByteVector &data, ReadStyle style);
|
||||
|
||||
/*!
|
||||
* Create an instance of AIFF::Properties with the data read from the
|
||||
@@ -73,7 +73,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -119,7 +119,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
int sampleWidth() const;
|
||||
TAGLIB_DEPRECATED int sampleWidth() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of sample frames
|
||||
|
||||
@@ -302,13 +302,11 @@ void RIFF::File::read()
|
||||
|
||||
if(!isValidChunkName(chunkName)) {
|
||||
debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(offset) + 8 + chunkSize > length()) {
|
||||
debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -325,9 +323,20 @@ void RIFF::File::read()
|
||||
if(offset & 1) {
|
||||
seek(offset);
|
||||
const ByteVector iByte = readBlock(1);
|
||||
if(iByte.size() == 1 && iByte[0] == '\0') {
|
||||
chunk.padding = 1;
|
||||
offset++;
|
||||
if(iByte.size() == 1) {
|
||||
bool skipPadding = iByte[0] == '\0';
|
||||
if(!skipPadding) {
|
||||
// Padding byte is not zero, check if it is good to ignore it
|
||||
const ByteVector fourCcAfterPadding = readBlock(4);
|
||||
if(isValidChunkName(fourCcAfterPadding)) {
|
||||
// Use the padding, it is followed by a valid chunk name.
|
||||
skipPadding = true;
|
||||
}
|
||||
}
|
||||
if(skipPadding) {
|
||||
chunk.padding = 1;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,13 @@ bool RIFF::WAV::File::save()
|
||||
}
|
||||
|
||||
bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version)
|
||||
{
|
||||
return save(tags,
|
||||
stripOthers ? StripOthers : StripNone,
|
||||
id3v2Version == 3 ? ID3v2::v3 : ID3v2::v4);
|
||||
}
|
||||
|
||||
bool RIFF::WAV::File::save(TagTypes tags, StripTags strip, ID3v2::Version version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("RIFF::WAV::File::save() -- File is read only.");
|
||||
@@ -162,14 +169,14 @@ bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(stripOthers)
|
||||
strip(static_cast<TagTypes>(AllTags & ~tags));
|
||||
if(strip == StripOthers)
|
||||
File::strip(static_cast<TagTypes>(AllTags & ~tags));
|
||||
|
||||
if(tags & ID3v2) {
|
||||
removeTagChunks(ID3v2);
|
||||
|
||||
if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
|
||||
setChunkData("ID3 ", ID3v2Tag()->render(id3v2Version));
|
||||
setChunkData("ID3 ", ID3v2Tag()->render(version));
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,19 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
bool save(TagTypes tags, bool stripOthers = true, int id3v2Version = 4);
|
||||
/*!
|
||||
* \deprecated
|
||||
*/
|
||||
TAGLIB_DEPRECATED bool save(TagTypes tags, bool stripOthers, int id3v2Version = 4);
|
||||
|
||||
/*!
|
||||
* 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(TagTypes tags, StripTags strip = StripOthers,
|
||||
ID3v2::Version version = ID3v2::v4);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
|
||||
@@ -35,7 +35,8 @@ namespace
|
||||
enum WaveFormat
|
||||
{
|
||||
FORMAT_UNKNOWN = 0x0000,
|
||||
FORMAT_PCM = 0x0001
|
||||
FORMAT_PCM = 0x0001,
|
||||
FORMAT_IEEE_FLOAT = 0x0003
|
||||
};
|
||||
}
|
||||
|
||||
@@ -183,7 +184,15 @@ void RIFF::WAV::Properties::read(File *file)
|
||||
}
|
||||
|
||||
d->format = data.toShort(0, false);
|
||||
if(d->format != FORMAT_PCM && totalSamples == 0) {
|
||||
if((d->format & 0xffff) == 0xfffe) {
|
||||
// if extensible then read the format from the subformat
|
||||
if(data.size() != 40) {
|
||||
debug("RIFF::WAV::Properties::read() - extensible size incorrect");
|
||||
return;
|
||||
}
|
||||
d->format = data.toShort(24, false);
|
||||
}
|
||||
if(d->format != FORMAT_PCM && d->format != FORMAT_IEEE_FLOAT && totalSamples == 0) {
|
||||
debug("RIFF::WAV::Properties::read() - Non-PCM format, but 'fact' chunk not found.");
|
||||
return;
|
||||
}
|
||||
@@ -192,7 +201,7 @@ void RIFF::WAV::Properties::read(File *file)
|
||||
d->sampleRate = data.toUInt(4, false);
|
||||
d->bitsPerSample = data.toShort(14, false);
|
||||
|
||||
if(d->format != FORMAT_PCM)
|
||||
if(d->format != FORMAT_PCM && !(d->format == FORMAT_IEEE_FLOAT && totalSamples == 0))
|
||||
d->sampleFrames = totalSamples;
|
||||
else if(d->channels > 0 && d->bitsPerSample > 0)
|
||||
d->sampleFrames = streamLength / (d->channels * ((d->bitsPerSample + 7) / 8));
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Properties(const ByteVector &data, ReadStyle style);
|
||||
TAGLIB_DEPRECATED Properties(const ByteVector &data, ReadStyle style);
|
||||
|
||||
/*!
|
||||
* Create an instance of WAV::Properties with the data read from the
|
||||
@@ -63,7 +63,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Properties(const ByteVector &data, unsigned int streamLength, ReadStyle style);
|
||||
TAGLIB_DEPRECATED Properties(const ByteVector &data, unsigned int streamLength, ReadStyle style);
|
||||
|
||||
/*!
|
||||
* Create an instance of WAV::Properties with the data read from the
|
||||
@@ -84,7 +84,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -130,7 +130,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
int sampleWidth() const;
|
||||
TAGLIB_DEPRECATED int sampleWidth() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of sample frames.
|
||||
|
||||
@@ -183,7 +183,7 @@ void S3M::File::read(bool)
|
||||
// "ultra click" and "use panning values" (if == 0xFC).
|
||||
// I don't see them in any spec, though.
|
||||
// Hm, but there is "UltraClick-removal" and some other
|
||||
// variables in ScreamTracker IIIs GUI.
|
||||
// variables in ScreamTracker III's GUI.
|
||||
|
||||
seek(12, Current);
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
#include "taglib_config.h"
|
||||
|
||||
#define TAGLIB_MAJOR_VERSION 1
|
||||
#define TAGLIB_MINOR_VERSION 11
|
||||
#define TAGLIB_PATCH_VERSION 1
|
||||
#define TAGLIB_MINOR_VERSION 12
|
||||
#define TAGLIB_PATCH_VERSION 0
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) || defined(__clang__)
|
||||
#define TAGLIB_IGNORE_MISSING_DESTRUCTOR _Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"")
|
||||
@@ -44,6 +44,16 @@
|
||||
#define TAGLIB_CONSTRUCT_BITSET(x) static_cast<unsigned long>(x)
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201402
|
||||
#define TAGLIB_DEPRECATED [[deprecated]]
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define TAGLIB_DEPRECATED __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define TAGLIB_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
#define TAGLIB_DEPRECATED
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
//! A namespace for all TagLib related classes and functions
|
||||
|
||||
@@ -281,7 +281,7 @@ namespace TagLib {
|
||||
* \deprecated
|
||||
*/
|
||||
// BIC: remove
|
||||
bool isNull() const;
|
||||
TAGLIB_DEPRECATED bool isNull() const;
|
||||
|
||||
/*!
|
||||
* Returns true if the ByteVector is empty.
|
||||
@@ -595,7 +595,7 @@ namespace TagLib {
|
||||
* \deprecated
|
||||
*/
|
||||
// BIC: remove
|
||||
static ByteVector null;
|
||||
TAGLIB_DEPRECATED static ByteVector null;
|
||||
|
||||
/*!
|
||||
* Returns a hex-encoded copy of the byte vector.
|
||||
|
||||
@@ -63,8 +63,6 @@
|
||||
#include "itfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "mp4file.h"
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -150,10 +148,6 @@ PropertyMap File::properties() const
|
||||
return dynamic_cast<const MP4::File* >(this)->properties();
|
||||
if(dynamic_cast<const ASF::File* >(this))
|
||||
return dynamic_cast<const ASF::File* >(this)->properties();
|
||||
if(dynamic_cast<const DSF::File* >(this))
|
||||
return dynamic_cast<const DSF::File* >(this)->properties();
|
||||
if(dynamic_cast<const DSDIFF::File* >(this))
|
||||
return dynamic_cast<const DSDIFF::File* >(this)->properties();
|
||||
return tag()->properties();
|
||||
}
|
||||
|
||||
@@ -183,10 +177,6 @@ void File::removeUnsupportedProperties(const StringList &properties)
|
||||
dynamic_cast<MP4::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if(dynamic_cast<ASF::File* >(this))
|
||||
dynamic_cast<ASF::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if(dynamic_cast<DSF::File* >(this))
|
||||
dynamic_cast<DSF::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if(dynamic_cast<DSDIFF::File* >(this))
|
||||
dynamic_cast<DSDIFF::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else
|
||||
tag()->removeUnsupportedProperties(properties);
|
||||
}
|
||||
@@ -229,10 +219,6 @@ PropertyMap File::setProperties(const PropertyMap &properties)
|
||||
return dynamic_cast<MP4::File* >(this)->setProperties(properties);
|
||||
else if(dynamic_cast<ASF::File* >(this))
|
||||
return dynamic_cast<ASF::File* >(this)->setProperties(properties);
|
||||
else if(dynamic_cast<DSF::File* >(this))
|
||||
return dynamic_cast<DSF::File* >(this)->setProperties(properties);
|
||||
else if(dynamic_cast<DSDIFF::File* >(this))
|
||||
return dynamic_cast<DSDIFF::File* >(this)->setProperties(properties);
|
||||
else
|
||||
return tag()->setProperties(properties);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,23 @@ namespace TagLib {
|
||||
End
|
||||
};
|
||||
|
||||
/*!
|
||||
* Specify which tags to strip either explicitly, or on save.
|
||||
*/
|
||||
enum StripTags {
|
||||
StripNone, //<! Don't strip any tags
|
||||
StripOthers //<! Strip all tags not explicitly referenced in method call
|
||||
};
|
||||
|
||||
/*!
|
||||
* Used to specify if when saving files, if values between different tag
|
||||
* types should be synchronized.
|
||||
*/
|
||||
enum DuplicateTags {
|
||||
Duplicate, //<! Synchronize values between different tag types
|
||||
DoNotDuplicate //<! Do not synchronize values between different tag types
|
||||
};
|
||||
|
||||
/*!
|
||||
* Destroys this File instance.
|
||||
*/
|
||||
@@ -246,14 +263,14 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
static bool isReadable(const char *file);
|
||||
TAGLIB_DEPRECATED static bool isReadable(const char *file);
|
||||
|
||||
/*!
|
||||
* Returns true if \a file can be opened for writing.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
static bool isWritable(const char *name);
|
||||
TAGLIB_DEPRECATED static bool isWritable(const char *name);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
|
||||
@@ -264,7 +264,7 @@ void FileStream::insert(const ByteVector &data, unsigned long start, unsigned lo
|
||||
// Now I'll explain the steps in this ugliness:
|
||||
|
||||
// First, make sure that we're working with a buffer that is longer than
|
||||
// the *differnce* in the tag sizes. We want to avoid overwriting parts
|
||||
// the *difference* in the tag sizes. We want to avoid overwriting parts
|
||||
// that aren't yet in memory, so this is necessary.
|
||||
|
||||
unsigned long bufferLength = bufferSize();
|
||||
@@ -493,9 +493,10 @@ void FileStream::truncate(long length)
|
||||
|
||||
#else
|
||||
|
||||
fflush(d->file);
|
||||
const int error = ftruncate(fileno(d->file), length);
|
||||
if(error != 0)
|
||||
debug("FileStream::truncate() -- Coundn't truncate the file.");
|
||||
debug("FileStream::truncate() -- Couldn't truncate the file.");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace TagLib {
|
||||
* - ALBUMSORT
|
||||
* - ARTISTSORT
|
||||
* - ALBUMARTISTSORT
|
||||
* - COMPOSERSORT
|
||||
*
|
||||
* Credits:
|
||||
*
|
||||
@@ -75,7 +76,7 @@ namespace TagLib {
|
||||
* - LYRICIST
|
||||
* - CONDUCTOR
|
||||
* - REMIXER
|
||||
* - PERFORMER:<XXXX>
|
||||
* - PERFORMER:\<XXXX>
|
||||
*
|
||||
* Other tags:
|
||||
*
|
||||
@@ -90,12 +91,16 @@ namespace TagLib {
|
||||
* - LABEL
|
||||
* - CATALOGNUMBER
|
||||
* - BARCODE
|
||||
* - RELEASECOUNTRY
|
||||
* - RELEASESTATUS
|
||||
* - RELEASETYPE
|
||||
*
|
||||
* MusicBrainz identifiers:
|
||||
*
|
||||
* - MUSICBRAINZ_TRACKID
|
||||
* - MUSICBRAINZ_ALBUMID
|
||||
* - MUSICBRAINZ_RELEASEGROUPID
|
||||
* - MUSICBRAINZ_RELEASETRACKID
|
||||
* - MUSICBRAINZ_WORKID
|
||||
* - MUSICBRAINZ_ARTISTID
|
||||
* - MUSICBRAINZ_ALBUMARTISTID
|
||||
@@ -199,12 +204,12 @@ namespace TagLib {
|
||||
StringList &operator[](const String &key);
|
||||
|
||||
/*!
|
||||
* Returns true if and only if \other has the same contents as this map.
|
||||
* Returns true if and only if \a other has the same contents as this map.
|
||||
*/
|
||||
bool operator==(const PropertyMap &other) const;
|
||||
|
||||
/*!
|
||||
* Returns false if and only \other has the same contents as this map.
|
||||
* Returns false if and only \a other has the same contents as this map.
|
||||
*/
|
||||
bool operator!=(const PropertyMap &other) const;
|
||||
|
||||
|
||||
@@ -29,12 +29,7 @@
|
||||
|
||||
#include "trefcounter.h"
|
||||
|
||||
#if defined(HAVE_STD_ATOMIC)
|
||||
# include <atomic>
|
||||
# define ATOMIC_INT std::atomic_int
|
||||
# define ATOMIC_INC(x) (++x)
|
||||
# define ATOMIC_DEC(x) (--x)
|
||||
#elif defined(HAVE_GCC_ATOMIC)
|
||||
#if defined(HAVE_GCC_ATOMIC)
|
||||
# define ATOMIC_INT int
|
||||
# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
|
||||
# define ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
|
||||
@@ -57,7 +52,7 @@
|
||||
# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
|
||||
# define ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
|
||||
#else
|
||||
# define ATOMIC_INT int
|
||||
# define ATOMIC_INT volatile int
|
||||
# define ATOMIC_INC(x) (++x)
|
||||
# define ATOMIC_DEC(x) (--x)
|
||||
#endif
|
||||
|
||||
@@ -340,7 +340,7 @@ namespace TagLib {
|
||||
* \deprecated
|
||||
*/
|
||||
// BIC: remove
|
||||
bool isNull() const;
|
||||
TAGLIB_DEPRECATED bool isNull() const;
|
||||
|
||||
/*!
|
||||
* Returns a ByteVector containing the string's data. If \a t is Latin1 or
|
||||
@@ -525,7 +525,7 @@ namespace TagLib {
|
||||
* \deprecated
|
||||
*/
|
||||
// BIC: remove
|
||||
static String null;
|
||||
TAGLIB_DEPRECATED static String null;
|
||||
|
||||
protected:
|
||||
/*!
|
||||
@@ -541,7 +541,7 @@ namespace TagLib {
|
||||
* may lead to a linkage error.
|
||||
*/
|
||||
// BIC: remove
|
||||
static const Type WCharByteOrder;
|
||||
TAGLIB_DEPRECATED static const Type WCharByteOrder;
|
||||
|
||||
class StringPrivate;
|
||||
StringPrivate *d;
|
||||
|
||||
@@ -57,7 +57,7 @@ ByteVector zlib::decompress(const ByteVector &data)
|
||||
z_stream stream = {};
|
||||
|
||||
if(inflateInit(&stream) != Z_OK) {
|
||||
debug("zlib::decompress() - Failed to initizlize zlib.");
|
||||
debug("zlib::decompress() - Failed to initialize zlib.");
|
||||
return ByteVector();
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace TagLib {
|
||||
* \see ID3v2FrameFactory
|
||||
* \deprecated This value should be passed in via the constructor
|
||||
*/
|
||||
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
TAGLIB_DEPRECATED void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
|
||||
/*!
|
||||
* Saves the file.
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -263,7 +263,7 @@ void WavPack::File::read(bool readProperties)
|
||||
d->APELocation = d->APELocation + APE::Footer::size() - d->APESize;
|
||||
}
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
if(d->ID3v1Location < 0)
|
||||
APETag(true);
|
||||
|
||||
// Look for WavPack audio properties
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
@@ -138,16 +139,10 @@ unsigned int WavPack::Properties::sampleFrames() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace
|
||||
{
|
||||
const unsigned int sample_rates[] = {
|
||||
6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
|
||||
32000, 44100, 48000, 64000, 88200, 96000, 192000, 0 };
|
||||
}
|
||||
|
||||
#define BYTES_STORED 3
|
||||
#define MONO_FLAG 4
|
||||
#define LOSSLESS_FLAG 8
|
||||
#define HYBRID_FLAG 8
|
||||
#define DSD_FLAG 0x80000000 // block is encoded DSD (1-bit PCM)
|
||||
|
||||
#define SHIFT_LSB 13
|
||||
#define SHIFT_MASK (0x1fL << SHIFT_LSB)
|
||||
@@ -158,8 +153,110 @@ namespace
|
||||
#define MIN_STREAM_VERS 0x402
|
||||
#define MAX_STREAM_VERS 0x410
|
||||
|
||||
#define INITIAL_BLOCK 0x800
|
||||
#define FINAL_BLOCK 0x1000
|
||||
|
||||
#define ID_DSD_BLOCK 0x0e
|
||||
#define ID_OPTIONAL_DATA 0x20
|
||||
#define ID_UNIQUE 0x3f
|
||||
#define ID_ODD_SIZE 0x40
|
||||
#define ID_LARGE 0x80
|
||||
#define ID_SAMPLE_RATE (ID_OPTIONAL_DATA | 0x7)
|
||||
|
||||
namespace
|
||||
{
|
||||
const unsigned int sampleRates[] = {
|
||||
6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
|
||||
32000, 44100, 48000, 64000, 88200, 96000, 192000, 0 };
|
||||
|
||||
/*!
|
||||
* Given a WavPack \a block (complete, but not including the 32-byte header),
|
||||
* parse the metadata blocks until an \a id block is found and return the
|
||||
* contained data, or zero if no such block is found.
|
||||
* Supported values for \a id are ID_SAMPLE_RATE and ID_DSD_BLOCK.
|
||||
*/
|
||||
int getMetaDataChunk(const ByteVector &block, unsigned char id)
|
||||
{
|
||||
if(id != ID_SAMPLE_RATE && id != ID_DSD_BLOCK)
|
||||
return 0;
|
||||
|
||||
const int blockSize = static_cast<int>(block.size());
|
||||
int index = 0;
|
||||
|
||||
while(index + 1 < blockSize) {
|
||||
const unsigned char metaId = static_cast<unsigned char>(block[index]);
|
||||
int metaBc = static_cast<unsigned char>(block[index + 1]) << 1;
|
||||
index += 2;
|
||||
|
||||
if(metaId & ID_LARGE) {
|
||||
if(index + 2 > blockSize)
|
||||
return 0;
|
||||
|
||||
metaBc += (static_cast<uint32_t>(static_cast<unsigned char>(block[index])) << 9)
|
||||
+ (static_cast<uint32_t>(static_cast<unsigned char>(block[index + 1])) << 17);
|
||||
index += 2;
|
||||
}
|
||||
|
||||
if(index + metaBc > blockSize)
|
||||
return 0;
|
||||
|
||||
// if we got a sample rate, return it
|
||||
|
||||
if(id == ID_SAMPLE_RATE && (metaId & ID_UNIQUE) == ID_SAMPLE_RATE && metaBc == 4) {
|
||||
int sampleRate = static_cast<int32_t>(static_cast<unsigned char>(block[index]));
|
||||
sampleRate |= static_cast<int32_t>(static_cast<unsigned char>(block[index + 1])) << 8;
|
||||
sampleRate |= static_cast<int32_t>(static_cast<unsigned char>(block[index + 2])) << 16;
|
||||
|
||||
// only use 4th byte if it's really there
|
||||
|
||||
if(!(metaId & ID_ODD_SIZE))
|
||||
sampleRate |= static_cast<int32_t>(static_cast<unsigned char>(block[index + 3]) & 0x7f) << 24;
|
||||
|
||||
return sampleRate;
|
||||
}
|
||||
|
||||
// if we got DSD block, return the specified rate shift amount
|
||||
|
||||
if(id == ID_DSD_BLOCK && (metaId & ID_UNIQUE) == ID_DSD_BLOCK && metaBc > 0) {
|
||||
const unsigned char rateShift = static_cast<unsigned char>(block[index]);
|
||||
if(rateShift <= 31)
|
||||
return rateShift;
|
||||
}
|
||||
|
||||
index += metaBc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Given a WavPack block (complete, but not including the 32-byte header),
|
||||
* parse the metadata blocks until an ID_SAMPLE_RATE block is found and
|
||||
* return the non-standard sample rate contained there, or zero if no such
|
||||
* block is found.
|
||||
*/
|
||||
int getNonStandardRate(const ByteVector &block)
|
||||
{
|
||||
return getMetaDataChunk(block, ID_SAMPLE_RATE);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Given a WavPack block (complete, but not including the 32-byte header),
|
||||
* parse the metadata blocks until a DSD audio data block is found and return
|
||||
* the sample-rate shift value contained there, or zero if no such block is
|
||||
* found. The nominal sample rate of DSD audio files (found in the header)
|
||||
* must be left-shifted by this amount to get the actual "byte" sample rate.
|
||||
* Note that 8-bit bytes are the "atoms" of the DSD audio coding (for
|
||||
* decoding, seeking, etc), so the shifted rate must be further multiplied by
|
||||
* 8 to get the actual DSD bit sample rate.
|
||||
*/
|
||||
int getDsdRateShifter(const ByteVector &block)
|
||||
{
|
||||
return getMetaDataChunk(block, ID_DSD_BLOCK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WavPack::Properties::read(File *file, long streamLength)
|
||||
{
|
||||
long offset = 0;
|
||||
@@ -178,17 +275,50 @@ void WavPack::Properties::read(File *file, long streamLength)
|
||||
break;
|
||||
}
|
||||
|
||||
const unsigned int blockSize = data.toUInt(4, false);
|
||||
const unsigned int sampleFrames = data.toUInt(12, false);
|
||||
const unsigned int blockSamples = data.toUInt(20, false);
|
||||
const unsigned int flags = data.toUInt(24, false);
|
||||
unsigned int sampleRate = sampleRates[(flags & SRATE_MASK) >> SRATE_LSB];
|
||||
|
||||
if(offset == 0) {
|
||||
if(!blockSamples) { // ignore blocks with no samples
|
||||
offset += blockSize + 8;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(blockSize < 24 || blockSize > 1048576) {
|
||||
debug("WavPack::Properties::read() -- Invalid block header found.");
|
||||
break;
|
||||
}
|
||||
|
||||
// For non-standard sample rates or DSD audio files, we must read and parse the block
|
||||
// to actually determine the sample rate.
|
||||
|
||||
if(!sampleRate || (flags & DSD_FLAG)) {
|
||||
const unsigned int adjustedBlockSize = blockSize - 24;
|
||||
const ByteVector block = file->readBlock(adjustedBlockSize);
|
||||
|
||||
if(block.size() < adjustedBlockSize) {
|
||||
debug("WavPack::Properties::read() -- block is too short.");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!sampleRate)
|
||||
sampleRate = static_cast<unsigned int>(getNonStandardRate(block));
|
||||
|
||||
if(sampleRate && (flags & DSD_FLAG))
|
||||
sampleRate <<= getDsdRateShifter(block);
|
||||
}
|
||||
|
||||
if(flags & INITIAL_BLOCK) {
|
||||
d->version = data.toShort(8, false);
|
||||
if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS)
|
||||
break;
|
||||
|
||||
d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - ((flags & SHIFT_MASK) >> SHIFT_LSB);
|
||||
d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB];
|
||||
d->lossless = !(flags & LOSSLESS_FLAG);
|
||||
d->sampleFrames = data.toUInt(12, false);
|
||||
d->sampleRate = static_cast<int>(sampleRate);
|
||||
d->lossless = !(flags & HYBRID_FLAG);
|
||||
d->sampleFrames = sampleFrames;
|
||||
}
|
||||
|
||||
d->channels += (flags & MONO_FLAG) ? 1 : 2;
|
||||
@@ -196,7 +326,6 @@ void WavPack::Properties::read(File *file, long streamLength)
|
||||
if(flags & FINAL_BLOCK)
|
||||
break;
|
||||
|
||||
const unsigned int blockSize = data.toUInt(4, false);
|
||||
offset += blockSize + 8;
|
||||
}
|
||||
|
||||
@@ -212,25 +341,34 @@ void WavPack::Properties::read(File *file, long streamLength)
|
||||
|
||||
unsigned int WavPack::Properties::seekFinalIndex(File *file, long streamLength)
|
||||
{
|
||||
const long offset = file->rfind("wvpk", streamLength);
|
||||
if(offset == -1)
|
||||
return 0;
|
||||
long offset = streamLength;
|
||||
|
||||
file->seek(offset);
|
||||
const ByteVector data = file->readBlock(32);
|
||||
if(data.size() < 32)
|
||||
return 0;
|
||||
while (offset >= 32) {
|
||||
offset = file->rfind("wvpk", offset - 4);
|
||||
|
||||
const int version = data.toShort(8, false);
|
||||
if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS)
|
||||
return 0;
|
||||
if(offset == -1)
|
||||
return 0;
|
||||
|
||||
const unsigned int flags = data.toUInt(24, false);
|
||||
if(!(flags & FINAL_BLOCK))
|
||||
return 0;
|
||||
file->seek(offset);
|
||||
const ByteVector data = file->readBlock(32);
|
||||
if(data.size() < 32)
|
||||
return 0;
|
||||
|
||||
const unsigned int blockIndex = data.toUInt(16, false);
|
||||
const unsigned int blockSamples = data.toUInt(20, false);
|
||||
const unsigned int blockSize = data.toUInt(4, false);
|
||||
const unsigned int blockIndex = data.toUInt(16, false);
|
||||
const unsigned int blockSamples = data.toUInt(20, false);
|
||||
const unsigned int flags = data.toUInt(24, false);
|
||||
const int version = data.toShort(8, false);
|
||||
|
||||
return blockIndex + blockSamples;
|
||||
// try not to trigger on a spurious "wvpk" in WavPack binary block data
|
||||
|
||||
if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS || (blockSize & 1) ||
|
||||
blockSize < 24 || blockSize >= 1048576 || blockSamples > 131072)
|
||||
continue;
|
||||
|
||||
if (blockSamples && (flags & FINAL_BLOCK))
|
||||
return blockIndex + blockSamples;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user