mirror of
https://github.com/taglib/taglib.git
synced 2026-06-07 06:50:32 -04:00
Compare commits
251 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95776b5905 | ||
|
|
dcfb71bcb4 | ||
|
|
f2c9ed4f36 | ||
|
|
2407933087 | ||
|
|
2af43ec8bf | ||
|
|
85d76a2428 | ||
|
|
5a4a05d9bc | ||
|
|
a2188e6cf9 | ||
|
|
e6f9a06894 | ||
|
|
e125bcb78b | ||
|
|
23b418b4e9 | ||
|
|
c12b6697f9 | ||
|
|
89fcab5669 | ||
|
|
35ca010df6 | ||
|
|
0f58646bfb | ||
|
|
4d126c49e9 | ||
|
|
3a636c752b | ||
|
|
ff5ab030c5 | ||
|
|
ff3b1466e1 | ||
|
|
89cb785f22 | ||
|
|
30f62ba887 | ||
|
|
1503909824 | ||
|
|
75b685fa53 | ||
|
|
62d55223b2 | ||
|
|
418a6c79cb | ||
|
|
ab417fd9e3 | ||
|
|
35cdcd3b95 | ||
|
|
a845f70c49 | ||
|
|
7e866e11ad | ||
|
|
6acbcfc68a | ||
|
|
02c81aaac2 | ||
|
|
550510ff3f | ||
|
|
5051010835 | ||
|
|
fdb8a6b065 | ||
|
|
6d4299ea94 | ||
|
|
b84b3afc9c | ||
|
|
451616f99a | ||
|
|
496b58e0c9 | ||
|
|
886236b978 | ||
|
|
6d2e0e8050 | ||
|
|
2f29ed003c | ||
|
|
12953b3fdc | ||
|
|
448648d61b | ||
|
|
241465eaac | ||
|
|
dce00b96b8 | ||
|
|
3b2d620671 | ||
|
|
e18546560e | ||
|
|
fc9abc7a33 | ||
|
|
98d010f460 | ||
|
|
36d7f9ba32 | ||
|
|
c4c5b06643 | ||
|
|
767a6ec4a2 | ||
|
|
860a605c8d | ||
|
|
df5bf232eb | ||
|
|
90a4bae6cc | ||
|
|
3ae452ee2a | ||
|
|
bc2d3ea72e | ||
|
|
9ccc4878d0 | ||
|
|
6365f36c75 | ||
|
|
da3d9b1c55 | ||
|
|
f77a84486e | ||
|
|
cf9f2a436b | ||
|
|
e73afa3325 | ||
|
|
dfee045d46 | ||
|
|
93f304a91d | ||
|
|
ddabffc7ef | ||
|
|
42d268c2c9 | ||
|
|
e7c0f3322d | ||
|
|
b3f3eeeec7 | ||
|
|
54fbe15611 | ||
|
|
d7523d7843 | ||
|
|
ca801c5411 | ||
|
|
4667ba02e5 | ||
|
|
3151336050 | ||
|
|
848f8c316e | ||
|
|
2303da48a2 | ||
|
|
bbec1c7f81 | ||
|
|
79f3edebc0 | ||
|
|
d49d0a6888 | ||
|
|
5c3f096fe4 | ||
|
|
7060d53cf3 | ||
|
|
dcf11b9586 | ||
|
|
229d69864d | ||
|
|
36d9fc1973 | ||
|
|
d06f480f82 | ||
|
|
f9efcfb8d6 | ||
|
|
db06166330 | ||
|
|
5d8f781467 | ||
|
|
03adafbfd9 | ||
|
|
198530547d | ||
|
|
4b6745b59b | ||
|
|
6f0bf734d5 | ||
|
|
23bd3784a1 | ||
|
|
8f8ef3788f | ||
|
|
b0938a3cf1 | ||
|
|
a2688a1ff0 | ||
|
|
d6fc2ef4aa | ||
|
|
9f29804f60 | ||
|
|
289b6abb43 | ||
|
|
55f1224d6e | ||
|
|
395743eb49 | ||
|
|
62827269b6 | ||
|
|
d2273a7218 | ||
|
|
bd85cf8928 | ||
|
|
f3cbb883f2 | ||
|
|
a80222efa5 | ||
|
|
5e13e0c838 | ||
|
|
58765ac40a | ||
|
|
6e35e56d7f | ||
|
|
a9cdbb7e75 | ||
|
|
8c71428d4f | ||
|
|
2c85b4d178 | ||
|
|
3293cee11e | ||
|
|
27990d0623 | ||
|
|
986ee3c44a | ||
|
|
dfb3962511 | ||
|
|
3e89f7cb40 | ||
|
|
8c233f4552 | ||
|
|
c2896fd629 | ||
|
|
49b07a2662 | ||
|
|
529d78f54b | ||
|
|
dc89bdd3f0 | ||
|
|
81a9f0474d | ||
|
|
8a7d1dd796 | ||
|
|
457b1abac8 | ||
|
|
e8498b9264 | ||
|
|
3eeff8b933 | ||
|
|
d959ab89f1 | ||
|
|
b081fb2833 | ||
|
|
e0805b039c | ||
|
|
c352b5e0c7 | ||
|
|
a71749a6b5 | ||
|
|
a188778eb8 | ||
|
|
439f27640a | ||
|
|
c9209cc99e | ||
|
|
1370a1cc83 | ||
|
|
266e8f5ae4 | ||
|
|
ccaac6c336 | ||
|
|
88005640d5 | ||
|
|
1f4e06ea7c | ||
|
|
b6c9fb2da1 | ||
|
|
66f5f396ff | ||
|
|
c9628aae49 | ||
|
|
bb5d3f0600 | ||
|
|
1f819ce2c5 | ||
|
|
593eda7d9d | ||
|
|
14dab7c649 | ||
|
|
94a07fa39a | ||
|
|
492a0f8199 | ||
|
|
24736b919a | ||
|
|
38220a57ce | ||
|
|
b14dc1572d | ||
|
|
21964f3cbc | ||
|
|
c13921b7c7 | ||
|
|
39fef2705c | ||
|
|
8c427c7de9 | ||
|
|
0bb995abd0 | ||
|
|
0195eef865 | ||
|
|
cc3dbd84ce | ||
|
|
5e6285afab | ||
|
|
df28a1335a | ||
|
|
88a0871784 | ||
|
|
f0edca2f8c | ||
|
|
f5462e3e19 | ||
|
|
53c5a97b4c | ||
|
|
a3352fd899 | ||
|
|
4a85e1e1ca | ||
|
|
94efe5c187 | ||
|
|
bef59a0b9a | ||
|
|
86142343ee | ||
|
|
5b2458ed66 | ||
|
|
b52cd44c25 | ||
|
|
40997e7fc9 | ||
|
|
a4e68a0304 | ||
|
|
703736fbcb | ||
|
|
fd45808555 | ||
|
|
e9fec47411 | ||
|
|
6029352c09 | ||
|
|
077208d17a | ||
|
|
08863dec0b | ||
|
|
80af92a715 | ||
|
|
237e0ec23c | ||
|
|
d52e97dfcd | ||
|
|
8329d6ac1a | ||
|
|
f1d723077f | ||
|
|
a095c468b2 | ||
|
|
b14e6a3570 | ||
|
|
6e3391a846 | ||
|
|
c0ca5c97d5 | ||
|
|
d5cf6d72e2 | ||
|
|
9eb0f2941f | ||
|
|
3fa295d99d | ||
|
|
812f63502b | ||
|
|
353eb9f00f | ||
|
|
1b813d9d6c | ||
|
|
c5dade5ee7 | ||
|
|
e75d6f616c | ||
|
|
15b601f053 | ||
|
|
45317ef7f2 | ||
|
|
3da792152a | ||
|
|
3f6da779d2 | ||
|
|
57b8ae6e1c | ||
|
|
ade8dc1a21 | ||
|
|
c6f7ad3e83 | ||
|
|
dbe6be778b | ||
|
|
45b0279b41 | ||
|
|
3a5aeb4573 | ||
|
|
72745846f4 | ||
|
|
9e788bb8c2 | ||
|
|
56fbe7e14d | ||
|
|
44155f6771 | ||
|
|
341711c04b | ||
|
|
be6187e893 | ||
|
|
c42bdeab43 | ||
|
|
7d7c58cb8e | ||
|
|
044da877e6 | ||
|
|
2d7686b5fa | ||
|
|
074f6db6d8 | ||
|
|
7a884af0ef | ||
|
|
e568e1019d | ||
|
|
ca543039a5 | ||
|
|
5e7b1da632 | ||
|
|
6b9ef6421f | ||
|
|
d15c8453ac | ||
|
|
1e660dda71 | ||
|
|
2a77afc593 | ||
|
|
a9df3e48f7 | ||
|
|
c8994ede3f | ||
|
|
7e255733e0 | ||
|
|
72f9a96cce | ||
|
|
1308ff6479 | ||
|
|
b7a15092d8 | ||
|
|
ad9ffc62e6 | ||
|
|
46e613dcca | ||
|
|
2d7414733e | ||
|
|
9f597bab1b | ||
|
|
321b9b5a8b | ||
|
|
60a3a4e455 | ||
|
|
57e5cc8c17 | ||
|
|
5250673fa0 | ||
|
|
ab5e19a016 | ||
|
|
8790e9de01 | ||
|
|
1086d4476d | ||
|
|
7b80418122 | ||
|
|
02d034f53f | ||
|
|
e7126db97c | ||
|
|
4582ea3b27 | ||
|
|
43e100fd37 | ||
|
|
071a1477b5 | ||
|
|
61c8013f2c | ||
|
|
9c8c215c30 |
7
.travis.yml
Normal file
7
.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
install: sudo apt-get install libcppunit-dev zlib1g-dev
|
||||
script: cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON . && make && make check
|
||||
|
||||
2
AUTHORS
2
AUTHORS
@@ -10,6 +10,8 @@ Teemu Tervo <teemu.tervo@gmx.net>
|
||||
Numerous bug reports and fixes
|
||||
Mathias Panzenböck <grosser.meister.morti@gmx.net>
|
||||
Mod, S3M, IT and XM metadata implementations
|
||||
Tsuda Kageyu <tsuda.kageyu@gmail.com>
|
||||
A lot of fixes and improvements, i.e. memory copy reduction etc.
|
||||
|
||||
Please send all patches and questions to taglib-devel@kde.org rather than to
|
||||
individual developers!
|
||||
|
||||
@@ -47,7 +47,7 @@ if (MSVC AND ENABLE_STATIC_RUNTIME)
|
||||
endif()
|
||||
|
||||
set(TAGLIB_LIB_MAJOR_VERSION "1")
|
||||
set(TAGLIB_LIB_MINOR_VERSION "8")
|
||||
set(TAGLIB_LIB_MINOR_VERSION "9")
|
||||
set(TAGLIB_LIB_PATCH_VERSION "0")
|
||||
|
||||
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
|
||||
@@ -56,9 +56,9 @@ set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VE
|
||||
# 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 13)
|
||||
set(TAGLIB_SOVERSION_CURRENT 14)
|
||||
set(TAGLIB_SOVERSION_REVISION 0)
|
||||
set(TAGLIB_SOVERSION_AGE 12)
|
||||
set(TAGLIB_SOVERSION_AGE 13)
|
||||
|
||||
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
|
||||
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
|
||||
@@ -66,8 +66,15 @@ math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib-config )
|
||||
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/taglib-config DESTINATION ${BIN_INSTALL_DIR})
|
||||
if(NOT WIN32)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib-config )
|
||||
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/taglib-config DESTINATION ${BIN_INSTALL_DIR})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmd.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd )
|
||||
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd DESTINATION ${BIN_INSTALL_DIR})
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT BUILD_FRAMEWORK)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib.pc )
|
||||
@@ -75,13 +82,18 @@ if(NOT WIN32 AND NOT BUILD_FRAMEWORK)
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
configure_file(config-taglib.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
if(WITH_ASF)
|
||||
set(TAGLIB_WITH_ASF TRUE)
|
||||
set(TAGLIB_WITH_ASF TRUE)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(TAGLIB_WITH_MP4 TRUE)
|
||||
set(TAGLIB_WITH_MP4 TRUE)
|
||||
endif()
|
||||
|
||||
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
|
||||
if(TRACE_IN_RELEASE)
|
||||
set(TRACE_IN_RELEASE TRUE)
|
||||
endif()
|
||||
|
||||
configure_file(taglib/taglib_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h)
|
||||
|
||||
@@ -5,19 +5,222 @@ include(CheckFunctionExists)
|
||||
include(CheckLibraryExists)
|
||||
include(CheckTypeSize)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(TestBigEndian)
|
||||
|
||||
# Check if the size of integral types are suitable.
|
||||
|
||||
check_type_size("short" SIZEOF_SHORT)
|
||||
if(NOT ${SIZEOF_SHORT} EQUAL 2)
|
||||
MESSAGE(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("int" SIZEOF_INT)
|
||||
if(NOT ${SIZEOF_INT} EQUAL 4)
|
||||
MESSAGE(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("long long" SIZEOF_LONGLONG)
|
||||
if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
|
||||
MESSAGE(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("wchar_t" SIZEOF_WCHAR_T)
|
||||
if(${SIZEOF_WCHAR_T} LESS 2)
|
||||
MESSAGE(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
|
||||
endif()
|
||||
|
||||
# Determine the CPU byte order.
|
||||
|
||||
test_big_endian(IS_BIG_ENDIAN)
|
||||
|
||||
if(NOT IS_BIG_ENDIAN)
|
||||
set(SYSTEM_BYTEORDER 1)
|
||||
else()
|
||||
set(SYSTEM_BYTEORDER 2)
|
||||
endif()
|
||||
|
||||
# Determine which kind of atomic operations your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic<unsigned int> x;
|
||||
x.fetch_add(1);
|
||||
x.fetch_sub(1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_ATOMIC)
|
||||
|
||||
if(NOT HAVE_STD_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <boost/atomic.hpp>
|
||||
int main() {
|
||||
boost::atomic<unsigned int> x(1);
|
||||
x.fetch_add(1);
|
||||
x.fetch_sub(1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_BOOST_ATOMIC)
|
||||
|
||||
if(NOT HAVE_BOOST_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
int main() {
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <windows.h>
|
||||
int main() {
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine which kind of byte swap functions your compiler supports.
|
||||
|
||||
# GCC's __builtin_bswap* should be checked individually
|
||||
# because some of them can be missing depends on the GCC version.
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
__builtin_bswap16(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_BYTESWAP_16)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
__builtin_bswap32(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_BYTESWAP_32)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
__builtin_bswap64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_BYTESWAP_64)
|
||||
|
||||
if(NOT HAVE_GCC_BYTESWAP_16 OR NOT HAVE_GCC_BYTESWAP_32 OR NOT HAVE_GCC_BYTESWAP_64)
|
||||
check_cxx_source_compiles("
|
||||
#include <byteswap.h>
|
||||
int main() {
|
||||
__bswap_16(0);
|
||||
__bswap_32(0);
|
||||
__bswap_64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GLIBC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GLIBC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <stdlib.h>
|
||||
int main() {
|
||||
_byteswap_ushort(0);
|
||||
_byteswap_ulong(0);
|
||||
_byteswap_uint64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MSC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_MSC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSByteOrder.h>
|
||||
int main() {
|
||||
OSSwapInt16(0);
|
||||
OSSwapInt32(0);
|
||||
OSSwapInt64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_MAC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <sys/endian.h>
|
||||
int main() {
|
||||
swap16(0);
|
||||
swap32(0);
|
||||
swap64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_OPENBSD_BYTESWAP)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports some safer version of sprintf.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdio>
|
||||
int main() { char buf[20]; snprintf(buf, 20, \"%d\", 1); return 0; }
|
||||
" HAVE_SNPRINTF)
|
||||
|
||||
if(NOT HAVE_SNPRINTF)
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdio>
|
||||
int main() { char buf[20]; sprintf_s(buf, \"%d\", 1); return 0; }
|
||||
" HAVE_SPRINTF_S)
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports codecvt.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <codecvt>
|
||||
int main() {
|
||||
std::codecvt_utf8_utf16<wchar_t> x;
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_CODECVT)
|
||||
|
||||
# Check for libz using the cmake supplied FindZLIB.cmake
|
||||
|
||||
# check for libz using the cmake supplied FindZLIB.cmake
|
||||
find_package(ZLIB)
|
||||
if(ZLIB_FOUND)
|
||||
set(HAVE_ZLIB 1)
|
||||
set(HAVE_ZLIB 1)
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
set(HAVE_ZLIB 0)
|
||||
endif()
|
||||
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
|
||||
|
||||
find_package(CppUnit)
|
||||
if(NOT CppUnit_FOUND AND BUILD_TESTS)
|
||||
message(STATUS "CppUnit not found, disabling tests.")
|
||||
set(BUILD_TESTS OFF)
|
||||
message(STATUS "CppUnit not found, disabling tests.")
|
||||
set(BUILD_TESTS OFF)
|
||||
endif()
|
||||
|
||||
|
||||
97
INSTALL
97
INSTALL
@@ -46,15 +46,102 @@ the include folder to the project's User Header Search Paths.
|
||||
Windows
|
||||
-------
|
||||
|
||||
For building a static library on Windows with Visual Studio 2010, cd to
|
||||
the TagLib folder then:
|
||||
It's Windows ... Systems vary!
|
||||
This means you need to adjust things to suit your system, especially paths.
|
||||
|
||||
cmake -DENABLE_STATIC=ON -DENABLE_STATIC_RUNTIME=ON -G "Visual Studio 10" ...
|
||||
Tested with:
|
||||
Microsoft Visual Studio 2010
|
||||
Gcc by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
|
||||
MinGW32-4.8.0
|
||||
|
||||
Requirements:
|
||||
1. Tool chain, Build Environment, Whatever ya want to call it ...
|
||||
Installed and working.
|
||||
2. CMake program. (Available at: www.cmake.org)
|
||||
Installed and working.
|
||||
|
||||
Optional:
|
||||
1. Zlib library.
|
||||
Available in some Tool Chains, Not all.
|
||||
Search the web, Take your choice.
|
||||
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
MSVS Command Prompt for MSVS Users.
|
||||
(Batch file and/or Shortcuts are your friends)
|
||||
|
||||
1. Build the Makefiles:
|
||||
Replace "GENERATOR" with your needs.
|
||||
For MSVS : "Visual Studio X" where X is the single or two digit version.
|
||||
For MinGW: "MinGW Makefiles"
|
||||
|
||||
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
|
||||
|
||||
Or use the CMake GUI:
|
||||
1. Open CMake GUI.
|
||||
2. Set Paths.
|
||||
"Where is the source code" and "Where to build the binaries"
|
||||
Example, Both would be: C:\GitRoot\taglib
|
||||
3. Tick: Advanced
|
||||
4. Select: Configure
|
||||
5. Select: Generator
|
||||
6. Tick: Use default native compilers
|
||||
7. Select: Finish
|
||||
Wait until done.
|
||||
5. If using ZLib, Scroll down.
|
||||
(to the bottom of the list of options ... should go over them all)
|
||||
1. Edit: ZLIB_INCLUDE_DIR
|
||||
2. Edit: ZLIB_LIBRARY
|
||||
6. Select: Generate
|
||||
|
||||
2. Build the project:
|
||||
MSVS:
|
||||
C:\GitRoot\taglib> msbuild all_build.vcxproj /p:Configuration=Release
|
||||
OR (Depending on MSVS version or personal choice)
|
||||
C:\GitRoot\taglib> devenv all_build.vcxproj /build Release
|
||||
MinGW:
|
||||
C:\GitRoot\taglib> gmake
|
||||
OR (Depending on MinGW install)
|
||||
C:\GitRoot\taglib> mingw32-make
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open MSVS.
|
||||
2. Open taglib solution.
|
||||
3. Set build type to: Release (look in the tool bars)
|
||||
2. Hit F7 to build the solution. (project)
|
||||
|
||||
3. Install the project:
|
||||
(Change 'install' to 'uninstall' to uninstall the project)
|
||||
MSVS:
|
||||
C:\GitRoot\taglib> msbuild install.vcxproj
|
||||
OR (Depending on MSVC version or personal choice)
|
||||
C:\GitRoot\taglib> devenv install.vcxproj
|
||||
MinGW:
|
||||
C:\GitRoot\taglib> gmake install
|
||||
OR (Depending on MinGW install)
|
||||
C:\GitRoot\taglib> mingw32-make install
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open project.
|
||||
2. Open Solution Explorer.
|
||||
3. Right Click: INSTALL
|
||||
4. Select: Project Only
|
||||
5. Select: Build Only INSTALL
|
||||
|
||||
To build a static library enable the following two options with CMake.
|
||||
-DENABLE_STATIC=ON -DENABLE_STATIC_RUNTIME=ON
|
||||
|
||||
Including ENABLE_STATIC_RUNTIME=ON indicates you want TagLib built using the
|
||||
static runtime library, rather than the DLL form of the runtime.
|
||||
CMake will create a Visual Studio solution, taglib.sln that you can open and
|
||||
build as normal.
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
|
||||
24
NEWS
24
NEWS
@@ -1,5 +1,27 @@
|
||||
TagLib 1.9 (Oct 6, 2013)
|
||||
==========================
|
||||
|
||||
* Added support for the Ogg Opus file format.
|
||||
* Added support for INFO tags in WAV files.
|
||||
* Changed FileStream to use Windows file API.
|
||||
* Included taglib-config.cmd script for Windows.
|
||||
* New ID3v1::Tag methods for working directly with genre numbers.
|
||||
* New MPEG::File methods for checking which tags are saved in the file.
|
||||
* Added support for the PropertyMap API to ASF and MP4 files.
|
||||
* Added MusicBrainz identifiers to the PropertyMap API.
|
||||
* Allowed reading of MP4 cover art without an explicitly specified format.
|
||||
* Better parsing of corrupted FLAC files.
|
||||
* Fixed saving of PropertyMap comments without description into ID3v2 tags.
|
||||
* Fixed crash when parsing certain XM files.
|
||||
* Fixed compilation of unit test with clang.
|
||||
* Better handling of files that can't be open or have read-only permissions.
|
||||
* Improved atomic reference counting.
|
||||
* New hookable API for debug messages.
|
||||
* More complete Windows install instructions.
|
||||
* Many smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.8 (Sep 6, 2012)
|
||||
==============================
|
||||
========================
|
||||
|
||||
1.8:
|
||||
|
||||
|
||||
@@ -19,10 +19,6 @@
|
||||
* USA *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#if defined(TAGLIB_STATIC)
|
||||
#define TAGLIB_C_EXPORT
|
||||
#elif defined(_WIN32) || defined(_WIN64)
|
||||
#ifdef MAKE_TAGLIB_C_LIB
|
||||
#define TAGLIB_C_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
/* config-taglib.h. Generated by cmake from config-taglib.h.cmake */
|
||||
|
||||
/* Define if you have libz */
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
|
||||
#cmakedefine NO_ITUNES_HACKS 1
|
||||
#cmakedefine WITH_ASF 1
|
||||
#cmakedefine WITH_MP4 1
|
||||
|
||||
#cmakedefine TESTS_DIR "@TESTS_DIR@"
|
||||
|
||||
37
config.h.cmake
Normal file
37
config.h.cmake
Normal file
@@ -0,0 +1,37 @@
|
||||
/* config.h. Generated by cmake from config.h.cmake */
|
||||
|
||||
/* Indicates the byte order of your target system */
|
||||
/* 1 if little-endian, 2 if big-endian. */
|
||||
#cmakedefine SYSTEM_BYTEORDER ${SYSTEM_BYTEORDER}
|
||||
|
||||
/* Defined if your compiler supports some byte swap functions */
|
||||
#cmakedefine HAVE_GCC_BYTESWAP_16 1
|
||||
#cmakedefine HAVE_GCC_BYTESWAP_32 1
|
||||
#cmakedefine HAVE_GCC_BYTESWAP_64 1
|
||||
#cmakedefine HAVE_GLIBC_BYTESWAP 1
|
||||
#cmakedefine HAVE_MSC_BYTESWAP 1
|
||||
#cmakedefine HAVE_MAC_BYTESWAP 1
|
||||
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
|
||||
|
||||
/* Defined if your compiler supports codecvt */
|
||||
#cmakedefine HAVE_STD_CODECVT 1
|
||||
|
||||
/* Defined if your compiler supports some atomic operations */
|
||||
#cmakedefine HAVE_STD_ATOMIC 1
|
||||
#cmakedefine HAVE_BOOST_ATOMIC 1
|
||||
#cmakedefine HAVE_GCC_ATOMIC 1
|
||||
#cmakedefine HAVE_MAC_ATOMIC 1
|
||||
#cmakedefine HAVE_WIN_ATOMIC 1
|
||||
#cmakedefine HAVE_IA64_ATOMIC 1
|
||||
|
||||
/* Defined if your compiler supports some safer version of sprintf */
|
||||
#cmakedefine HAVE_SNPRINTF 1
|
||||
#cmakedefine HAVE_SPRINTF_S 1
|
||||
|
||||
/* Defined if you have libz */
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
|
||||
/* Indicates whether debug messages are shown even in release mode */
|
||||
#cmakedefine TRACE_IN_RELEASE 1
|
||||
|
||||
#cmakedefine TESTS_DIR "@TESTS_DIR@"
|
||||
@@ -95,7 +95,10 @@ int main(int argc, char *argv[])
|
||||
for(APE::ItemListMap::ConstIterator it = ape->itemListMap().begin();
|
||||
it != ape->itemListMap().end(); ++it)
|
||||
{
|
||||
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
|
||||
if((*it).second.type() != APE::Item::Binary)
|
||||
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
|
||||
else
|
||||
cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -23,10 +23,12 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <fileref.h>
|
||||
#include <tag.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -49,7 +51,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
TagLib::Tag *tag = f.tag();
|
||||
|
||||
cout << "-- TAG --" << endl;
|
||||
cout << "-- TAG (basic) --" << endl;
|
||||
cout << "title - \"" << tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << tag->album() << "\"" << endl;
|
||||
@@ -57,6 +59,23 @@ int main(int argc, char *argv[])
|
||||
cout << "comment - \"" << tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
|
||||
TagLib::PropertyMap tags = f.file()->properties();
|
||||
|
||||
unsigned int longest = 0;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
if (i->first.size() > longest) {
|
||||
longest = i->first.size();
|
||||
}
|
||||
}
|
||||
|
||||
cout << "-- TAG (properties) --" << endl;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!f.isNull() && f.audioProperties()) {
|
||||
|
||||
36
taglib-config.cmd.cmake
Normal file
36
taglib-config.cmd.cmake
Normal file
@@ -0,0 +1,36 @@
|
||||
@echo off
|
||||
goto beginning
|
||||
*
|
||||
* It is what it is, you can do with it as you please.
|
||||
*
|
||||
* Just don't blame me if it teaches your computer to smoke!
|
||||
*
|
||||
* -Enjoy
|
||||
* fh :)_~
|
||||
*
|
||||
:beginning
|
||||
if /i "%1#" == "--libs#" goto doit
|
||||
if /i "%1#" == "--cflags#" goto doit
|
||||
if /i "%1#" == "--version#" goto doit
|
||||
if /i "%1#" == "--prefix#" goto doit
|
||||
|
||||
echo "usage: %0 [OPTIONS]"
|
||||
echo [--libs]
|
||||
echo [--cflags]
|
||||
echo [--version]
|
||||
echo [--prefix]
|
||||
goto theend
|
||||
|
||||
*
|
||||
* NOTE: Windows does not assume libraries are prefixed with 'lib'.
|
||||
* NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process
|
||||
* to allow for static, shared or debug builds.
|
||||
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
|
||||
:doit
|
||||
if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag
|
||||
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
|
||||
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
|
||||
|
||||
:theend
|
||||
|
||||
16
taglib/CMakeLists.txt
Normal file → Executable file
16
taglib/CMakeLists.txt
Normal file → Executable file
@@ -10,6 +10,7 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mp4
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1
|
||||
@@ -49,6 +50,8 @@ set(tag_HDRS
|
||||
toolkit/tmap.h
|
||||
toolkit/tmap.tcc
|
||||
toolkit/tpropertymap.h
|
||||
toolkit/trefcounter.h
|
||||
toolkit/tdebuglistener.h
|
||||
mpeg/mpegfile.h
|
||||
mpeg/mpegproperties.h
|
||||
mpeg/mpegheader.h
|
||||
@@ -83,6 +86,8 @@ set(tag_HDRS
|
||||
ogg/flac/oggflacfile.h
|
||||
ogg/speex/speexfile.h
|
||||
ogg/speex/speexproperties.h
|
||||
ogg/opus/opusfile.h
|
||||
ogg/opus/opusproperties.h
|
||||
flac/flacfile.h
|
||||
flac/flacpicture.h
|
||||
flac/flacproperties.h
|
||||
@@ -103,6 +108,7 @@ set(tag_HDRS
|
||||
riff/aiff/aiffproperties.h
|
||||
riff/wav/wavfile.h
|
||||
riff/wav/wavproperties.h
|
||||
riff/wav/infotag.h
|
||||
asf/asffile.h
|
||||
asf/asfproperties.h
|
||||
asf/asftag.h
|
||||
@@ -219,6 +225,11 @@ set(speex_SRCS
|
||||
ogg/speex/speexproperties.cpp
|
||||
)
|
||||
|
||||
set(opus_SRCS
|
||||
ogg/opus/opusfile.cpp
|
||||
ogg/opus/opusproperties.cpp
|
||||
)
|
||||
|
||||
set(trueaudio_SRCS
|
||||
trueaudio/trueaudiofile.cpp
|
||||
trueaudio/trueaudioproperties.cpp
|
||||
@@ -244,6 +255,7 @@ set(aiff_SRCS
|
||||
set(wav_SRCS
|
||||
riff/wav/wavfile.cpp
|
||||
riff/wav/wavproperties.cpp
|
||||
riff/wav/infotag.cpp
|
||||
)
|
||||
|
||||
set(mod_SRCS
|
||||
@@ -279,6 +291,8 @@ set(toolkit_SRCS
|
||||
toolkit/tfilestream.cpp
|
||||
toolkit/tdebug.cpp
|
||||
toolkit/tpropertymap.cpp
|
||||
toolkit/trefcounter.cpp
|
||||
toolkit/tdebuglistener.cpp
|
||||
toolkit/unicode.cpp
|
||||
)
|
||||
|
||||
@@ -286,7 +300,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}
|
||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
|
||||
@@ -47,7 +47,7 @@ using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum { ApeAPEIndex, ApeID3v1Index };
|
||||
enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
|
||||
}
|
||||
|
||||
class APE::File::FilePrivate
|
||||
@@ -90,14 +90,16 @@ APE::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
APE::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
APE::File::~File()
|
||||
@@ -129,12 +131,9 @@ void APE::File::removeUnsupportedProperties(const StringList &properties)
|
||||
|
||||
PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->hasAPE)
|
||||
return d->tag.access<APE::Tag>(ApeAPEIndex, false)->setProperties(properties);
|
||||
else if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
|
||||
else
|
||||
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
|
||||
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
|
||||
}
|
||||
|
||||
APE::Properties *APE::File::audioProperties() const
|
||||
@@ -236,6 +235,16 @@ void APE::File::strip(int tags)
|
||||
}
|
||||
}
|
||||
|
||||
bool APE::File::hasAPETag() const
|
||||
{
|
||||
return d->hasAPE;
|
||||
}
|
||||
|
||||
bool APE::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->hasID3v1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace TagLib {
|
||||
//! An implementation of TagLib::File with APE specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface APE WavPack files to the
|
||||
* This implements and provides an interface for APE files to the
|
||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
||||
* the abstract TagLib::File API as well as providing some additional
|
||||
* information specific to APE files.
|
||||
@@ -84,20 +84,22 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
/*!
|
||||
* Contructs an WavPack file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an APE 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);
|
||||
|
||||
/*!
|
||||
* Contructs an WavPack file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an APE file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -128,10 +130,11 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* As for the export, only one tag is taken into account. If the file
|
||||
* has no tag at all, APE will be created.
|
||||
* Creates an APEv2 tag if necessary. A pontentially existing ID3v1
|
||||
* tag will be updated as well.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the APE::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
@@ -149,27 +152,38 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* If \a create is false (the default) this may return a null pointer
|
||||
* if there is no valid ID3v1 tag. If \a create is true it will create
|
||||
* an ID3v1 tag if one does not exist. If there is already an APE tag, the
|
||||
* new ID3v1 tag will be placed after it.
|
||||
* an ID3v1 tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the APE::File and should not be
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
|
||||
* on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasID3v1Tag()
|
||||
*/
|
||||
ID3v1::Tag *ID3v1Tag(bool create = false);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the APE tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* If \a create is false (the default) this may return a null pointer
|
||||
* if there is no valid APE tag. If \a create is true it will create
|
||||
* a APE tag if one does not exist.
|
||||
* an APE tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the APE::File and should not be
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an APE tag. Use hasAPETag() to check if the file
|
||||
* on disk actually has an APE tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasAPETag()
|
||||
*/
|
||||
APE::Tag *APETag(bool create = false);
|
||||
|
||||
@@ -183,6 +197,20 @@ namespace TagLib {
|
||||
*/
|
||||
void strip(int tags = AllTags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an APE tag.
|
||||
*
|
||||
* \see APETag()
|
||||
*/
|
||||
bool hasAPETag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \see ID3v1Tag()
|
||||
*/
|
||||
bool hasID3v1Tag() const;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
@@ -177,19 +177,19 @@ void APE::Footer::parse(const ByteVector &data)
|
||||
|
||||
// Read the version number
|
||||
|
||||
d->version = data.mid(8, 4).toUInt(false);
|
||||
d->version = data.toUInt(8, false);
|
||||
|
||||
// Read the tag size
|
||||
|
||||
d->tagSize = data.mid(12, 4).toUInt(false);
|
||||
d->tagSize = data.toUInt(12, false);
|
||||
|
||||
// Read the item count
|
||||
|
||||
d->itemCount = data.mid(16, 4).toUInt(false);
|
||||
d->itemCount = data.toUInt(16, false);
|
||||
|
||||
// Read the flags
|
||||
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(20, 4).toUInt(false)));
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
|
||||
|
||||
d->headerPresent = flags[31];
|
||||
d->footerPresent = !flags[30];
|
||||
|
||||
@@ -125,6 +125,7 @@ void APE::Item::setBinaryData(const ByteVector &value)
|
||||
{
|
||||
d->type = Binary;
|
||||
d->value = value;
|
||||
d->text.clear();
|
||||
}
|
||||
|
||||
ByteVector APE::Item::value() const
|
||||
@@ -137,31 +138,35 @@ ByteVector APE::Item::value() const
|
||||
|
||||
void APE::Item::setKey(const String &key)
|
||||
{
|
||||
d->key = key;
|
||||
d->key = key;
|
||||
}
|
||||
|
||||
void APE::Item::setValue(const String &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::setValues(const StringList &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::appendValue(const String &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text.append(value);
|
||||
d->type = Text;
|
||||
d->text.append(value);
|
||||
d->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::appendValues(const StringList &values)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text.append(values);
|
||||
d->type = Text;
|
||||
d->text.append(values);
|
||||
d->value.clear();
|
||||
}
|
||||
|
||||
int APE::Item::size() const
|
||||
@@ -200,7 +205,10 @@ StringList APE::Item::values() const
|
||||
|
||||
String APE::Item::toString() const
|
||||
{
|
||||
return isEmpty() ? String::null : d->text.front();
|
||||
if(d->type == Text && !isEmpty())
|
||||
return d->text.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
bool APE::Item::isEmpty() const
|
||||
@@ -229,18 +237,20 @@ void APE::Item::parse(const ByteVector &data)
|
||||
return;
|
||||
}
|
||||
|
||||
uint valueLength = data.mid(0, 4).toUInt(false);
|
||||
uint flags = data.mid(4, 4).toUInt(false);
|
||||
const uint valueLength = data.toUInt(0, false);
|
||||
const uint flags = data.toUInt(4, false);
|
||||
|
||||
d->key = String(data.mid(8), String::UTF8);
|
||||
|
||||
d->value = data.mid(8 + d->key.size() + 1, valueLength);
|
||||
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
|
||||
|
||||
setReadOnly(flags & 1);
|
||||
setType(ItemTypes((flags >> 1) & 3));
|
||||
|
||||
if(Text == d->type)
|
||||
d->text = StringList(ByteVectorList::split(d->value, '\0'), String::UTF8);
|
||||
if(Text == d->type)
|
||||
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
|
||||
else
|
||||
d->value = value;
|
||||
}
|
||||
|
||||
ByteVector APE::Item::render() const
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Returns the binary value.
|
||||
* If the item type is not \a Binary, the returned contents are undefined
|
||||
* If the item type is not \a Binary, always returns an empty ByteVector.
|
||||
*/
|
||||
ByteVector binaryData() const;
|
||||
|
||||
@@ -152,8 +152,9 @@ namespace TagLib {
|
||||
int size() const;
|
||||
|
||||
/*!
|
||||
* Returns the value as a single string. In case of multiple strings,
|
||||
* the first is returned.
|
||||
* Returns the value as a single string. In case of multiple strings,
|
||||
* the first is returned. If the data type is not \a Text, always returns
|
||||
* an empty String.
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
@@ -163,7 +164,8 @@ namespace TagLib {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Returns the list of text values.
|
||||
* Returns the list of text values. If the data type is not \a Text, always
|
||||
* returns an empty StringList.
|
||||
*/
|
||||
StringList values() const;
|
||||
|
||||
|
||||
@@ -125,10 +125,10 @@ void APE::Properties::read()
|
||||
|
||||
// Then we read the header common for all versions of APE
|
||||
d->file->seek(offset);
|
||||
ByteVector commonHeader=d->file->readBlock(6);
|
||||
ByteVector commonHeader = d->file->readBlock(6);
|
||||
if(!commonHeader.startsWith("MAC "))
|
||||
return;
|
||||
d->version = commonHeader.mid(4).toUInt(false);
|
||||
d->version = commonHeader.toUShort(4, false);
|
||||
|
||||
if(d->version >= 3980) {
|
||||
analyzeCurrent();
|
||||
@@ -182,7 +182,7 @@ void APE::Properties::analyzeCurrent()
|
||||
// Read the descriptor
|
||||
d->file->seek(2, File::Current);
|
||||
ByteVector descriptor = d->file->readBlock(44);
|
||||
uint descriptorBytes = descriptor.mid(0,4).toUInt(false);
|
||||
const uint descriptorBytes = descriptor.toUInt(0, false);
|
||||
|
||||
if ((descriptorBytes - 52) > 0)
|
||||
d->file->seek(descriptorBytes - 52, File::Current);
|
||||
@@ -191,14 +191,14 @@ void APE::Properties::analyzeCurrent()
|
||||
ByteVector header = d->file->readBlock(24);
|
||||
|
||||
// Get the APE info
|
||||
d->channels = header.mid(18, 2).toShort(false);
|
||||
d->sampleRate = header.mid(20, 4).toUInt(false);
|
||||
d->bitsPerSample = header.mid(16, 2).toShort(false);
|
||||
d->channels = header.toShort(18, false);
|
||||
d->sampleRate = header.toUInt(20, false);
|
||||
d->bitsPerSample = header.toShort(16, false);
|
||||
//d->compressionLevel =
|
||||
|
||||
uint totalFrames = header.mid(12, 4).toUInt(false);
|
||||
uint blocksPerFrame = header.mid(4, 4).toUInt(false);
|
||||
uint finalFrameBlocks = header.mid(8, 4).toUInt(false);
|
||||
const uint totalFrames = header.toUInt(12, false);
|
||||
const uint blocksPerFrame = header.toUInt(4, false);
|
||||
const uint finalFrameBlocks = header.toUInt(8, false);
|
||||
d->sampleFrames = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
|
||||
d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
|
||||
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
|
||||
@@ -207,13 +207,13 @@ void APE::Properties::analyzeCurrent()
|
||||
void APE::Properties::analyzeOld()
|
||||
{
|
||||
ByteVector header = d->file->readBlock(26);
|
||||
uint totalFrames = header.mid(18, 4).toUInt(false);
|
||||
const uint totalFrames = header.toUInt(18, false);
|
||||
|
||||
// Fail on 0 length APE files (catches non-finalized APE files)
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
short compressionLevel = header.mid(0, 2).toShort(false);
|
||||
const short compressionLevel = header.toShort(0, false);
|
||||
uint blocksPerFrame;
|
||||
if(d->version >= 3950)
|
||||
blocksPerFrame = 73728 * 4;
|
||||
@@ -221,10 +221,11 @@ void APE::Properties::analyzeOld()
|
||||
blocksPerFrame = 73728;
|
||||
else
|
||||
blocksPerFrame = 9216;
|
||||
d->channels = header.mid(4, 2).toShort(false);
|
||||
d->sampleRate = header.mid(6, 4).toUInt(false);
|
||||
uint finalFrameBlocks = header.mid(22, 4).toUInt(false);
|
||||
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
|
||||
d->channels = header.toShort(4, false);
|
||||
d->sampleRate = header.toUInt(6, false);
|
||||
const uint finalFrameBlocks = header.toUInt(22, false);
|
||||
const uint totalBlocks
|
||||
= totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
|
||||
d->length = totalBlocks / d->sampleRate;
|
||||
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
|
||||
}
|
||||
|
||||
@@ -23,12 +23,9 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include "asfattribute.h"
|
||||
#include "asffile.h"
|
||||
|
||||
|
||||
@@ -23,12 +23,9 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
#include "asffile.h"
|
||||
#include "asftag.h"
|
||||
@@ -186,7 +183,8 @@ ByteVector ASF::File::FilePropertiesObject::guid()
|
||||
void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L));
|
||||
file->d->properties->setLength(
|
||||
(int)(data.toLongLong(40, false) / 10000000L - data.toLongLong(56, false) / 1000L));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::StreamPropertiesObject::guid()
|
||||
@@ -197,9 +195,9 @@ ByteVector ASF::File::StreamPropertiesObject::guid()
|
||||
void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
file->d->properties->setChannels(data.mid(56, 2).toShort(false));
|
||||
file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false));
|
||||
file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000);
|
||||
file->d->properties->setChannels(data.toShort(56, false));
|
||||
file->d->properties->setSampleRate(data.toUInt(58, false));
|
||||
file->d->properties->setBitrate(data.toUInt(62, false) * 8 / 1000);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::ContentDescriptionObject::guid()
|
||||
@@ -372,14 +370,16 @@ ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle proper
|
||||
: TagLib::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||
: TagLib::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
ASF::File::~File()
|
||||
@@ -401,6 +401,21 @@ ASF::Tag *ASF::File::tag() const
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
void ASF::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
ASF::Properties *ASF::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
|
||||
@@ -48,30 +48,27 @@ namespace TagLib {
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Contructs an ASF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an ASF file from \a file.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs an ASF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an ASF file from \a stream.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored.
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
@@ -90,6 +87,22 @@ namespace TagLib {
|
||||
*/
|
||||
virtual Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the ASF audio properties for this file.
|
||||
*/
|
||||
@@ -103,7 +116,6 @@ namespace TagLib {
|
||||
virtual bool save();
|
||||
|
||||
private:
|
||||
|
||||
int readBYTE(bool *ok = 0);
|
||||
int readWORD(bool *ok = 0);
|
||||
unsigned int readDWORD(bool *ok = 0);
|
||||
|
||||
@@ -23,12 +23,9 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include "asfattribute.h"
|
||||
#include "asffile.h"
|
||||
#include "asfpicture.h"
|
||||
@@ -149,7 +146,7 @@ void ASF::Picture::parse(const ByteVector& bytes)
|
||||
return;
|
||||
int pos = 0;
|
||||
d->type = (Type)bytes[0]; ++pos;
|
||||
uint dataLen = bytes.mid(pos, 4).toUInt(false); pos+=4;
|
||||
const uint dataLen = bytes.toUInt(pos, false); pos+=4;
|
||||
|
||||
const ByteVector nullStringTerminator(2, 0);
|
||||
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "asfproperties.h"
|
||||
|
||||
@@ -23,10 +23,7 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tpropertymap.h>
|
||||
#include "asftag.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -196,3 +193,162 @@ bool ASF::Tag::isEmpty() const
|
||||
d->attributeListMap.isEmpty();
|
||||
}
|
||||
|
||||
static const char *keyTranslation[][2] = {
|
||||
{ "WM/AlbumTitle", "ALBUM" },
|
||||
{ "WM/Composer", "COMPOSER" },
|
||||
{ "WM/Writer", "WRITER" },
|
||||
{ "WM/Conductor", "CONDUCTOR" },
|
||||
{ "WM/ModifiedBy", "REMIXER" },
|
||||
{ "WM/Year", "DATE" },
|
||||
{ "WM/OriginalReleaseYear", "ORIGINALDATE" },
|
||||
{ "WM/Producer", "PRODUCER" },
|
||||
{ "WM/ContentGroupDescription", "GROUPING" },
|
||||
{ "WM/SubTitle", "SUBTITLE" },
|
||||
{ "WM/SetSubTitle", "DISCSUBTITLE" },
|
||||
{ "WM/TrackNumber", "TRACKNUMBER" },
|
||||
{ "WM/PartOfSet", "DISCNUMBER" },
|
||||
{ "WM/Genre", "GENRE" },
|
||||
{ "WM/BeatsPerMinute", "BPM" },
|
||||
{ "WM/Mood", "MOOD" },
|
||||
{ "WM/ISRC", "ISRC" },
|
||||
{ "WM/Lyrics", "LYRICS" },
|
||||
{ "WM/Media", "MEDIA" },
|
||||
{ "WM/Publisher", "LABEL" },
|
||||
{ "WM/CatalogNo", "CATALOGNUMBER" },
|
||||
{ "WM/Barcode", "BARCODE" },
|
||||
{ "WM/EncodedBy", "ENCODEDBY" },
|
||||
{ "WM/AlbumSortOrder", "ALBUMSORT" },
|
||||
{ "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" },
|
||||
{ "WM/ArtistSortOrder", "ARTISTSORT" },
|
||||
{ "WM/TitleSortOrder", "TITLESORT" },
|
||||
{ "WM/Script", "SCRIPT" },
|
||||
{ "WM/Language", "LANGUAGE" },
|
||||
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "MusicIP/PUID", "MUSICIP_PUID" },
|
||||
{ "Acoustid/Id", "ACOUSTID_ID" },
|
||||
{ "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" },
|
||||
};
|
||||
|
||||
PropertyMap ASF::Tag::properties() const
|
||||
{
|
||||
static Map<String, String> keyMap;
|
||||
if(keyMap.isEmpty()) {
|
||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
for(int i = 0; i < numKeys; i++) {
|
||||
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap props;
|
||||
|
||||
if(!d->title.isEmpty()) {
|
||||
props["TITLE"] = d->title;
|
||||
}
|
||||
if(!d->artist.isEmpty()) {
|
||||
props["ARTIST"] = d->artist;
|
||||
}
|
||||
if(!d->copyright.isEmpty()) {
|
||||
props["COPYRIGHT"] = d->copyright;
|
||||
}
|
||||
if(!d->comment.isEmpty()) {
|
||||
props["COMMENT"] = d->comment;
|
||||
}
|
||||
|
||||
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
|
||||
for(; it != d->attributeListMap.end(); ++it) {
|
||||
if(keyMap.contains(it->first)) {
|
||||
String key = keyMap[it->first];
|
||||
AttributeList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
if(key == "TRACKNUMBER") {
|
||||
if(it2->type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(it2->toUInt()));
|
||||
else
|
||||
props.insert(key, it2->toString());
|
||||
}
|
||||
else {
|
||||
props.insert(key, it2->toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
props.unsupportedData().append(it->first);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
|
||||
{
|
||||
StringList::ConstIterator it = props.begin();
|
||||
for(; it != props.end(); ++it)
|
||||
d->attributeListMap.erase(*it);
|
||||
}
|
||||
|
||||
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
|
||||
{
|
||||
static Map<String, String> reverseKeyMap;
|
||||
if(reverseKeyMap.isEmpty()) {
|
||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
for(int i = 0; i < numKeys; i++) {
|
||||
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap origProps = properties();
|
||||
PropertyMap::ConstIterator it = origProps.begin();
|
||||
for(; it != origProps.end(); ++it) {
|
||||
if(!props.contains(it->first) || props[it->first].isEmpty()) {
|
||||
if(it->first == "TITLE") {
|
||||
d->title = String::null;
|
||||
}
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist = String::null;
|
||||
}
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment = String::null;
|
||||
}
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright = String::null;
|
||||
}
|
||||
else {
|
||||
d->attributeListMap.erase(reverseKeyMap[it->first]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap ignoredProps;
|
||||
it = props.begin();
|
||||
for(; it != props.end(); ++it) {
|
||||
if(reverseKeyMap.contains(it->first)) {
|
||||
String name = reverseKeyMap[it->first];
|
||||
removeItem(name);
|
||||
StringList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
addAttribute(name, *it2);
|
||||
}
|
||||
}
|
||||
else if(it->first == "TITLE") {
|
||||
d->title = it->second.toString();
|
||||
}
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist = it->second.toString();
|
||||
}
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment = it->second.toString();
|
||||
}
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright = it->second.toString();
|
||||
}
|
||||
else {
|
||||
ignoredProps.insert(it->first, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
return ignoredProps;
|
||||
}
|
||||
|
||||
@@ -176,6 +176,10 @@ namespace TagLib {
|
||||
*/
|
||||
void addAttribute(const String &name, const Attribute &attribute);
|
||||
|
||||
PropertyMap properties() const;
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
private:
|
||||
|
||||
class TagPrivate;
|
||||
|
||||
@@ -27,13 +27,10 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
|
||||
#include "fileref.h"
|
||||
#include "asffile.h"
|
||||
@@ -45,6 +42,7 @@
|
||||
#include "mp4file.h"
|
||||
#include "wavpackfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "opusfile.h"
|
||||
#include "trueaudiofile.h"
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
@@ -217,21 +215,28 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
|
||||
|
||||
// Ok, this is really dumb for now, but it works for testing.
|
||||
|
||||
String s;
|
||||
|
||||
String ext;
|
||||
{
|
||||
#ifdef _WIN32
|
||||
s = (wcslen((const wchar_t *) fileName) > 0) ? String((const wchar_t *) fileName) : String((const char *) fileName);
|
||||
|
||||
String s = fileName.toString();
|
||||
|
||||
#else
|
||||
s = fileName;
|
||||
#endif
|
||||
|
||||
String s = fileName;
|
||||
|
||||
#endif
|
||||
|
||||
const int pos = s.rfind(".");
|
||||
if(pos != -1)
|
||||
ext = s.substr(pos + 1).upper();
|
||||
}
|
||||
|
||||
// If this list is updated, the method defaultFileExtensions() should also be
|
||||
// updated. However at some point that list should be created at the same time
|
||||
// that a default file type resolver is created.
|
||||
|
||||
int pos = s.rfind(".");
|
||||
if(pos != -1) {
|
||||
String ext = s.substr(pos + 1).upper();
|
||||
if(!ext.isEmpty()) {
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
@@ -252,6 +257,8 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
|
||||
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "SPX")
|
||||
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OPUS")
|
||||
return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "TTA")
|
||||
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
|
||||
|
||||
0
taglib/fileref.h
Normal file → Executable file
0
taglib/fileref.h
Normal file → Executable file
@@ -70,7 +70,8 @@ public:
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
for(uint i = 0; i < blocks.size(); i++) {
|
||||
uint size = blocks.size();
|
||||
for(uint i = 0; i < size; i++) {
|
||||
delete blocks[i];
|
||||
}
|
||||
delete properties;
|
||||
@@ -108,7 +109,8 @@ FLAC::File::File(FileName file, bool readProperties,
|
||||
TagLib::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
@@ -117,7 +119,8 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->ID3v2FrameFactory = frameFactory;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
@@ -126,7 +129,8 @@ FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->ID3v2FrameFactory = frameFactory;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
FLAC::File::~File()
|
||||
@@ -164,14 +168,7 @@ void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
|
||||
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->hasXiphComment)
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->setProperties(properties);
|
||||
else if(d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->setProperties(properties);
|
||||
else if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->setProperties(properties);
|
||||
else
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
|
||||
}
|
||||
|
||||
FLAC::Properties *FLAC::File::audioProperties() const
|
||||
@@ -402,7 +399,7 @@ void FLAC::File::scan()
|
||||
|
||||
char blockType = header[0] & 0x7f;
|
||||
bool isLastBlock = (header[0] & 0x80) != 0;
|
||||
uint length = header.mid(1, 3).toUInt();
|
||||
uint length = header.toUInt(1U, 3U);
|
||||
|
||||
// First block should be the stream_info metadata
|
||||
|
||||
@@ -422,10 +419,10 @@ void FLAC::File::scan()
|
||||
header = readBlock(4);
|
||||
blockType = header[0] & 0x7f;
|
||||
isLastBlock = (header[0] & 0x80) != 0;
|
||||
length = header.mid(1, 3).toUInt();
|
||||
length = header.toUInt(1U, 3U);
|
||||
|
||||
ByteVector data = readBlock(length);
|
||||
if(data.size() != length) {
|
||||
if(data.size() != length || length == 0) {
|
||||
debug("FLAC::File::scan() -- FLAC stream corrupted");
|
||||
setValid(false);
|
||||
return;
|
||||
@@ -555,3 +552,17 @@ void FLAC::File::removePictures()
|
||||
d->blocks = newBlocks;
|
||||
}
|
||||
|
||||
bool FLAC::File::hasXiphComment() const
|
||||
{
|
||||
return d->hasXiphComment;
|
||||
}
|
||||
|
||||
bool FLAC::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->hasID3v1;
|
||||
}
|
||||
|
||||
bool FLAC::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->hasID3v2;
|
||||
}
|
||||
|
||||
@@ -67,9 +67,10 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs a FLAC 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.
|
||||
*
|
||||
* \deprecated This constructor will be dropped in favor of the one below
|
||||
* in a future version.
|
||||
@@ -78,12 +79,13 @@ namespace TagLib {
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an APE file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
@@ -91,15 +93,16 @@ namespace TagLib {
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs a FLAC file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note 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.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
@@ -133,8 +136,10 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* As with the export, only one tag is taken into account. If the file
|
||||
* has no tag at all, a XiphComment will be created.
|
||||
* This always creates a Xiph comment, if none exists. The return value
|
||||
* relates to the Xiph comment only.
|
||||
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
|
||||
* in the FLAC specification.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
@@ -156,39 +161,57 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns a pointer to the ID3v2 tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* If \a create is false (the default) this returns a null pointer
|
||||
* if there is no valid ID3v2 tag. If \a create is true it will create
|
||||
* an ID3v2 tag if one does not exist.
|
||||
* an ID3v2 tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
|
||||
* on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasID3v2Tag()
|
||||
*/
|
||||
ID3v2::Tag *ID3v2Tag(bool create = false);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* if there is no valid ID3v1 tag. If \a create is true it will create
|
||||
* an ID3v1 tag if one does not exist.
|
||||
* If \a create is false (the default) this returns a null pointer
|
||||
* if there is no valid APE tag. If \a create is true it will create
|
||||
* an APE tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
|
||||
* on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasID3v1Tag()
|
||||
*/
|
||||
ID3v1::Tag *ID3v1Tag(bool create = false);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the XiphComment for the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* If \a create is false (the default) this returns a null pointer
|
||||
* if there is no valid XiphComment. If \a create is true it will create
|
||||
* a XiphComment if one does not exist.
|
||||
* a XiphComment if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has a XiphComment. Use hasXiphComment() to check if the
|
||||
* file on disk actually has a XiphComment.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasXiphComment()
|
||||
*/
|
||||
Ogg::XiphComment *xiphComment(bool create = false);
|
||||
|
||||
@@ -241,6 +264,27 @@ namespace TagLib {
|
||||
*/
|
||||
void addPicture(Picture *picture);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has a XiphComment.
|
||||
*
|
||||
* \see xiphComment()
|
||||
*/
|
||||
bool hasXiphComment() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \see ID3v1Tag()
|
||||
*/
|
||||
bool hasID3v1Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "flacpicture.h"
|
||||
@@ -82,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
return false;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
d->type = FLAC::Picture::Type(data.mid(pos, 4).toUInt());
|
||||
uint pos = 0;
|
||||
d->type = FLAC::Picture::Type(data.toUInt(pos));
|
||||
pos += 4;
|
||||
uint mimeTypeLength = data.mid(pos, 4).toUInt();
|
||||
uint mimeTypeLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + mimeTypeLength + 24 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -93,7 +89,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
|
||||
pos += mimeTypeLength;
|
||||
uint descriptionLength = data.mid(pos, 4).toUInt();
|
||||
uint descriptionLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + descriptionLength + 20 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -101,15 +97,15 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->description = String(data.mid(pos, descriptionLength), String::UTF8);
|
||||
pos += descriptionLength;
|
||||
d->width = data.mid(pos, 4).toUInt();
|
||||
d->width = data.toUInt(pos);
|
||||
pos += 4;
|
||||
d->height = data.mid(pos, 4).toUInt();
|
||||
d->height = data.toUInt(pos);
|
||||
pos += 4;
|
||||
d->colorDepth = data.mid(pos, 4).toUInt();
|
||||
d->colorDepth = data.toUInt(pos);
|
||||
pos += 4;
|
||||
d->numColors = data.mid(pos, 4).toUInt();
|
||||
d->numColors = data.toUInt(pos);
|
||||
pos += 4;
|
||||
uint dataLength = data.mid(pos, 4).toUInt();
|
||||
uint dataLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + dataLength > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
|
||||
@@ -124,7 +124,7 @@ void FLAC::Properties::read()
|
||||
return;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
uint pos = 0;
|
||||
|
||||
// Minimum block size (in samples)
|
||||
pos += 2;
|
||||
@@ -138,7 +138,7 @@ void FLAC::Properties::read()
|
||||
// Maximum frame size (in bytes)
|
||||
pos += 3;
|
||||
|
||||
uint flags = d->data.mid(pos, 4).toUInt(true);
|
||||
uint flags = d->data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
|
||||
d->sampleRate = flags >> 12;
|
||||
@@ -149,7 +149,7 @@ void FLAC::Properties::read()
|
||||
// stream length in samples. (Audio files measured in days)
|
||||
|
||||
unsigned long long hi = flags & 0xf;
|
||||
unsigned long long lo = d->data.mid(pos, 4).toUInt(true);
|
||||
unsigned long long lo = d->data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
|
||||
d->sampleFrames = (hi << 32) | lo;
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
|
||||
@@ -45,7 +45,8 @@ IT::File::File(FileName file, bool readProperties,
|
||||
Mod::FileBase(file),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
{
|
||||
read(readProperties);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
IT::File::File(IOStream *stream, bool readProperties,
|
||||
@@ -53,7 +54,8 @@ IT::File::File(IOStream *stream, bool readProperties,
|
||||
Mod::FileBase(stream),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
{
|
||||
read(readProperties);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
IT::File::~File()
|
||||
|
||||
@@ -36,23 +36,27 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT File : public Mod::FileBase {
|
||||
public:
|
||||
/*!
|
||||
* Contructs a Impulse Tracker file from \a file. If \a readProperties
|
||||
* is true the file's audio properties will also be read using
|
||||
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
|
||||
* Constructs a Impulse Tracker file from \a file.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs a Impulse Tracker file from \a stream. If \a readProperties
|
||||
* is true the file's audio properties will also be read using
|
||||
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
|
||||
* Constructs a Impulse Tracker file from \a stream.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stram, bool readProperties = true,
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average);
|
||||
|
||||
|
||||
@@ -45,7 +45,8 @@ Mod::File::File(FileName file, bool readProperties,
|
||||
Mod::FileBase(file),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
{
|
||||
read(readProperties);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
Mod::File::File(IOStream *stream, bool readProperties,
|
||||
@@ -53,7 +54,8 @@ Mod::File::File(IOStream *stream, bool readProperties,
|
||||
Mod::FileBase(stream),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
{
|
||||
read(readProperties);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
Mod::File::~File()
|
||||
|
||||
@@ -37,18 +37,22 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs a Protracker file from \a file. If \a readProperties
|
||||
* is true the file's audio properties will also be read using
|
||||
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
|
||||
* Constructs a Protracker file from \a file.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs a Protracker file from \a stream. If \a readProperties
|
||||
* is true the file's audio properties will also be read using
|
||||
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
|
||||
* Constructs a Protracker file from \a stream.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "mp4atom.h"
|
||||
@@ -52,10 +48,10 @@ MP4::Atom::Atom(File *file)
|
||||
return;
|
||||
}
|
||||
|
||||
length = header.mid(0, 4).toUInt();
|
||||
length = header.toUInt();
|
||||
|
||||
if (length == 1) {
|
||||
long long longLength = file->readBlock(8).toLongLong();
|
||||
const long long longLength = file->readBlock(8).toLongLong();
|
||||
if (longLength >= 8 && longLength <= 0xFFFFFFFF) {
|
||||
// The atom has a 64-bit length, but it's actually a 32-bit value
|
||||
length = (long)longLength;
|
||||
|
||||
@@ -23,12 +23,9 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include "mp4coverart.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -42,10 +42,11 @@ namespace TagLib {
|
||||
* This describes the image type.
|
||||
*/
|
||||
enum Format {
|
||||
JPEG = TypeJPEG,
|
||||
PNG = TypePNG,
|
||||
BMP = TypeBMP,
|
||||
GIF = TypeGIF
|
||||
JPEG = TypeJPEG,
|
||||
PNG = TypePNG,
|
||||
BMP = TypeBMP,
|
||||
GIF = TypeGIF,
|
||||
Unknown = TypeImplicit,
|
||||
};
|
||||
|
||||
CoverArt(Format format, const ByteVector &data);
|
||||
|
||||
@@ -23,12 +23,9 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "mp4atom.h"
|
||||
#include "mp4tag.h"
|
||||
#include "mp4file.h"
|
||||
@@ -67,14 +64,16 @@ MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle a
|
||||
: TagLib::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, audioPropertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
: TagLib::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, audioPropertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
MP4::File::~File()
|
||||
@@ -88,6 +87,21 @@ MP4::File::tag() const
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
PropertyMap MP4::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
void MP4::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap MP4::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
MP4::Properties *
|
||||
MP4::File::audioProperties() const
|
||||
{
|
||||
|
||||
@@ -49,27 +49,25 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs a MP4 file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an MP4 file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored.
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs a MP4 file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored.
|
||||
* Constructs an MP4 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 audioPropertiesStyle = Properties::Average);
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
@@ -88,6 +86,22 @@ namespace TagLib {
|
||||
*/
|
||||
Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the MP4 audio properties for this file.
|
||||
*/
|
||||
|
||||
@@ -23,12 +23,9 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include "mp4item.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "mp4file.h"
|
||||
@@ -96,8 +92,8 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
long long unit = data.mid(28, 8).toLongLong();
|
||||
long long length = data.mid(36, 8).toLongLong();
|
||||
const long long unit = data.toLongLong(28U);
|
||||
const long long length = data.toLongLong(36U);
|
||||
d->length = unit ? int(length / unit) : 0;
|
||||
}
|
||||
else {
|
||||
@@ -105,8 +101,8 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
unsigned int unit = data.mid(20, 4).toUInt();
|
||||
unsigned int length = data.mid(24, 4).toUInt();
|
||||
const unsigned int unit = data.toUInt(20U);
|
||||
const unsigned int length = data.toUInt(24U);
|
||||
d->length = unit ? length / unit : 0;
|
||||
}
|
||||
|
||||
@@ -118,11 +114,11 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
|
||||
file->seek(atom->offset);
|
||||
data = file->readBlock(atom->length);
|
||||
if(data.mid(20, 4) == "mp4a") {
|
||||
d->channels = data.mid(40, 2).toShort();
|
||||
d->bitsPerSample = data.mid(42, 2).toShort();
|
||||
d->sampleRate = data.mid(46, 4).toUInt();
|
||||
d->channels = data.toShort(40U);
|
||||
d->bitsPerSample = data.toShort(42U);
|
||||
d->sampleRate = data.toUInt(46U);
|
||||
if(data.mid(56, 4) == "esds" && data[64] == 0x03) {
|
||||
long pos = 65;
|
||||
uint pos = 65;
|
||||
if(data.mid(pos, 3) == "\x80\x80\x80") {
|
||||
pos += 3;
|
||||
}
|
||||
@@ -133,16 +129,16 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
|
||||
pos += 3;
|
||||
}
|
||||
pos += 10;
|
||||
d->bitrate = (data.mid(pos, 4).toUInt() + 500) / 1000;
|
||||
d->bitrate = (data.toUInt(pos) + 500) / 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (data.mid(20, 4) == "alac") {
|
||||
if (atom->length == 88 && data.mid(56, 4) == "alac") {
|
||||
d->bitsPerSample = data.at(69);
|
||||
d->channels = data.at(73);
|
||||
d->bitrate = data.mid(80, 4).toUInt() / 1000;
|
||||
d->sampleRate = data.mid(84, 4).toUInt();
|
||||
d->channels = data.at(73);
|
||||
d->bitrate = data.toUInt(80U) / 1000;
|
||||
d->sampleRate = data.toUInt(84U);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,12 +23,9 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "mp4atom.h"
|
||||
#include "mp4tag.h"
|
||||
#include "id3v1genres.h"
|
||||
@@ -113,9 +110,9 @@ MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, boo
|
||||
int i = 0;
|
||||
unsigned int pos = 0;
|
||||
while(pos < data.size()) {
|
||||
int length = data.mid(pos, 4).toUInt();
|
||||
const int length = static_cast<int>(data.toUInt(pos));
|
||||
ByteVector name = data.mid(pos + 4, 4);
|
||||
int flags = data.mid(pos + 8, 4).toUInt();
|
||||
const int flags = static_cast<int>(data.toUInt(pos + 8));
|
||||
if(freeForm && i < 2) {
|
||||
if(i == 0 && name != "mean") {
|
||||
debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
|
||||
@@ -158,7 +155,7 @@ MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, file);
|
||||
if(data.size()) {
|
||||
d->items.insert(atom->name, (int)data[0].toShort());
|
||||
addItem(atom->name, (int)data[0].toShort());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +164,7 @@ MP4::Tag::parseUInt(MP4::Atom *atom, TagLib::File *file)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, file);
|
||||
if(data.size()) {
|
||||
d->items.insert(atom->name, data[0].toUInt());
|
||||
addItem(atom->name, data[0].toUInt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +173,7 @@ MP4::Tag::parseLongLong(MP4::Atom *atom, TagLib::File *file)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, file);
|
||||
if(data.size()) {
|
||||
d->items.insert(atom->name, data[0].toLongLong());
|
||||
addItem(atom->name, data[0].toLongLong());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +182,7 @@ MP4::Tag::parseByte(MP4::Atom *atom, TagLib::File *file)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, file);
|
||||
if(data.size()) {
|
||||
d->items.insert(atom->name, (uchar)data[0].at(0));
|
||||
addItem(atom->name, (uchar)data[0].at(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +192,8 @@ MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file)
|
||||
ByteVectorList data = parseData(atom, file);
|
||||
if(data.size()) {
|
||||
int idx = (int)data[0].toShort();
|
||||
if(!d->items.contains("\251gen") && idx > 0) {
|
||||
d->items.insert("\251gen", StringList(ID3v1::genre(idx - 1)));
|
||||
if(idx > 0) {
|
||||
addItem("\251gen", StringList(ID3v1::genre(idx - 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,9 +203,9 @@ MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, file);
|
||||
if(data.size()) {
|
||||
int a = data[0].mid(2, 2).toShort();
|
||||
int b = data[0].mid(4, 2).toShort();
|
||||
d->items.insert(atom->name, MP4::Item(a, b));
|
||||
const int a = data[0].toShort(2U);
|
||||
const int b = data[0].toShort(4U);
|
||||
addItem(atom->name, MP4::Item(a, b));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +215,7 @@ MP4::Tag::parseBool(MP4::Atom *atom, TagLib::File *file)
|
||||
ByteVectorList data = parseData(atom, file);
|
||||
if(data.size()) {
|
||||
bool value = data[0].size() ? data[0][0] != '\0' : false;
|
||||
d->items.insert(atom->name, value);
|
||||
addItem(atom->name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +228,7 @@ MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags)
|
||||
for(unsigned int i = 0; i < data.size(); i++) {
|
||||
value.append(String(data[i], String::UTF8));
|
||||
}
|
||||
d->items.insert(atom->name, value);
|
||||
addItem(atom->name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +252,7 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
|
||||
}
|
||||
Item item(value);
|
||||
item.setAtomDataType(type);
|
||||
d->items.insert(name, item);
|
||||
addItem(name, item);
|
||||
}
|
||||
else {
|
||||
ByteVectorList value;
|
||||
@@ -264,7 +261,7 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
|
||||
}
|
||||
Item item(value);
|
||||
item.setAtomDataType(type);
|
||||
d->items.insert(name, item);
|
||||
addItem(name, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,21 +273,24 @@ MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
|
||||
ByteVector data = file->readBlock(atom->length - 8);
|
||||
unsigned int pos = 0;
|
||||
while(pos < data.size()) {
|
||||
int length = data.mid(pos, 4).toUInt();
|
||||
const int length = static_cast<int>(data.toUInt(pos));
|
||||
ByteVector name = data.mid(pos + 4, 4);
|
||||
int flags = data.mid(pos + 8, 4).toUInt();
|
||||
const int flags = static_cast<int>(data.toUInt(pos + 8));
|
||||
if(name != "data") {
|
||||
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
|
||||
break;
|
||||
}
|
||||
if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF) {
|
||||
if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF || flags == TypeImplicit) {
|
||||
value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
|
||||
data.mid(pos + 16, length - 16)));
|
||||
}
|
||||
else {
|
||||
debug("MP4: Unknown covr format " + String::number(flags));
|
||||
}
|
||||
pos += length;
|
||||
}
|
||||
if(value.size() > 0)
|
||||
d->items.insert(atom->name, value);
|
||||
addItem(atom->name, value);
|
||||
}
|
||||
|
||||
ByteVector
|
||||
@@ -526,11 +526,11 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
}
|
||||
d->file->seek(atom->offset + 12);
|
||||
ByteVector data = d->file->readBlock(atom->length - 12);
|
||||
unsigned int count = data.mid(0, 4).toUInt();
|
||||
unsigned int count = data.toUInt();
|
||||
d->file->seek(atom->offset + 16);
|
||||
int pos = 4;
|
||||
uint pos = 4;
|
||||
while(count--) {
|
||||
long o = data.mid(pos, 4).toUInt();
|
||||
long o = static_cast<long>(data.toUInt(pos));
|
||||
if(o > offset) {
|
||||
o += delta;
|
||||
}
|
||||
@@ -547,11 +547,11 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
}
|
||||
d->file->seek(atom->offset + 12);
|
||||
ByteVector data = d->file->readBlock(atom->length - 12);
|
||||
unsigned int count = data.mid(0, 4).toUInt();
|
||||
unsigned int count = data.toUInt();
|
||||
d->file->seek(atom->offset + 16);
|
||||
int pos = 4;
|
||||
uint pos = 4;
|
||||
while(count--) {
|
||||
long long o = data.mid(pos, 8).toLongLong();
|
||||
long long o = data.toLongLong(pos);
|
||||
if(o > offset) {
|
||||
o += delta;
|
||||
}
|
||||
@@ -570,10 +570,10 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
atom->offset += delta;
|
||||
}
|
||||
d->file->seek(atom->offset + 9);
|
||||
ByteVector data = d->file->readBlock(atom->offset - 9);
|
||||
unsigned int flags = (ByteVector(1, '\0') + data.mid(0, 3)).toUInt();
|
||||
ByteVector data = d->file->readBlock(atom->length - 9);
|
||||
const unsigned int flags = data.toUInt(0, 3, true);
|
||||
if(flags & 1) {
|
||||
long long o = data.mid(7, 8).toLongLong();
|
||||
long long o = data.toLongLong(7U);
|
||||
if(o > offset) {
|
||||
o += delta;
|
||||
}
|
||||
@@ -756,3 +756,162 @@ MP4::Tag::itemListMap()
|
||||
return d->items;
|
||||
}
|
||||
|
||||
static const char *keyTranslation[][2] = {
|
||||
{ "\251nam", "TITLE" },
|
||||
{ "\251ART", "ARTIST" },
|
||||
{ "\251alb", "ALBUM" },
|
||||
{ "\251cmt", "COMMENT" },
|
||||
{ "\251gen", "GENRE" },
|
||||
{ "\251day", "DATE" },
|
||||
{ "\251wrt", "COMPOSER" },
|
||||
{ "\251grp", "GROUPING" },
|
||||
{ "trkn", "TRACKNUMBER" },
|
||||
{ "disk", "DISCNUMBER" },
|
||||
{ "cpil", "COMPILATION" },
|
||||
{ "tmpo", "BPM" },
|
||||
{ "cprt", "COPYRIGHT" },
|
||||
{ "\251lyr", "LYRICS" },
|
||||
{ "\251too", "ENCODEDBY" },
|
||||
{ "soal", "ALBUMSORT" },
|
||||
{ "soaa", "ALBUMARTISTSORT" },
|
||||
{ "soar", "ARTISTSORT" },
|
||||
{ "sonm", "TITLESORT" },
|
||||
{ "soco", "COMPOSERSORT" },
|
||||
{ "sosn", "SHOWSORT" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "----: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 Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "----:com.apple.iTunes:ASIN", "ASIN" },
|
||||
{ "----:com.apple.iTunes:LABEL", "LABEL" },
|
||||
{ "----:com.apple.iTunes:LYRICIST", "LYRICIST" },
|
||||
{ "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" },
|
||||
{ "----:com.apple.iTunes:REMIXER", "REMIXER" },
|
||||
{ "----:com.apple.iTunes:ENGINEER", "ENGINEER" },
|
||||
{ "----:com.apple.iTunes:PRODUCER", "PRODUCER" },
|
||||
{ "----:com.apple.iTunes:DJMIXER", "DJMIXER" },
|
||||
{ "----:com.apple.iTunes:MIXER", "MIXER" },
|
||||
{ "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" },
|
||||
{ "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" },
|
||||
{ "----:com.apple.iTunes:MOOD", "MOOD" },
|
||||
{ "----:com.apple.iTunes:ISRC", "ISRC" },
|
||||
{ "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" },
|
||||
{ "----:com.apple.iTunes:BARCODE", "BARCODE" },
|
||||
{ "----:com.apple.iTunes:SCRIPT", "SCRIPT" },
|
||||
{ "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" },
|
||||
{ "----:com.apple.iTunes:LICENSE", "LICENSE" },
|
||||
{ "----:com.apple.iTunes:MEDIA", "MEDIA" },
|
||||
};
|
||||
|
||||
PropertyMap MP4::Tag::properties() const
|
||||
{
|
||||
static Map<String, String> keyMap;
|
||||
if(keyMap.isEmpty()) {
|
||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
for(int i = 0; i < numKeys; i++) {
|
||||
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap props;
|
||||
MP4::ItemListMap::ConstIterator it = d->items.begin();
|
||||
for(; it != d->items.end(); ++it) {
|
||||
if(keyMap.contains(it->first)) {
|
||||
String key = keyMap[it->first];
|
||||
if(key == "TRACKNUMBER" || key == "DISCNUMBER") {
|
||||
MP4::Item::IntPair ip = it->second.toIntPair();
|
||||
String value = String::number(ip.first);
|
||||
if(ip.second) {
|
||||
value += "/" + String::number(ip.second);
|
||||
}
|
||||
props[key] = value;
|
||||
}
|
||||
else if(key == "BPM") {
|
||||
props[key] = String::number(it->second.toInt());
|
||||
}
|
||||
else if(key == "COMPILATION") {
|
||||
props[key] = String::number(it->second.toBool());
|
||||
}
|
||||
else {
|
||||
props[key] = it->second.toStringList();
|
||||
}
|
||||
}
|
||||
else {
|
||||
props.unsupportedData().append(it->first);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
void MP4::Tag::removeUnsupportedProperties(const StringList &props)
|
||||
{
|
||||
StringList::ConstIterator it = props.begin();
|
||||
for(; it != props.end(); ++it)
|
||||
d->items.erase(*it);
|
||||
}
|
||||
|
||||
PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
|
||||
{
|
||||
static Map<String, String> reverseKeyMap;
|
||||
if(reverseKeyMap.isEmpty()) {
|
||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
for(int i = 0; i < numKeys; i++) {
|
||||
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap origProps = properties();
|
||||
PropertyMap::ConstIterator it = origProps.begin();
|
||||
for(; it != origProps.end(); ++it) {
|
||||
if(!props.contains(it->first) || props[it->first].isEmpty()) {
|
||||
d->items.erase(reverseKeyMap[it->first]);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap ignoredProps;
|
||||
it = props.begin();
|
||||
for(; it != props.end(); ++it) {
|
||||
if(reverseKeyMap.contains(it->first)) {
|
||||
String name = reverseKeyMap[it->first];
|
||||
if(it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") {
|
||||
int first = 0, second = 0;
|
||||
StringList parts = StringList::split(it->second.front(), "/");
|
||||
if(parts.size() > 0) {
|
||||
first = parts[0].toInt();
|
||||
if(parts.size() > 1) {
|
||||
second = parts[1].toInt();
|
||||
}
|
||||
d->items[name] = MP4::Item(first, second);
|
||||
}
|
||||
}
|
||||
else if(it->first == "BPM") {
|
||||
int value = it->second.front().toInt();
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
else if(it->first == "COMPILATION") {
|
||||
bool value = (it->second.front().toInt() != 0);
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
else {
|
||||
d->items[name] = it->second;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ignoredProps.insert(it->first, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
return ignoredProps;
|
||||
}
|
||||
|
||||
void MP4::Tag::addItem(const String &name, const Item &value)
|
||||
{
|
||||
if(!d->items.contains(name)) {
|
||||
d->items.insert(name, value);
|
||||
}
|
||||
else {
|
||||
debug("MP4: Ignoring duplicate atom \"" + name + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,10 @@ namespace TagLib {
|
||||
|
||||
ItemListMap &itemListMap();
|
||||
|
||||
PropertyMap properties() const;
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
private:
|
||||
AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
|
||||
TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
|
||||
@@ -101,6 +105,8 @@ namespace TagLib {
|
||||
void saveNew(TagLib::ByteVector &data);
|
||||
void saveExisting(TagLib::ByteVector &data, AtomList &path);
|
||||
|
||||
void addItem(const String &name, const Item &value);
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum { MPCAPEIndex, MPCID3v1Index };
|
||||
enum { MPCAPEIndex = 0, MPCID3v1Index = 1 };
|
||||
}
|
||||
|
||||
class MPC::File::FilePrivate
|
||||
@@ -94,14 +94,16 @@ MPC::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
MPC::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
MPC::File::~File()
|
||||
@@ -133,15 +135,11 @@ void MPC::File::removeUnsupportedProperties(const StringList &properties)
|
||||
|
||||
PropertyMap MPC::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->hasAPE)
|
||||
return d->tag.access<APE::Tag>(MPCAPEIndex, false)->setProperties(properties);
|
||||
else if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(MPCID3v1Index, false)->setProperties(properties);
|
||||
else
|
||||
return d->tag.access<APE::Tag>(APE, true)->setProperties(properties);
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<APE::Tag>(MPCID3v1Index, false)->setProperties(properties);
|
||||
return d->tag.access<APE::Tag>(MPCAPEIndex, true)->setProperties(properties);
|
||||
}
|
||||
|
||||
|
||||
MPC::Properties *MPC::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
@@ -258,6 +256,15 @@ void MPC::File::remove(int tags)
|
||||
strip(tags);
|
||||
}
|
||||
|
||||
bool MPC::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->hasID3v1;
|
||||
}
|
||||
|
||||
bool MPC::File::hasAPETag() const
|
||||
{
|
||||
return d->hasAPE;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
|
||||
@@ -84,20 +84,22 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
/*!
|
||||
* Contructs an MPC 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.
|
||||
* Constructs an MPC 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);
|
||||
|
||||
/*!
|
||||
* Contructs an MPC 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.
|
||||
* Constructs an MPC file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -124,8 +126,8 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* As with the export, only one tag is taken into account. If the file
|
||||
* has no tag at all, an APE tag will be created.
|
||||
* Affects only the APEv2 tag which will be created if necessary.
|
||||
* If an ID3v1 tag exists, it will be updated as well.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
@@ -143,28 +145,39 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* if there is no valid ID3v1 tag. If \a create is true it will create
|
||||
* an ID3v1 tag if one does not exist. If there is already an APE tag, the
|
||||
* new ID3v1 tag will be placed after it.
|
||||
* If \a create is false (the default) this returns a null pointer
|
||||
* if there is no valid APE tag. If \a create is true it will create
|
||||
* an APE tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the APE::File and should not be
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
|
||||
* on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasID3v1Tag()
|
||||
*/
|
||||
ID3v1::Tag *ID3v1Tag(bool create = false);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the APE tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* If \a create is false (the default) this may return a null pointer
|
||||
* if there is no valid APE tag. If \a create is true it will create
|
||||
* a APE tag if one does not exist. If there is already an ID3v1 tag, thes
|
||||
* new APE tag will be placed before it.
|
||||
* an APE tag if one does not exist and returns a valid pointer. If
|
||||
* there already be an ID3v1 tag, the new APE tag will be placed before it.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the APE::File and should not be
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an APE tag. Use hasAPETag() to check if the file
|
||||
* on disk actually has an APE tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasAPETag()
|
||||
*/
|
||||
APE::Tag *APETag(bool create = false);
|
||||
|
||||
@@ -185,6 +198,20 @@ namespace TagLib {
|
||||
*/
|
||||
void remove(int tags = AllTags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \see ID3v1Tag()
|
||||
*/
|
||||
bool hasID3v1Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an APE tag.
|
||||
*
|
||||
* \see APETag()
|
||||
*/
|
||||
bool hasAPETag() const;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
@@ -207,7 +207,7 @@ void MPC::Properties::readSV8(File *file)
|
||||
d->sampleFrames = readSize(data.mid(pos), pos);
|
||||
ulong begSilence = readSize(data.mid(pos), pos);
|
||||
|
||||
std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(pos, 2).toUShort(true)));
|
||||
std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.toUShort(pos, true)));
|
||||
pos += 2;
|
||||
|
||||
d->sampleRate = sftable[flags[15] * 4 + flags[14] * 2 + flags[13]];
|
||||
@@ -228,10 +228,10 @@ void MPC::Properties::readSV8(File *file)
|
||||
|
||||
int replayGainVersion = data[0];
|
||||
if(replayGainVersion == 1) {
|
||||
d->trackGain = data.mid(1, 2).toUInt(true);
|
||||
d->trackPeak = data.mid(3, 2).toUInt(true);
|
||||
d->albumGain = data.mid(5, 2).toUInt(true);
|
||||
d->albumPeak = data.mid(7, 2).toUInt(true);
|
||||
d->trackGain = data.toShort(1, true);
|
||||
d->trackPeak = data.toShort(3, true);
|
||||
d->albumGain = data.toShort(5, true);
|
||||
d->albumPeak = data.toShort(7, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,18 +252,18 @@ void MPC::Properties::readSV7(const ByteVector &data)
|
||||
if(d->version < 7)
|
||||
return;
|
||||
|
||||
d->totalFrames = data.mid(4, 4).toUInt(false);
|
||||
d->totalFrames = data.toUInt(4, false);
|
||||
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(8, 4).toUInt(false)));
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(8, false)));
|
||||
d->sampleRate = sftable[flags[17] * 2 + flags[16]];
|
||||
d->channels = 2;
|
||||
|
||||
uint gapless = data.mid(5, 4).toUInt(false);
|
||||
uint gapless = data.toUInt(5, false);
|
||||
|
||||
d->trackGain = data.mid(14,2).toShort(false);
|
||||
d->trackPeak = data.mid(12,2).toUInt(false);
|
||||
d->albumGain = data.mid(18,2).toShort(false);
|
||||
d->albumPeak = data.mid(16,2).toUInt(false);
|
||||
d->trackGain = data.toShort(14, false);
|
||||
d->trackPeak = data.toShort(12, false);
|
||||
d->albumGain = data.toShort(18, false);
|
||||
d->albumPeak = data.toShort(16, false);
|
||||
|
||||
// convert gain info
|
||||
if(d->trackGain != 0) {
|
||||
@@ -293,7 +293,7 @@ void MPC::Properties::readSV7(const ByteVector &data)
|
||||
d->sampleFrames = d->totalFrames * 1152 - 576;
|
||||
}
|
||||
else {
|
||||
uint headerData = data.mid(0, 4).toUInt(false);
|
||||
uint headerData = data.toUInt(0, false);
|
||||
|
||||
d->bitrate = (headerData >> 23) & 0x01ff;
|
||||
d->version = (headerData >> 11) & 0x03ff;
|
||||
@@ -301,9 +301,9 @@ void MPC::Properties::readSV7(const ByteVector &data)
|
||||
d->channels = 2;
|
||||
|
||||
if(d->version >= 5)
|
||||
d->totalFrames = data.mid(4, 4).toUInt(false);
|
||||
d->totalFrames = data.toUInt(4, false);
|
||||
else
|
||||
d->totalFrames = data.mid(6, 2).toUInt(false);
|
||||
d->totalFrames = data.toUShort(6, false);
|
||||
|
||||
d->sampleFrames = d->totalFrames * 1152 - 576;
|
||||
}
|
||||
|
||||
@@ -182,16 +182,26 @@ void ID3v1::Tag::setGenre(const String &s)
|
||||
d->genre = ID3v1::genreIndex(s);
|
||||
}
|
||||
|
||||
void ID3v1::Tag::setYear(uint i)
|
||||
void ID3v1::Tag::setYear(TagLib::uint i)
|
||||
{
|
||||
d->year = i > 0 ? String::number(i) : String::null;
|
||||
}
|
||||
|
||||
void ID3v1::Tag::setTrack(uint i)
|
||||
void ID3v1::Tag::setTrack(TagLib::uint i)
|
||||
{
|
||||
d->track = i < 256 ? i : 0;
|
||||
}
|
||||
|
||||
TagLib::uint ID3v1::Tag::genreNumber() const
|
||||
{
|
||||
return d->genre;
|
||||
}
|
||||
|
||||
void ID3v1::Tag::setGenreNumber(TagLib::uint i)
|
||||
{
|
||||
d->genre = i < 256 ? i : 255;
|
||||
}
|
||||
|
||||
void ID3v1::Tag::setStringHandler(const StringHandler *handler)
|
||||
{
|
||||
if (handler)
|
||||
|
||||
@@ -140,16 +140,31 @@ namespace TagLib {
|
||||
virtual String album() const;
|
||||
virtual String comment() const;
|
||||
virtual String genre() const;
|
||||
virtual uint year() const;
|
||||
virtual uint track() const;
|
||||
virtual TagLib::uint year() const;
|
||||
virtual TagLib::uint track() const;
|
||||
|
||||
virtual void setTitle(const String &s);
|
||||
virtual void setArtist(const String &s);
|
||||
virtual void setAlbum(const String &s);
|
||||
virtual void setComment(const String &s);
|
||||
virtual void setGenre(const String &s);
|
||||
virtual void setYear(uint i);
|
||||
virtual void setTrack(uint i);
|
||||
virtual void setYear(TagLib::uint i);
|
||||
virtual void setTrack(TagLib::uint i);
|
||||
|
||||
/*!
|
||||
* Returns the genre in number.
|
||||
*
|
||||
* /note Normally 255 indicates that this tag contains no genre.
|
||||
*/
|
||||
TagLib::uint genreNumber() const;
|
||||
|
||||
/*!
|
||||
* Sets the genre in number to \a i.
|
||||
*
|
||||
* /note Valid value is from 0 up to 255. Normally 255 indicates that
|
||||
* this tag contains no genre.
|
||||
*/
|
||||
void setGenreNumber(TagLib::uint i);
|
||||
|
||||
/*!
|
||||
* Sets the string handler that decides how the ID3v1 data will be
|
||||
|
||||
@@ -109,7 +109,7 @@ void PopularimeterFrame::parseFields(const ByteVector &data)
|
||||
if(pos < size) {
|
||||
d->rating = (unsigned char)(data[pos++]);
|
||||
if(pos < size) {
|
||||
d->counter = data.mid(pos, 4).toUInt();
|
||||
d->counter = data.toUInt(static_cast<uint>(pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ void RelativeVolumeFrame::parseFields(const ByteVector &data)
|
||||
|
||||
ChannelData &channel = d->channels[type];
|
||||
|
||||
channel.volumeAdjustment = data.mid(pos, 2).toShort();
|
||||
channel.volumeAdjustment = data.toShort(static_cast<uint>(pos));
|
||||
pos += 2;
|
||||
|
||||
channel.peakVolume.bitsRepresentingPeak = data[pos];
|
||||
|
||||
@@ -381,18 +381,12 @@ void UserTextIdentificationFrame::setDescription(const String &s)
|
||||
|
||||
PropertyMap UserTextIdentificationFrame::asProperties() const
|
||||
{
|
||||
String tagName = description();
|
||||
|
||||
PropertyMap map;
|
||||
String key = tagName.upper();
|
||||
if(key.isNull()) // this frame's description is not a valid PropertyMap key -> add to unsupported list
|
||||
map.unsupportedData().append(L"TXXX/" + description());
|
||||
else {
|
||||
StringList v = fieldList();
|
||||
for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it)
|
||||
if(*it != description())
|
||||
map.insert(key, *it);
|
||||
}
|
||||
String tagName = txxxToKey(description());
|
||||
StringList v = fieldList();
|
||||
for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it)
|
||||
if(it != v.begin())
|
||||
map.insert(tagName, *it);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "id3v2tag.h"
|
||||
#include "uniquefileidentifierframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -87,6 +89,34 @@ String UniqueFileIdentifierFrame::toString() const
|
||||
return String::null;
|
||||
}
|
||||
|
||||
PropertyMap UniqueFileIdentifierFrame::asProperties() const
|
||||
{
|
||||
PropertyMap map;
|
||||
if(d->owner == "http://musicbrainz.org") {
|
||||
map.insert("MUSICBRAINZ_TRACKID", String(d->identifier));
|
||||
}
|
||||
else {
|
||||
map.unsupportedData().append(frameID() + String("/") + d->owner);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
UniqueFileIdentifierFrame *UniqueFileIdentifierFrame::findByOwner(const ID3v2::Tag *tag, const String &o) // static
|
||||
{
|
||||
ID3v2::FrameList comments = tag->frameList("UFID");
|
||||
|
||||
for(ID3v2::FrameList::ConstIterator it = comments.begin();
|
||||
it != comments.end();
|
||||
++it)
|
||||
{
|
||||
UniqueFileIdentifierFrame *frame = dynamic_cast<UniqueFileIdentifierFrame *>(*it);
|
||||
if(frame && frame->owner() == o)
|
||||
return frame;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UniqueFileIdentifierFrame::parseFields(const ByteVector &data)
|
||||
{
|
||||
if(data.size() < 1) {
|
||||
|
||||
@@ -94,6 +94,16 @@ namespace TagLib {
|
||||
|
||||
virtual String toString() const;
|
||||
|
||||
PropertyMap asProperties() const;
|
||||
|
||||
/*!
|
||||
* UFID frames each have a unique owner. This searches for a UFID
|
||||
* frame with the owner \a o and returns a pointer to it.
|
||||
*
|
||||
* \see owner()
|
||||
*/
|
||||
static UniqueFileIdentifierFrame *findByOwner(const Tag *tag, const String &o);
|
||||
|
||||
protected:
|
||||
virtual void parseFields(const ByteVector &data);
|
||||
virtual ByteVector renderFields() const;
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "frames/urllinkframe.h"
|
||||
#include "frames/unsynchronizedlyricsframe.h"
|
||||
#include "frames/commentsframe.h"
|
||||
#include "frames/uniquefileidentifierframe.h"
|
||||
#include "frames/unknownframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -120,16 +121,20 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
|
||||
TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8);
|
||||
frame->setText(values);
|
||||
return frame;
|
||||
} else if(values.size() == 1){ // URL frame (not WXXX); support only one value
|
||||
} else if((frameID[0] == 'W') && (values.size() == 1)){ // URL frame (not WXXX); support only one value
|
||||
UrlLinkFrame* frame = new UrlLinkFrame(frameID);
|
||||
frame->setUrl(values.front());
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
if(key == "MUSICBRAINZ_TRACKID" && values.size() == 1) {
|
||||
UniqueFileIdentifierFrame *frame = new UniqueFileIdentifierFrame("http://musicbrainz.org", values.front().data(String::UTF8));
|
||||
return frame;
|
||||
}
|
||||
// now we check if it's one of the "special" cases:
|
||||
// -LYRICS: depending on the number of values, use USLT or TXXX (with description=LYRICS)
|
||||
if((key == "LYRICS" || key.startsWith(lyricsPrefix)) && values.size() == 1){
|
||||
UnsynchronizedLyricsFrame *frame = new UnsynchronizedLyricsFrame();
|
||||
UnsynchronizedLyricsFrame *frame = new UnsynchronizedLyricsFrame(String::UTF8);
|
||||
frame->setDescription(key == "LYRICS" ? key : key.substr(lyricsPrefix.size()));
|
||||
frame->setText(values.front());
|
||||
return frame;
|
||||
@@ -144,12 +149,14 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
|
||||
// -COMMENT: depending on the number of values, use COMM or TXXX (with description=COMMENT)
|
||||
if((key == "COMMENT" || key.startsWith(commentPrefix)) && values.size() == 1){
|
||||
CommentsFrame *frame = new CommentsFrame(String::UTF8);
|
||||
frame->setDescription(key == "COMMENT" ? key : key.substr(commentPrefix.size()));
|
||||
if (key != "COMMENT"){
|
||||
frame->setDescription(key.substr(commentPrefix.size()));
|
||||
}
|
||||
frame->setText(values.front());
|
||||
return frame;
|
||||
}
|
||||
// if non of the above cases apply, we use a TXXX frame with the key as description
|
||||
return new UserTextIdentificationFrame(key, values, String::UTF8);
|
||||
return new UserTextIdentificationFrame(keyToTXXX(key), values, String::UTF8);
|
||||
}
|
||||
|
||||
Frame::~Frame()
|
||||
@@ -348,7 +355,7 @@ static const char *frameTranslation[][2] = {
|
||||
{ "TLAN", "LANGUAGE" },
|
||||
{ "TLEN", "LENGTH" },
|
||||
//{ "TMCL", "MUSICIANCREDITS" }, handled separately
|
||||
{ "TMED", "MEDIATYPE" },
|
||||
{ "TMED", "MEDIA" },
|
||||
{ "TMOO", "MOOD" },
|
||||
{ "TOAL", "ORIGINALALBUM" },
|
||||
{ "TOFN", "ORIGINALFILENAME" },
|
||||
@@ -361,7 +368,7 @@ static const char *frameTranslation[][2] = {
|
||||
{ "TPE4", "REMIXER" }, // could also be ARRANGER
|
||||
{ "TPOS", "DISCNUMBER" },
|
||||
{ "TPRO", "PRODUCEDNOTICE" },
|
||||
{ "TPUB", "PUBLISHER" },
|
||||
{ "TPUB", "LABEL" },
|
||||
{ "TRCK", "TRACKNUMBER" },
|
||||
{ "TRSN", "RADIOSTATION" },
|
||||
{ "TRSO", "RADIOSTATIONOWNER" },
|
||||
@@ -385,6 +392,18 @@ static const char *frameTranslation[][2] = {
|
||||
//{ "USLT", "LYRICS" }, handled specially
|
||||
};
|
||||
|
||||
static const TagLib::uint txxxFrameTranslationSize = 7;
|
||||
static const char *txxxFrameTranslation[][2] = {
|
||||
{ "MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "Acoustid Id", "ACOUSTID_ID" },
|
||||
{ "Acoustid Fingerprint", "ACOUSTID_FINGERPRINT" },
|
||||
{ "MusicIP PUID", "MUSICIP_PUID" },
|
||||
};
|
||||
|
||||
Map<ByteVector, String> &idMap()
|
||||
{
|
||||
static Map<ByteVector, String> m;
|
||||
@@ -394,6 +413,18 @@ Map<ByteVector, String> &idMap()
|
||||
return m;
|
||||
}
|
||||
|
||||
Map<String, String> &txxxMap()
|
||||
{
|
||||
static Map<String, String> m;
|
||||
if(m.isEmpty()) {
|
||||
for(size_t i = 0; i < txxxFrameTranslationSize; ++i) {
|
||||
String key = String(txxxFrameTranslation[i][0]).upper();
|
||||
m[key] = txxxFrameTranslation[i][1];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
// list of deprecated frames and their successors
|
||||
static const TagLib::uint deprecatedFramesSize = 4;
|
||||
static const char *deprecatedFrames[][2] = {
|
||||
@@ -433,6 +464,26 @@ ByteVector Frame::keyToFrameID(const String &s)
|
||||
return ByteVector::null;
|
||||
}
|
||||
|
||||
String Frame::txxxToKey(const String &description)
|
||||
{
|
||||
Map<String, String> &m = txxxMap();
|
||||
String d = description.upper();
|
||||
if(m.contains(d))
|
||||
return m[d];
|
||||
return d;
|
||||
}
|
||||
|
||||
String Frame::keyToTXXX(const String &s)
|
||||
{
|
||||
static Map<String, String> m;
|
||||
if(m.isEmpty())
|
||||
for(size_t i = 0; i < txxxFrameTranslationSize; ++i)
|
||||
m[txxxFrameTranslation[i][1]] = txxxFrameTranslation[i][0];
|
||||
if(m.contains(s.upper()))
|
||||
return m[s];
|
||||
return s;
|
||||
}
|
||||
|
||||
PropertyMap Frame::asProperties() const
|
||||
{
|
||||
if(dynamic_cast< const UnknownFrame *>(this)) {
|
||||
@@ -454,6 +505,8 @@ PropertyMap Frame::asProperties() const
|
||||
return dynamic_cast< const CommentsFrame* >(this)->asProperties();
|
||||
else if(id == "USLT")
|
||||
return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties();
|
||||
else if(id == "UFID")
|
||||
return dynamic_cast< const UniqueFileIdentifierFrame* >(this)->asProperties();
|
||||
PropertyMap m;
|
||||
m.unsupportedData().append(id);
|
||||
return m;
|
||||
@@ -589,7 +642,7 @@ void Frame::Header::setData(const ByteVector &data, uint version)
|
||||
return;
|
||||
}
|
||||
|
||||
d->frameSize = data.mid(3, 3).toUInt();
|
||||
d->frameSize = data.toUInt(3, 3, true);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -617,7 +670,7 @@ void Frame::Header::setData(const ByteVector &data, uint version)
|
||||
// Set the size -- the frame size is the four bytes starting at byte four in
|
||||
// the frame header (structure 4)
|
||||
|
||||
d->frameSize = data.mid(4, 4).toUInt();
|
||||
d->frameSize = data.toUInt(4U);
|
||||
|
||||
{ // read the first byte of flags
|
||||
std::bitset<8> flags(data[8]);
|
||||
@@ -664,7 +717,7 @@ void Frame::Header::setData(const ByteVector &data, uint version)
|
||||
// iTunes writes v2.4 tags with v2.3-like frame sizes
|
||||
if(d->frameSize > 127) {
|
||||
if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) {
|
||||
unsigned int uintSize = data.mid(4, 4).toUInt();
|
||||
unsigned int uintSize = data.toUInt(4U);
|
||||
if(isValidFrameID(data.mid(uintSize + 10, 4))) {
|
||||
d->frameSize = uintSize;
|
||||
}
|
||||
|
||||
@@ -274,6 +274,15 @@ namespace TagLib {
|
||||
*/
|
||||
static String frameIDToKey(const ByteVector &);
|
||||
|
||||
/*!
|
||||
* Returns an appropriate TXXX frame description for the given free-form tag key.
|
||||
*/
|
||||
static String keyToTXXX(const String &);
|
||||
|
||||
/*!
|
||||
* Returns a free-form tag name for the given ID3 frame description.
|
||||
*/
|
||||
static String txxxToKey(const String &);
|
||||
|
||||
/*!
|
||||
* This helper function splits the PropertyMap \a original into three ProperytMaps
|
||||
|
||||
@@ -99,7 +99,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
|
||||
// A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
|
||||
// characters. Also make sure that there is data in the frame.
|
||||
|
||||
if(!frameID.size() == (version < 3 ? 3 : 4) ||
|
||||
if(frameID.size() != (version < 3 ? 3 : 4) ||
|
||||
header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) ||
|
||||
header->frameSize() > data.size())
|
||||
{
|
||||
@@ -107,6 +107,17 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef NO_ITUNES_HACKS
|
||||
if(version == 3 && frameID.size() == 4 && frameID[3] == '\0') {
|
||||
// iTunes v2.3 tags store v2.2 frames - convert now
|
||||
frameID = frameID.mid(0, 3);
|
||||
header->setFrameID(frameID);
|
||||
header->setVersion(2);
|
||||
updateFrame(header);
|
||||
header->setVersion(3);
|
||||
}
|
||||
#endif
|
||||
|
||||
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
|
||||
if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
|
||||
delete header;
|
||||
@@ -125,7 +136,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
|
||||
// TagLib doesn't mess with encrypted frames, so just treat them
|
||||
// as unknown frames.
|
||||
|
||||
#if HAVE_ZLIB == 0
|
||||
#if !defined(HAVE_ZLIB) || HAVE_ZLIB == 0
|
||||
if(header->compression()) {
|
||||
debug("Compressed frames are currently not supported.");
|
||||
return new UnknownFrame(data, header);
|
||||
|
||||
@@ -49,7 +49,14 @@ TagLib::uint SynchData::toUInt(const ByteVector &data)
|
||||
// Invalid data; assume this was created by some buggy software that just
|
||||
// put normal integers here rather than syncsafe ones, and try it that
|
||||
// way.
|
||||
sum = (data.size() > 4) ? data.mid(0, 4).toUInt() : data.toUInt();
|
||||
if(data.size() >= 4) {
|
||||
sum = data.toUInt(0, true);
|
||||
}
|
||||
else {
|
||||
ByteVector tmp(data);
|
||||
tmp.resize(4);
|
||||
sum = tmp.toUInt(0, true);
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tfile.h>
|
||||
|
||||
#include "id3v2tag.h"
|
||||
@@ -379,10 +383,12 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties)
|
||||
for(FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++)
|
||||
if (dynamic_cast<const UnknownFrame *>(*fit) != 0)
|
||||
removeFrame(*fit);
|
||||
} else if(it->size() == 4){
|
||||
}
|
||||
else if(it->size() == 4){
|
||||
ByteVector id = it->data(String::Latin1);
|
||||
removeFrames(id);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
ByteVector id = it->substr(0,4).data(String::Latin1);
|
||||
if(it->size() <= 5)
|
||||
continue; // invalid specification
|
||||
@@ -396,6 +402,8 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties)
|
||||
frame = CommentsFrame::findByDescription(this, description);
|
||||
else if(id == "USLT")
|
||||
frame = UnsynchronizedLyricsFrame::findByDescription(this, description);
|
||||
else if(id == "UFID")
|
||||
frame = UniqueFileIdentifierFrame::findByOwner(this, description);
|
||||
if(frame)
|
||||
removeFrame(frame);
|
||||
}
|
||||
|
||||
@@ -156,16 +156,13 @@ void MPEG::File::removeUnsupportedProperties(const StringList &properties)
|
||||
else if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap MPEG::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->setProperties(properties);
|
||||
else if(d->hasAPE)
|
||||
return d->tag.access<APE::Tag>(APEIndex, false)->setProperties(properties);
|
||||
else if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
|
||||
else
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
|
||||
if(d->hasID3v1)
|
||||
// update ID3v1 tag if it exists, but ignore the return value
|
||||
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
|
||||
}
|
||||
|
||||
MPEG::Properties *MPEG::File::audioProperties() const
|
||||
@@ -189,6 +186,11 @@ bool MPEG::File::save(int tags, bool stripOthers)
|
||||
}
|
||||
|
||||
bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version)
|
||||
{
|
||||
return save(tags, stripOthers, id3v2Version, true);
|
||||
}
|
||||
|
||||
bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags)
|
||||
{
|
||||
if(tags == NoTags && stripOthers)
|
||||
return strip(AllTags);
|
||||
@@ -206,14 +208,19 @@ bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the tags if we've been asked to. Copy the values from the tag that
|
||||
// does exist into the new tag, except if the existing tag is to be stripped.
|
||||
// Create the tags if we've been asked to.
|
||||
|
||||
if((tags & ID3v2) && ID3v1Tag() && !(stripOthers && !(tags & ID3v1)))
|
||||
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
|
||||
if (duplicateTags) {
|
||||
|
||||
if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2)))
|
||||
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
|
||||
// Copy the values from the tag that does exist into the new tag,
|
||||
// except if the existing tag is to be stripped.
|
||||
|
||||
if((tags & ID3v2) && ID3v1Tag() && !(stripOthers && !(tags & ID3v1)))
|
||||
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
|
||||
|
||||
if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2)))
|
||||
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
|
||||
@@ -437,6 +444,21 @@ long MPEG::File::lastFrameOffset()
|
||||
return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length());
|
||||
}
|
||||
|
||||
bool MPEG::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->hasID3v1;
|
||||
}
|
||||
|
||||
bool MPEG::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->hasID3v2;
|
||||
}
|
||||
|
||||
bool MPEG::File::hasAPETag() const
|
||||
{
|
||||
return d->hasAPE;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -634,9 +656,6 @@ void MPEG::File::findAPE()
|
||||
|
||||
bool MPEG::File::secondSynchByte(char byte)
|
||||
{
|
||||
if(uchar(byte) == 0xff)
|
||||
return false;
|
||||
|
||||
std::bitset<8> b(byte);
|
||||
|
||||
// check to see if the byte matches 111xxxxx
|
||||
|
||||
@@ -71,9 +71,10 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
/*!
|
||||
* Contructs an MPEG 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.
|
||||
* Constructs an MPEG 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.
|
||||
*
|
||||
* \deprecated This constructor will be dropped in favor of the one below
|
||||
* in a future version.
|
||||
@@ -82,28 +83,31 @@ namespace TagLib {
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs an MPEG 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. The frames will be created using
|
||||
* Constructs an MPEG file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \deprecated This constructor will be dropped in favor of the one below
|
||||
* in a future version.
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs an MPEG file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored. The frames will be created using
|
||||
* \a frameFactory.
|
||||
* Constructs an MPEG file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -133,7 +137,7 @@ namespace TagLib {
|
||||
virtual Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Implements the reading part of the unified property interface.
|
||||
* If the file contains more than one tag, only the
|
||||
* first one (in the order ID3v2, APE, ID3v1) will be converted to the
|
||||
* PropertyMap.
|
||||
@@ -143,9 +147,12 @@ namespace TagLib {
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function.
|
||||
* As with the export, only one tag is taken into account. If the file
|
||||
* has no tag at all, ID3v2 will be created.
|
||||
* Implements the writing part of the unified tag dictionary interface.
|
||||
* In order to avoid problems with deprecated tag formats, this method
|
||||
* always creates an ID3v2 tag if necessary.
|
||||
* If an ID3v1 tag exists, it will be updated as well, within the
|
||||
* limitations of that format.
|
||||
* The returned PropertyMap refers to the ID3v2 tag only.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
@@ -210,42 +217,78 @@ namespace TagLib {
|
||||
// BIC: combine with the above method
|
||||
bool save(int tags, bool stripOthers, int id3v2Version);
|
||||
|
||||
/*!
|
||||
* Save the file. This will attempt to save all of the tag types that are
|
||||
* specified by OR-ing together TagTypes values. The save() method above
|
||||
* uses AllTags. This returns true if saving was successful.
|
||||
*
|
||||
* If \a stripOthers is true this strips all tags not included in the mask,
|
||||
* but does not modify them in memory, so later calls to save() which make
|
||||
* use of these tags will remain valid. This also strips empty tags.
|
||||
*
|
||||
* The \a id3v2Version parameter specifies the version of the saved
|
||||
* ID3v2 tag. It can be either 4 or 3.
|
||||
*
|
||||
* If \a duplicateTags is true and at least one tag -- ID3v1 or ID3v2 --
|
||||
* exists this will duplicate its content into the other tag.
|
||||
*/
|
||||
// BIC: combine with the above method
|
||||
bool save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v2 tag of the file.
|
||||
*
|
||||
* A tag will always be returned, regardless of whether there is a
|
||||
* tag in the file or not. Use ID3v2::Tag::isEmpty() to check if
|
||||
* the tag contains no data.
|
||||
* If \a create is false (the default) this may return a null pointer
|
||||
* if there is no valid ID3v2 tag. If \a create is true it will create
|
||||
* an ID3v2 tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
|
||||
* on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasID3v2Tag()
|
||||
*/
|
||||
ID3v2::Tag *ID3v2Tag(bool create = false);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
*
|
||||
* A tag will always be returned, regardless of whether there is a
|
||||
* tag in the file or not. Use Tag::isEmpty() to check if
|
||||
* the tag contains no data.
|
||||
* If \a create is false (the default) this may return a null pointer
|
||||
* if there is no valid ID3v1 tag. If \a create is true it will create
|
||||
* an ID3v1 tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
|
||||
* on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasID3v1Tag()
|
||||
*/
|
||||
ID3v1::Tag *ID3v1Tag(bool create = false);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the APE tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* If \a create is false (the default) this may return a null pointer
|
||||
* if there is no valid APE tag. If \a create is true it will create
|
||||
* an APE tag if one does not exist.
|
||||
* an APE tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an APE tag. Use hasAPETag() to check if the file
|
||||
* on disk actually has an APE tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasAPETag()
|
||||
*/
|
||||
APE::Tag *APETag(bool create = false);
|
||||
|
||||
@@ -301,6 +344,27 @@ namespace TagLib {
|
||||
*/
|
||||
long lastFrameOffset();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \see ID3v1Tag()
|
||||
*/
|
||||
bool hasID3v1Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an APE tag.
|
||||
*
|
||||
* \see APETag()
|
||||
*/
|
||||
bool hasAPETag() const;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <tbytevector.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
|
||||
#include "mpegheader.h"
|
||||
|
||||
|
||||
@@ -108,8 +108,8 @@ void MPEG::XingHeader::parse(const ByteVector &data)
|
||||
return;
|
||||
}
|
||||
|
||||
d->frames = data.mid(8, 4).toUInt();
|
||||
d->size = data.mid(12, 4).toUInt();
|
||||
d->frames = data.toUInt(8U);
|
||||
d->size = data.toUInt(12U);
|
||||
|
||||
d->valid = true;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <tbytevector.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
#include <xiphcomment.h>
|
||||
#include "oggflacfile.h"
|
||||
@@ -72,14 +73,16 @@ Ogg::FLAC::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : Ogg::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
Ogg::FLAC::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
Ogg::FLAC::File::~File()
|
||||
@@ -92,6 +95,16 @@ Ogg::XiphComment *Ogg::FLAC::File::tag() const
|
||||
return d->comment;
|
||||
}
|
||||
|
||||
PropertyMap Ogg::FLAC::File::properties() const
|
||||
{
|
||||
return d->comment->properties();
|
||||
}
|
||||
|
||||
PropertyMap Ogg::FLAC::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->comment->setProperties(properties);
|
||||
}
|
||||
|
||||
Properties *Ogg::FLAC::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
@@ -124,6 +137,11 @@ bool Ogg::FLAC::File::save()
|
||||
return Ogg::File::save();
|
||||
}
|
||||
|
||||
bool Ogg::FLAC::File::hasXiphComment() const
|
||||
{
|
||||
return d->hasXiphComment;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -228,7 +246,7 @@ void Ogg::FLAC::File::scan()
|
||||
|
||||
char blockType = header[0] & 0x7f;
|
||||
bool lastBlock = (header[0] & 0x80) != 0;
|
||||
uint length = header.mid(1, 3).toUInt();
|
||||
uint length = header.toUInt(1, 3, true);
|
||||
overhead += length;
|
||||
|
||||
// Sanity: First block should be the stream_info metadata
|
||||
@@ -238,7 +256,7 @@ void Ogg::FLAC::File::scan()
|
||||
return;
|
||||
}
|
||||
|
||||
d->streamInfoData = metadataHeader.mid(4,length);
|
||||
d->streamInfoData = metadataHeader.mid(4, length);
|
||||
|
||||
// Search through the remaining metadata
|
||||
|
||||
@@ -251,7 +269,7 @@ void Ogg::FLAC::File::scan()
|
||||
header = metadataHeader.mid(0, 4);
|
||||
blockType = header[0] & 0x7f;
|
||||
lastBlock = (header[0] & 0x80) != 0;
|
||||
length = header.mid(1, 3).toUInt();
|
||||
length = header.toUInt(1, 3, true);
|
||||
overhead += length;
|
||||
|
||||
if(blockType == 1) {
|
||||
|
||||
@@ -64,20 +64,22 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs an Ogg/FLAC file from \a file. If \a readProperties is true
|
||||
* the file's audio properties will also be read using \a propertiesStyle.
|
||||
* If false, \a propertiesStyle is ignored.
|
||||
* Constructs an Ogg/FLAC 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);
|
||||
|
||||
/*!
|
||||
* Contructs an Ogg/FLAC file from \a file. If \a readProperties is true
|
||||
* the file's audio properties will also be read using \a propertiesStyle.
|
||||
* If false, \a propertiesStyle is ignored.
|
||||
* Constructs an Ogg/FLAC file from \a stream. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -89,6 +91,16 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file. This will always be a XiphComment.
|
||||
*
|
||||
* \note This always returns a valid pointer regardless of whether or not
|
||||
* the file on disk has a XiphComment. Use hasXiphComment() to check if
|
||||
* the file on disk actually has a XiphComment.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasXiphComment()
|
||||
*/
|
||||
virtual XiphComment *tag() const;
|
||||
|
||||
@@ -98,6 +110,20 @@ namespace TagLib {
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This forwards directly to XiphComment::properties().
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function.
|
||||
* Like properties(), this is a forwarder to the file's XiphComment.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
|
||||
/*!
|
||||
* Save the file. This will primarily save and update the XiphComment.
|
||||
* Returns true if the save is successful.
|
||||
@@ -110,6 +136,13 @@ namespace TagLib {
|
||||
*/
|
||||
long streamLength();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has a XiphComment.
|
||||
*
|
||||
* \see tag()
|
||||
*/
|
||||
bool hasXiphComment() const;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
@@ -82,9 +82,7 @@ namespace TagLib {
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Contructs an Ogg 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.
|
||||
* Constructs an Ogg file from \a file.
|
||||
*
|
||||
* \note This constructor is protected since Ogg::File shouldn't be
|
||||
* instantiated directly but rather should be used through the codec
|
||||
@@ -93,9 +91,7 @@ namespace TagLib {
|
||||
File(FileName file);
|
||||
|
||||
/*!
|
||||
* Contructs an Ogg 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.
|
||||
* Constructs an Ogg file from \a stream.
|
||||
*
|
||||
* \note This constructor is protected since Ogg::File shouldn't be
|
||||
* instantiated directly but rather should be used through the codec
|
||||
|
||||
@@ -255,9 +255,9 @@ void Ogg::PageHeader::read()
|
||||
d->firstPageOfStream = flags.test(1);
|
||||
d->lastPageOfStream = flags.test(2);
|
||||
|
||||
d->absoluteGranularPosition = data.mid(6, 8).toLongLong(false);
|
||||
d->streamSerialNumber = data.mid(14, 4).toUInt(false);
|
||||
d->pageSequenceNumber = data.mid(18, 4).toUInt(false);
|
||||
d->absoluteGranularPosition = data.toLongLong(6, false);
|
||||
d->streamSerialNumber = data.toUInt(14, false);
|
||||
d->pageSequenceNumber = data.toUInt(18, false);
|
||||
|
||||
// Byte number 27 is the number of page segments, which is the only variable
|
||||
// length portion of the page header. After reading the number of page
|
||||
|
||||
139
taglib/ogg/opus/opusfile.cpp
Normal file
139
taglib/ogg/opus/opusfile.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2012 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
|
||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||
email : wheeler@kde.org
|
||||
(original Vorbis implementation)
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 <bitset>
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
#include "opusfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace TagLib::Ogg;
|
||||
|
||||
class Opus::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
comment(0),
|
||||
properties(0) {}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete comment;
|
||||
delete properties;
|
||||
}
|
||||
|
||||
Ogg::XiphComment *comment;
|
||||
Properties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Opus::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) :
|
||||
Ogg::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
Opus::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) :
|
||||
Ogg::File(stream),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
Opus::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
Ogg::XiphComment *Opus::File::tag() const
|
||||
{
|
||||
return d->comment;
|
||||
}
|
||||
|
||||
PropertyMap Opus::File::properties() const
|
||||
{
|
||||
return d->comment->properties();
|
||||
}
|
||||
|
||||
PropertyMap Opus::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->comment->setProperties(properties);
|
||||
}
|
||||
|
||||
Opus::Properties *Opus::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
}
|
||||
|
||||
bool Opus::File::save()
|
||||
{
|
||||
if(!d->comment)
|
||||
d->comment = new Ogg::XiphComment;
|
||||
|
||||
setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false));
|
||||
|
||||
return Ogg::File::save();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Opus::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||
{
|
||||
ByteVector opusHeaderData = packet(0);
|
||||
|
||||
if(!opusHeaderData.startsWith("OpusHead")) {
|
||||
setValid(false);
|
||||
debug("Opus::File::read() -- invalid Opus identification header");
|
||||
return;
|
||||
}
|
||||
|
||||
ByteVector commentHeaderData = packet(1);
|
||||
|
||||
if(!commentHeaderData.startsWith("OpusTags")) {
|
||||
setValid(false);
|
||||
debug("Opus::File::read() -- invalid Opus tags header");
|
||||
return;
|
||||
}
|
||||
|
||||
d->comment = new Ogg::XiphComment(commentHeaderData.mid(8));
|
||||
|
||||
if(readProperties)
|
||||
d->properties = new Properties(this, propertiesStyle);
|
||||
}
|
||||
124
taglib/ogg/opus/opusfile.h
Normal file
124
taglib/ogg/opus/opusfile.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2012 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
|
||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||
email : wheeler@kde.org
|
||||
(original Vorbis implementation)
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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_OPUSFILE_H
|
||||
#define TAGLIB_OPUSFILE_H
|
||||
|
||||
#include "oggfile.h"
|
||||
#include "xiphcomment.h"
|
||||
|
||||
#include "opusproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace Ogg {
|
||||
|
||||
//! A namespace containing classes for Opus metadata
|
||||
|
||||
namespace Opus {
|
||||
|
||||
//! An implementation of Ogg::File with Opus specific methods
|
||||
|
||||
/*!
|
||||
* This is the central class in the Ogg Opus metadata processing collection
|
||||
* of classes. It's built upon Ogg::File which handles processing of the Ogg
|
||||
* logical bitstream and breaking it down into pages which are handled by
|
||||
* the codec implementations, in this case Opus specifically.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public Ogg::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Constructs an Opus 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 Opus 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 the XiphComment for this file. XiphComment implements the tag
|
||||
* interface, so this serves as the reimplementation of
|
||||
* TagLib::File::tag().
|
||||
*/
|
||||
virtual Ogg::XiphComment *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This forwards directly to XiphComment::properties().
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function.
|
||||
* Like properties(), this is a forwarder to the file's XiphComment.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the Opus::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
|
||||
virtual bool save();
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
161
taglib/ogg/opus/opusproperties.cpp
Normal file
161
taglib/ogg/opus/opusproperties.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2012 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
|
||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||
email : wheeler@kde.org
|
||||
(original Vorbis implementation)
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 <oggpageheader.h>
|
||||
|
||||
#include "opusproperties.h"
|
||||
#include "opusfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace TagLib::Ogg;
|
||||
|
||||
class Opus::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate(File *f, ReadStyle s) :
|
||||
file(f),
|
||||
style(s),
|
||||
length(0),
|
||||
inputSampleRate(0),
|
||||
channels(0),
|
||||
opusVersion(0) {}
|
||||
|
||||
File *file;
|
||||
ReadStyle style;
|
||||
int length;
|
||||
int inputSampleRate;
|
||||
int channels;
|
||||
int opusVersion;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Opus::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
|
||||
{
|
||||
d = new PropertiesPrivate(file, style);
|
||||
read();
|
||||
}
|
||||
|
||||
Opus::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int Opus::Properties::length() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int Opus::Properties::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Opus::Properties::sampleRate() const
|
||||
{
|
||||
// Opus can decode any stream at a sample rate of 8, 12, 16, 24, or 48 kHz,
|
||||
// so there is no single sample rate. Let's assume it's the highest
|
||||
// possible.
|
||||
return 48000;
|
||||
}
|
||||
|
||||
int Opus::Properties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int Opus::Properties::inputSampleRate() const
|
||||
{
|
||||
return d->inputSampleRate;
|
||||
}
|
||||
|
||||
int Opus::Properties::opusVersion() const
|
||||
{
|
||||
return d->opusVersion;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Opus::Properties::read()
|
||||
{
|
||||
// Get the identification header from the Ogg implementation.
|
||||
|
||||
// http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1
|
||||
|
||||
ByteVector data = d->file->packet(0);
|
||||
|
||||
// *Magic Signature*
|
||||
uint pos = 8;
|
||||
|
||||
// *Version* (8 bits, unsigned)
|
||||
d->opusVersion = uchar(data.at(pos));
|
||||
pos += 1;
|
||||
|
||||
// *Output Channel Count* 'C' (8 bits, unsigned)
|
||||
d->channels = uchar(data.at(pos));
|
||||
pos += 1;
|
||||
|
||||
// *Pre-skip* (16 bits, unsigned, little endian)
|
||||
const ushort preSkip = data.toUShort(pos, false);
|
||||
pos += 2;
|
||||
|
||||
// *Input Sample Rate* (32 bits, unsigned, little endian)
|
||||
d->inputSampleRate = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
// *Output Gain* (16 bits, signed, little endian)
|
||||
pos += 2;
|
||||
|
||||
// *Channel Mapping Family* (8 bits, unsigned)
|
||||
pos += 1;
|
||||
|
||||
const Ogg::PageHeader *first = d->file->firstPageHeader();
|
||||
const Ogg::PageHeader *last = d->file->lastPageHeader();
|
||||
|
||||
if(first && last) {
|
||||
long long start = first->absoluteGranularPosition();
|
||||
long long end = last->absoluteGranularPosition();
|
||||
|
||||
if(start >= 0 && end >= 0)
|
||||
d->length = (int) ((end - start - preSkip) / 48000);
|
||||
else {
|
||||
debug("Opus::Properties::read() -- The PCM values for the start or "
|
||||
"end of this file was incorrect.");
|
||||
}
|
||||
}
|
||||
else
|
||||
debug("Opus::Properties::read() -- Could not find valid first and last Ogg pages.");
|
||||
}
|
||||
96
taglib/ogg/opus/opusproperties.h
Normal file
96
taglib/ogg/opus/opusproperties.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2012 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
|
||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||
email : wheeler@kde.org
|
||||
(original Vorbis implementation)
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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_OPUSPROPERTIES_H
|
||||
#define TAGLIB_OPUSPROPERTIES_H
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace Ogg {
|
||||
|
||||
namespace Opus {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for Ogg Opus
|
||||
|
||||
/*!
|
||||
* This reads the data from an Ogg Opus stream found in the AudioProperties
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of Opus::Properties with the data read from the
|
||||
* Opus::File \a file.
|
||||
*/
|
||||
Properties(File *file, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this Opus::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
virtual int length() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* The Opus codec supports decoding at multiple sample rates, there is no
|
||||
* single sample rate of the encoded stream. This returns the sample rate
|
||||
* of the original audio stream.
|
||||
*/
|
||||
int inputSampleRate() const;
|
||||
|
||||
/*!
|
||||
* Returns the Opus version, currently "0" (as specified by the spec).
|
||||
*/
|
||||
int opusVersion() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read();
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
#include "speexfile.h"
|
||||
|
||||
@@ -62,14 +63,16 @@ Speex::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : Ogg::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
Speex::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
Speex::File::~File()
|
||||
@@ -82,6 +85,16 @@ Ogg::XiphComment *Speex::File::tag() const
|
||||
return d->comment;
|
||||
}
|
||||
|
||||
PropertyMap Speex::File::properties() const
|
||||
{
|
||||
return d->comment->properties();
|
||||
}
|
||||
|
||||
PropertyMap Speex::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->comment->setProperties(properties);
|
||||
}
|
||||
|
||||
Speex::Properties *Speex::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
|
||||
@@ -56,20 +56,22 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs a Speex 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.
|
||||
* Constructs a Speex 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);
|
||||
|
||||
/*!
|
||||
* Contructs a Speex 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.
|
||||
* Constructs a Speex file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -86,12 +88,26 @@ namespace TagLib {
|
||||
*/
|
||||
virtual Ogg::XiphComment *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This forwards directly to XiphComment::properties().
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function.
|
||||
* Like properties(), this is a forwarder to the file's XiphComment.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the Speex::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
|
||||
|
||||
|
||||
virtual bool save();
|
||||
|
||||
private:
|
||||
|
||||
@@ -113,32 +113,32 @@ void Speex::Properties::read()
|
||||
|
||||
ByteVector data = d->file->packet(0);
|
||||
|
||||
int pos = 28;
|
||||
uint pos = 28;
|
||||
|
||||
// speex_version_id; /**< Version for Speex (for checking compatibility) */
|
||||
d->speexVersion = data.mid(pos, 4).toUInt(false);
|
||||
d->speexVersion = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
// header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */
|
||||
pos += 4;
|
||||
|
||||
// rate; /**< Sampling rate used */
|
||||
d->sampleRate = data.mid(pos, 4).toUInt(false);
|
||||
d->sampleRate = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
// mode; /**< Mode used (0 for narrowband, 1 for wideband) */
|
||||
d->mode = data.mid(pos, 4).toUInt(false);
|
||||
d->mode = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
// mode_bitstream_version; /**< Version ID of the bit-stream */
|
||||
pos += 4;
|
||||
|
||||
// nb_channels; /**< Number of channels encoded */
|
||||
d->channels = data.mid(pos, 4).toUInt(false);
|
||||
d->channels = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
// bitrate; /**< Bit-rate used */
|
||||
d->bitrate = data.mid(pos, 4).toUInt(false);
|
||||
d->bitrate = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
// frame_size; /**< Size of frames */
|
||||
@@ -146,7 +146,7 @@ void Speex::Properties::read()
|
||||
pos += 4;
|
||||
|
||||
// vbr; /**< 1 for a VBR encoding, 0 otherwise */
|
||||
d->vbr = data.mid(pos, 4).toUInt(false) == 1;
|
||||
d->vbr = data.toUInt(pos, false) == 1;
|
||||
pos += 4;
|
||||
|
||||
// frames_per_packet; /**< Number of frames stored per Ogg packet */
|
||||
|
||||
@@ -67,14 +67,16 @@ Vorbis::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : Ogg::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
Vorbis::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
Vorbis::File::~File()
|
||||
|
||||
@@ -63,20 +63,22 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs a Vorbis 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.
|
||||
* Constructs a Vorbis 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);
|
||||
|
||||
/*!
|
||||
* Contructs a Vorbis 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.
|
||||
* Constructs a Vorbis file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
@@ -133,7 +133,7 @@ void Vorbis::Properties::read()
|
||||
|
||||
ByteVector data = d->file->packet(0);
|
||||
|
||||
int pos = 0;
|
||||
uint pos = 0;
|
||||
|
||||
if(data.mid(pos, 7) != vorbisSetupHeaderID) {
|
||||
debug("Vorbis::Properties::read() -- invalid Vorbis identification header");
|
||||
@@ -142,22 +142,22 @@ void Vorbis::Properties::read()
|
||||
|
||||
pos += 7;
|
||||
|
||||
d->vorbisVersion = data.mid(pos, 4).toUInt(false);
|
||||
d->vorbisVersion = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
d->channels = uchar(data[pos]);
|
||||
pos += 1;
|
||||
|
||||
d->sampleRate = data.mid(pos, 4).toUInt(false);
|
||||
d->sampleRate = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
d->bitrateMaximum = data.mid(pos, 4).toUInt(false);
|
||||
d->bitrateMaximum = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
d->bitrateNominal = data.mid(pos, 4).toUInt(false);
|
||||
d->bitrateNominal = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
d->bitrateMinimum = data.mid(pos, 4).toUInt(false);
|
||||
d->bitrateMinimum = data.toUInt(pos, false);
|
||||
|
||||
// TODO: Later this should be only the "fast" mode.
|
||||
d->bitrate = d->bitrateNominal;
|
||||
|
||||
@@ -340,7 +340,7 @@ void Ogg::XiphComment::parse(const ByteVector &data)
|
||||
|
||||
uint pos = 0;
|
||||
|
||||
uint vendorLength = data.mid(0, 4).toUInt(false);
|
||||
const uint vendorLength = data.toUInt(0, false);
|
||||
pos += 4;
|
||||
|
||||
d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
|
||||
@@ -348,7 +348,7 @@ void Ogg::XiphComment::parse(const ByteVector &data)
|
||||
|
||||
// Next the number of fields in the comment vector.
|
||||
|
||||
uint commentFields = data.mid(pos, 4).toUInt(false);
|
||||
const uint commentFields = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
if(commentFields > (data.size() - 8) / 4) {
|
||||
@@ -360,7 +360,7 @@ void Ogg::XiphComment::parse(const ByteVector &data)
|
||||
// Each comment field is in the format "KEY=value" in a UTF8 string and has
|
||||
// 4 bytes before the text starts that gives the length.
|
||||
|
||||
uint commentLength = data.mid(pos, 4).toUInt(false);
|
||||
const uint commentLength = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
String comment = String(data.mid(pos, commentLength), String::UTF8);
|
||||
|
||||
@@ -90,6 +90,11 @@ PropertyMap RIFF::AIFF::File::properties() const
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
void RIFF::AIFF::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
d->tag->removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap RIFF::AIFF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
@@ -118,6 +123,7 @@ bool RIFF::AIFF::File::save()
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -58,20 +58,22 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs an AIFF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an AIFF 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);
|
||||
|
||||
/*!
|
||||
* Contructs an AIFF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an AIFF 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);
|
||||
@@ -83,6 +85,12 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Returns the 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 *tag() const;
|
||||
|
||||
@@ -92,6 +100,8 @@ namespace TagLib {
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
@@ -109,6 +119,13 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
#define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0)
|
||||
|
||||
static double ConvertFromIeeeExtended(unsigned char *bytes)
|
||||
static double ConvertFromIeeeExtended(const TagLib::uchar *bytes)
|
||||
{
|
||||
double f;
|
||||
int expon;
|
||||
@@ -150,10 +150,10 @@ TagLib::uint RIFF::AIFF::Properties::sampleFrames() const
|
||||
|
||||
void RIFF::AIFF::Properties::read(const ByteVector &data)
|
||||
{
|
||||
d->channels = data.mid(0, 2).toShort();
|
||||
d->sampleFrames = data.mid(2, 4).toUInt();
|
||||
d->sampleWidth = data.mid(6, 2).toShort();
|
||||
double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<unsigned char *>(data.mid(8, 10).data()));
|
||||
d->channels = data.toShort(0U);
|
||||
d->sampleFrames = data.toUInt(2U);
|
||||
d->sampleWidth = data.toShort(6U);
|
||||
double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<const uchar *>(data.data() + 8));
|
||||
d->sampleRate = (int)sampleRate;
|
||||
d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0);
|
||||
d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <tstring.h>
|
||||
|
||||
#include "rifffile.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -138,34 +139,49 @@ ByteVector RIFF::File::chunkData(uint i)
|
||||
return readBlock(d->chunks[i].size);
|
||||
}
|
||||
|
||||
void RIFF::File::setChunkData(uint i, const ByteVector &data)
|
||||
{
|
||||
// First we update the global size
|
||||
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
||||
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(chunkName(i), data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8);
|
||||
|
||||
d->chunks[i].size = data.size();
|
||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Now update the internal offsets
|
||||
|
||||
for(i++; i < d->chunks.size(); i++)
|
||||
d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding;
|
||||
}
|
||||
|
||||
void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
|
||||
{
|
||||
setChunkData(name, data, false);
|
||||
}
|
||||
|
||||
void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate)
|
||||
{
|
||||
if(d->chunks.size() == 0) {
|
||||
debug("RIFF::File::setChunkData - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
for(uint i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == name) {
|
||||
if(alwaysCreate && name != "LIST") {
|
||||
debug("RIFF::File::setChunkData - alwaysCreate should be used for only \"LIST\" chunks.");
|
||||
return;
|
||||
}
|
||||
|
||||
// First we update the global size
|
||||
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
||||
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(name, data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8);
|
||||
|
||||
d->chunks[i].size = data.size();
|
||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Now update the internal offsets
|
||||
|
||||
for(i++; i < d->chunks.size(); i++)
|
||||
d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding;
|
||||
|
||||
return;
|
||||
if(!alwaysCreate) {
|
||||
for(uint i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == name) {
|
||||
setChunkData(i, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +197,7 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
|
||||
|
||||
// Now add the chunk to the file
|
||||
|
||||
writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0);
|
||||
writeChunk(name, data, offset, std::max<long>(0, length() - offset), (offset & 1) ? 1 : 0);
|
||||
|
||||
// And update our internal structure
|
||||
|
||||
@@ -199,6 +215,28 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void RIFF::File::removeChunk(uint i)
|
||||
{
|
||||
if(i >= d->chunks.size())
|
||||
return;
|
||||
|
||||
removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8);
|
||||
d->chunks.erase(d->chunks.begin() + i);
|
||||
}
|
||||
|
||||
void RIFF::File::removeChunk(const ByteVector &name)
|
||||
{
|
||||
std::vector<Chunk> newChunks;
|
||||
for(size_t i = 0; i < d->chunks.size(); ++i) {
|
||||
if(d->chunks[i].name == name)
|
||||
removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8);
|
||||
else
|
||||
newChunks.push_back(d->chunks[i]);
|
||||
}
|
||||
|
||||
d->chunks.swap(newChunks);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -95,6 +95,13 @@ namespace TagLib {
|
||||
*/
|
||||
ByteVector chunkData(uint i);
|
||||
|
||||
/*!
|
||||
* Sets the data for the the specified chunk to \a data.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setChunkData(uint i, const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Sets the data for the chunk \a name to \a data. If a chunk with the
|
||||
* given name already exists it will be overwritten, otherwise it will be
|
||||
@@ -104,6 +111,34 @@ namespace TagLib {
|
||||
*/
|
||||
void setChunkData(const ByteVector &name, const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Sets the data for the chunk \a name to \a data. If a chunk with the
|
||||
* given name already exists it will be overwritten, otherwise it will be
|
||||
* created after the existing chunks.
|
||||
*
|
||||
* \note If \a alwaysCreate is true, a new chunk is created regardless of
|
||||
* whether or not the chunk \a name exists. It should only be used for
|
||||
* "LIST" chunks.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate);
|
||||
|
||||
/*!
|
||||
* Removes the specified chunk.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void removeChunk(uint i);
|
||||
|
||||
/*!
|
||||
* Removes the chunk \a name.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
* \warning This removes all the chunks with the given name.
|
||||
*/
|
||||
void removeChunk(const ByteVector &name);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
266
taglib/riff/wav/infotag.cpp
Normal file
266
taglib/riff/wav/infotag.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2012 by Tsuda Kageyu
|
||||
email : wheeler@kde.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tfile.h>
|
||||
|
||||
#include "infotag.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace RIFF::Info;
|
||||
|
||||
namespace {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
class RIFF::Info::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate()
|
||||
{}
|
||||
|
||||
FieldListMap fieldListMap;
|
||||
|
||||
static const StringHandler *stringHandler;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// StringHandler implementation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
StringHandler::StringHandler()
|
||||
{
|
||||
}
|
||||
|
||||
StringHandler::~StringHandler()
|
||||
{
|
||||
}
|
||||
|
||||
String RIFF::Info::StringHandler::parse(const ByteVector &data) const
|
||||
{
|
||||
return String(data, String::UTF8);
|
||||
}
|
||||
|
||||
ByteVector RIFF::Info::StringHandler::render(const String &s) const
|
||||
{
|
||||
return s.data(String::UTF8);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const StringHandler defaultStringHandler;
|
||||
const RIFF::Info::StringHandler *RIFF::Info::Tag::TagPrivate::stringHandler = &defaultStringHandler;
|
||||
|
||||
RIFF::Info::Tag::Tag(const ByteVector &data)
|
||||
: TagLib::Tag()
|
||||
, d(new TagPrivate())
|
||||
{
|
||||
parse(data);
|
||||
}
|
||||
|
||||
RIFF::Info::Tag::Tag()
|
||||
: TagLib::Tag()
|
||||
, d(new TagPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
RIFF::Info::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::title() const
|
||||
{
|
||||
return fieldText("INAM");
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::artist() const
|
||||
{
|
||||
return fieldText("IART");
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::album() const
|
||||
{
|
||||
return fieldText("IPRD");
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::comment() const
|
||||
{
|
||||
return fieldText("ICMT");
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::genre() const
|
||||
{
|
||||
return fieldText("IGNR");
|
||||
}
|
||||
|
||||
TagLib::uint RIFF::Info::Tag::year() const
|
||||
{
|
||||
return fieldText("ICRD").substr(0, 4).toInt();
|
||||
}
|
||||
|
||||
TagLib::uint RIFF::Info::Tag::track() const
|
||||
{
|
||||
return fieldText("IPRT").toInt();
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setTitle(const String &s)
|
||||
{
|
||||
setFieldText("INAM", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setArtist(const String &s)
|
||||
{
|
||||
setFieldText("IART", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setAlbum(const String &s)
|
||||
{
|
||||
setFieldText("IPRD", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setComment(const String &s)
|
||||
{
|
||||
setFieldText("ICMT", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setGenre(const String &s)
|
||||
{
|
||||
setFieldText("IGNR", s);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setYear(uint i)
|
||||
{
|
||||
if(i != 0)
|
||||
setFieldText("ICRD", String::number(i));
|
||||
else
|
||||
d->fieldListMap.erase("ICRD");
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setTrack(uint i)
|
||||
{
|
||||
if(i != 0)
|
||||
setFieldText("IPRT", String::number(i));
|
||||
else
|
||||
d->fieldListMap.erase("IPRT");
|
||||
}
|
||||
|
||||
bool RIFF::Info::Tag::isEmpty() const
|
||||
{
|
||||
return d->fieldListMap.isEmpty();
|
||||
}
|
||||
|
||||
FieldListMap RIFF::Info::Tag::fieldListMap() const
|
||||
{
|
||||
return d->fieldListMap;
|
||||
}
|
||||
|
||||
String RIFF::Info::Tag::fieldText(const ByteVector &id) const
|
||||
{
|
||||
if(d->fieldListMap.contains(id))
|
||||
return String(d->fieldListMap[id]);
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s)
|
||||
{
|
||||
// id must be four-byte long pure ascii string.
|
||||
if(!isValidChunkID(id))
|
||||
return;
|
||||
|
||||
if(!s.isEmpty())
|
||||
d->fieldListMap[id] = s;
|
||||
else
|
||||
removeField(id);
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::removeField(const ByteVector &id)
|
||||
{
|
||||
if(d->fieldListMap.contains(id))
|
||||
d->fieldListMap.erase(id);
|
||||
}
|
||||
|
||||
ByteVector RIFF::Info::Tag::render() const
|
||||
{
|
||||
ByteVector data("INFO");
|
||||
|
||||
FieldListMap::ConstIterator it = d->fieldListMap.begin();
|
||||
for(; it != d->fieldListMap.end(); ++it) {
|
||||
ByteVector text = TagPrivate::stringHandler->render(it->second);
|
||||
if(text.isEmpty())
|
||||
continue;
|
||||
|
||||
data.append(it->first);
|
||||
data.append(ByteVector::fromUInt(text.size() + 1, false));
|
||||
data.append(text);
|
||||
|
||||
do {
|
||||
data.append('\0');
|
||||
} while(data.size() & 1);
|
||||
}
|
||||
|
||||
if(data.size() == 4)
|
||||
return ByteVector();
|
||||
else
|
||||
return data;
|
||||
}
|
||||
|
||||
void RIFF::Info::Tag::setStringHandler(const StringHandler *handler)
|
||||
{
|
||||
if(handler)
|
||||
TagPrivate::stringHandler = handler;
|
||||
else
|
||||
TagPrivate::stringHandler = &defaultStringHandler;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RIFF::Info::Tag::parse(const ByteVector &data)
|
||||
{
|
||||
uint p = 4;
|
||||
while(p < data.size()) {
|
||||
const uint size = data.toUInt(p + 4, false);
|
||||
d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size));
|
||||
|
||||
p += ((size + 1) & ~1) + 8;
|
||||
}
|
||||
}
|
||||
|
||||
192
taglib/riff/wav/infotag.h
Normal file
192
taglib/riff/wav/infotag.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2012 by Tsuda Kageyu
|
||||
email : tsuda.kageyu@gmail.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_INFOTAG_H
|
||||
#define TAGLIB_INFOTAG_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tmap.h"
|
||||
#include "tstring.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
class File;
|
||||
|
||||
//! A RIFF Info tag implementation.
|
||||
namespace RIFF {
|
||||
namespace Info {
|
||||
|
||||
typedef Map<ByteVector, String> FieldListMap;
|
||||
|
||||
//! A abstraction for the string to data encoding in Info tags.
|
||||
|
||||
/*!
|
||||
* RIFF Info tag has no clear definitions about character encodings.
|
||||
* In practice, local encoding of each system is largely used and UTF-8 is
|
||||
* popular too.
|
||||
*
|
||||
* Here is an option to read and write tags in your preferrd encoding
|
||||
* by subclassing this class, reimplementing parse() and render() and setting
|
||||
* your reimplementation as the default with Info::Tag::setStringHandler().
|
||||
*
|
||||
* \see ID3v1::Tag::setStringHandler()
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT StringHandler
|
||||
{
|
||||
public:
|
||||
StringHandler();
|
||||
~StringHandler();
|
||||
|
||||
/*!
|
||||
* Decode a string from \a data. The default implementation assumes that
|
||||
* \a data is an UTF-8 character array.
|
||||
*/
|
||||
virtual String parse(const ByteVector &data) const;
|
||||
|
||||
/*!
|
||||
* Encode a ByteVector with the data from \a s. The default implementation
|
||||
* assumes that \a s is an UTF-8 string.
|
||||
*/
|
||||
virtual ByteVector render(const String &s) const;
|
||||
};
|
||||
|
||||
//! The main class in the ID3v2 implementation
|
||||
|
||||
/*!
|
||||
* This is the main class in the INFO tag implementation. RIFF INFO tag is a
|
||||
* metadata format found in WAV audio and AVI video files. Though it is a part
|
||||
* of Microsoft/IBM's RIFF specification, the author could not find the official
|
||||
* documents about it. So, this implementation is referring to unofficial documents
|
||||
* online and some applications' behaviors especially Windows Explorer.
|
||||
*/
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Constructs an empty Info tag.
|
||||
*/
|
||||
Tag();
|
||||
|
||||
/*!
|
||||
* Constructs an Info tag read from \a data which is contents of "LIST" chunk.
|
||||
*/
|
||||
Tag(const ByteVector &data);
|
||||
|
||||
virtual ~Tag();
|
||||
|
||||
// Reimplementations
|
||||
|
||||
virtual String title() const;
|
||||
virtual String artist() const;
|
||||
virtual String album() const;
|
||||
virtual String comment() const;
|
||||
virtual String genre() const;
|
||||
virtual uint year() const;
|
||||
virtual uint track() const;
|
||||
|
||||
virtual void setTitle(const String &s);
|
||||
virtual void setArtist(const String &s);
|
||||
virtual void setAlbum(const String &s);
|
||||
virtual void setComment(const String &s);
|
||||
virtual void setGenre(const String &s);
|
||||
virtual void setYear(uint i);
|
||||
virtual void setTrack(uint i);
|
||||
|
||||
virtual bool isEmpty() const;
|
||||
|
||||
/*!
|
||||
* Returns a copy of the internal fields of the tag. The returned map directly
|
||||
* reflects the contents of the "INFO" chunk.
|
||||
*
|
||||
* \note Modifying this map does not affect the tag's internal data.
|
||||
* Use setFieldText() and removeField() instead.
|
||||
*
|
||||
* \see setFieldText()
|
||||
* \see removeField()
|
||||
*/
|
||||
FieldListMap fieldListMap() const;
|
||||
|
||||
/*
|
||||
* Gets the value of the field with the ID \a id.
|
||||
*/
|
||||
String fieldText(const ByteVector &id) const;
|
||||
|
||||
/*
|
||||
* Sets the value of the field with the ID \a id to \a s.
|
||||
* If the field does not exist, it is created.
|
||||
* If \s is empty, the field is removed.
|
||||
*
|
||||
* \note fieldId must be four-byte long pure ASCII string. This function
|
||||
* performs nothing if fieldId is invalid.
|
||||
*/
|
||||
void setFieldText(const ByteVector &id, const String &s);
|
||||
|
||||
/*
|
||||
* Removes the field with the ID \a id.
|
||||
*/
|
||||
void removeField(const ByteVector &id);
|
||||
|
||||
/*!
|
||||
* Render the tag back to binary data, suitable to be written to disk.
|
||||
*
|
||||
* \note Returns empty ByteVector is the tag contains no fields.
|
||||
*/
|
||||
ByteVector render() const;
|
||||
|
||||
/*!
|
||||
* Sets the string handler that decides how the text data will be
|
||||
* converted to and from binary data.
|
||||
* If the parameter \a handler is null, the previous handler is
|
||||
* released and default UTF-8 handler is restored.
|
||||
*
|
||||
* \note The caller is responsible for deleting the previous handler
|
||||
* as needed after it is released.
|
||||
*
|
||||
* \see StringHandler
|
||||
*/
|
||||
static void setStringHandler(const StringHandler *handler);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Pareses the body of the tag in \a data.
|
||||
*/
|
||||
void parse(const ByteVector &data);
|
||||
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
};
|
||||
}}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -23,36 +23,47 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
#include <tdebug.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "tbytevector.h"
|
||||
#include "tdebug.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
#include "wavfile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "infotag.h"
|
||||
#include "tagunion.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum { ID3v2Index = 0, InfoIndex = 1 };
|
||||
}
|
||||
|
||||
class RIFF::WAV::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
properties(0),
|
||||
tag(0),
|
||||
tagChunkID("ID3 ")
|
||||
tagChunkID("ID3 "),
|
||||
hasID3v2(false),
|
||||
hasInfo(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete properties;
|
||||
delete tag;
|
||||
}
|
||||
|
||||
Properties *properties;
|
||||
ID3v2::Tag *tag;
|
||||
|
||||
ByteVector tagChunkID;
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
bool hasID3v2;
|
||||
bool hasInfo;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -82,26 +93,45 @@ RIFF::WAV::File::~File()
|
||||
|
||||
ID3v2::Tag *RIFF::WAV::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
return ID3v2Tag();
|
||||
}
|
||||
|
||||
ID3v2::Tag *RIFF::WAV::File::ID3v2Tag() const
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
}
|
||||
|
||||
RIFF::Info::Tag *RIFF::WAV::File::InfoTag() const
|
||||
{
|
||||
return d->tag.access<RIFF::Info::Tag>(InfoIndex, false);
|
||||
}
|
||||
|
||||
PropertyMap RIFF::WAV::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
return tag()->properties();
|
||||
}
|
||||
|
||||
void RIFF::WAV::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
tag()->removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
return tag()->setProperties(properties);
|
||||
}
|
||||
|
||||
|
||||
RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
}
|
||||
|
||||
bool RIFF::WAV::File::save()
|
||||
{
|
||||
return RIFF::WAV::File::save(AllTags);
|
||||
}
|
||||
|
||||
bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("RIFF::WAV::File::save() -- File is read only.");
|
||||
@@ -113,11 +143,43 @@ bool RIFF::WAV::File::save()
|
||||
return false;
|
||||
}
|
||||
|
||||
setChunkData(d->tagChunkID, d->tag->render());
|
||||
if(stripOthers)
|
||||
strip(static_cast<TagTypes>(AllTags & ~tags));
|
||||
|
||||
ID3v2::Tag *id3v2tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
if(!id3v2tag->isEmpty()) {
|
||||
if(tags & ID3v2) {
|
||||
setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version));
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
Info::Tag *infotag = d->tag.access<Info::Tag>(InfoIndex, false);
|
||||
if(!infotag->isEmpty()) {
|
||||
if(tags & Info) {
|
||||
int chunkId = findInfoTagChunk();
|
||||
if(chunkId != -1)
|
||||
setChunkData(chunkId, infotag->render());
|
||||
else
|
||||
setChunkData("LIST", infotag->render(), true);
|
||||
|
||||
d->hasInfo = true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RIFF::WAV::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->hasID3v2;
|
||||
}
|
||||
|
||||
bool RIFF::WAV::File::hasInfoTag() const
|
||||
{
|
||||
return d->hasInfo;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -127,19 +189,56 @@ void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle properties
|
||||
ByteVector formatData;
|
||||
uint streamLength = 0;
|
||||
for(uint i = 0; i < chunkCount(); i++) {
|
||||
if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") {
|
||||
String name = chunkName(i);
|
||||
if(name == "ID3 " || name == "id3 ") {
|
||||
d->tagChunkID = chunkName(i);
|
||||
d->tag = new ID3v2::Tag(this, chunkOffset(i));
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i)));
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else if(chunkName(i) == "fmt " && readProperties)
|
||||
else if(name == "fmt " && readProperties)
|
||||
formatData = chunkData(i);
|
||||
else if(chunkName(i) == "data" && readProperties)
|
||||
else if(name == "data" && readProperties)
|
||||
streamLength = chunkDataSize(i);
|
||||
else if(name == "LIST") {
|
||||
ByteVector data = chunkData(i);
|
||||
ByteVector type = data.mid(0, 4);
|
||||
|
||||
if(type == "INFO") {
|
||||
d->tag.set(InfoIndex, new RIFF::Info::Tag(data));
|
||||
d->hasInfo = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!d->tag[ID3v2Index])
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag);
|
||||
|
||||
if (!d->tag[InfoIndex])
|
||||
d->tag.set(InfoIndex, new RIFF::Info::Tag);
|
||||
|
||||
if(!formatData.isEmpty())
|
||||
d->properties = new Properties(formatData, streamLength, propertiesStyle);
|
||||
|
||||
if(!d->tag)
|
||||
d->tag = new ID3v2::Tag;
|
||||
}
|
||||
|
||||
void RIFF::WAV::File::strip(TagTypes tags)
|
||||
{
|
||||
if(tags & ID3v2)
|
||||
removeChunk(d->tagChunkID);
|
||||
|
||||
if(tags & Info){
|
||||
TagLib::uint chunkId = findInfoTagChunk();
|
||||
if(chunkId != TagLib::uint(-1))
|
||||
removeChunk(chunkId);
|
||||
}
|
||||
}
|
||||
|
||||
TagLib::uint RIFF::WAV::File::findInfoTagChunk()
|
||||
{
|
||||
for(uint i = 0; i < chunkCount(); ++i) {
|
||||
if(chunkName(i) == "LIST" && chunkData(i).mid(0, 4) == "INFO") {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return TagLib::uint(-1);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "rifffile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "infotag.h"
|
||||
#include "wavproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
@@ -57,21 +58,34 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT File : public TagLib::RIFF::File
|
||||
{
|
||||
public:
|
||||
enum TagTypes {
|
||||
//! Empty set. Matches no tag types.
|
||||
NoTags = 0x0000,
|
||||
//! Matches ID3v2 tags.
|
||||
ID3v2 = 0x0001,
|
||||
//! Matches Info tags.
|
||||
Info = 0x0002,
|
||||
//! Matches all tag types.
|
||||
AllTags = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
* Contructs an WAV file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs a WAV 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);
|
||||
|
||||
/*!
|
||||
* Contructs an WAV file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs a WAV 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);
|
||||
@@ -82,9 +96,34 @@ namespace TagLib {
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file.
|
||||
* Returns the ID3v2 Tag for this file.
|
||||
*
|
||||
* \note This method does not return all the tags for this file for
|
||||
* backward compatibility. Will be fixed in TagLib 2.0.
|
||||
*/
|
||||
virtual ID3v2::Tag *tag() const;
|
||||
ID3v2::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()
|
||||
*/
|
||||
ID3v2::Tag *ID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the RIFF INFO Tag for this file.
|
||||
*
|
||||
* \note This always returns a valid pointer regardless of whether or not
|
||||
* the file on disk has a RIFF INFO tag. Use hasInfoTag() to check if the
|
||||
* file on disk actually has a RIFF INFO tag.
|
||||
*
|
||||
* \see hasInfoTag()
|
||||
*/
|
||||
Info::Tag *InfoTag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
@@ -92,6 +131,8 @@ namespace TagLib {
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
@@ -109,12 +150,35 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
bool save(TagTypes tags, bool stripOthers = true, int id3v2Version = 4);
|
||||
|
||||
/*!
|
||||
* 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 a RIFF INFO tag.
|
||||
*
|
||||
* \see InfoTag()
|
||||
*/
|
||||
bool hasInfoTag() const;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
|
||||
void strip(TagTypes tags);
|
||||
|
||||
/*!
|
||||
* Returns the index of the chunk that its name is "LIST" and list type is "INFO".
|
||||
*/
|
||||
uint findInfoTagChunk();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
||||
@@ -115,12 +115,12 @@ TagLib::uint RIFF::WAV::Properties::sampleFrames() const
|
||||
|
||||
void RIFF::WAV::Properties::read(const ByteVector &data)
|
||||
{
|
||||
d->format = data.mid(0, 2).toShort(false);
|
||||
d->channels = data.mid(2, 2).toShort(false);
|
||||
d->sampleRate = data.mid(4, 4).toUInt(false);
|
||||
d->sampleWidth = data.mid(14, 2).toShort(false);
|
||||
d->format = data.toShort(0, false);
|
||||
d->channels = data.toShort(2, false);
|
||||
d->sampleRate = data.toUInt(4, false);
|
||||
d->sampleWidth = data.toShort(14, false);
|
||||
|
||||
uint byteRate = data.mid(8, 4).toUInt(false);
|
||||
const uint byteRate = data.toUInt(8, false);
|
||||
d->bitrate = byteRate * 8 / 1000;
|
||||
|
||||
d->length = byteRate > 0 ? d->streamLength / byteRate : 0;
|
||||
|
||||
@@ -47,7 +47,8 @@ S3M::File::File(FileName file, bool readProperties,
|
||||
Mod::FileBase(file),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
{
|
||||
read(readProperties);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
S3M::File::File(IOStream *stream, bool readProperties,
|
||||
@@ -55,7 +56,8 @@ S3M::File::File(IOStream *stream, bool readProperties,
|
||||
Mod::FileBase(stream),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
{
|
||||
read(readProperties);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
S3M::File::~File()
|
||||
|
||||
@@ -36,18 +36,22 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT File : public Mod::FileBase {
|
||||
public:
|
||||
/*!
|
||||
* Contructs a ScreamTracker III 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.
|
||||
* Constructs a ScreamTracker III from \a file.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs a ScreamTracker III file from \a stream. If \a readProperties
|
||||
* is true the file's audio properties will also be read using
|
||||
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
|
||||
* Constructs a ScreamTracker III file from \a stream.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
|
||||
@@ -40,8 +40,4 @@
|
||||
#define TAGLIB_EXPORT
|
||||
#endif
|
||||
|
||||
#ifndef TAGLIB_NO_CONFIG
|
||||
#include "taglib_config.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
78
taglib/toolkit/taglib.h
Normal file → Executable file
78
taglib/toolkit/taglib.h
Normal file → Executable file
@@ -26,8 +26,10 @@
|
||||
#ifndef TAGLIB_H
|
||||
#define TAGLIB_H
|
||||
|
||||
#include "taglib_config.h"
|
||||
|
||||
#define TAGLIB_MAJOR_VERSION 1
|
||||
#define TAGLIB_MINOR_VERSION 7
|
||||
#define TAGLIB_MINOR_VERSION 9
|
||||
#define TAGLIB_PATCH_VERSION 0
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1))
|
||||
@@ -44,23 +46,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <libkern/OSAtomic.h>
|
||||
# define TAGLIB_ATOMIC_MAC
|
||||
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
|
||||
# define NOMINMAX
|
||||
# include <windows.h>
|
||||
# define TAGLIB_ATOMIC_WIN
|
||||
#elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 401) \
|
||||
&& (defined(__i386__) || defined(__i486__) || defined(__i586__) || \
|
||||
defined(__i686__) || defined(__x86_64) || defined(__ia64)) \
|
||||
&& !defined(__INTEL_COMPILER)
|
||||
# define TAGLIB_ATOMIC_GCC
|
||||
#elif defined(__ia64) && defined(__INTEL_COMPILER)
|
||||
# include <ia64intrin.h>
|
||||
# define TAGLIB_ATOMIC_GCC
|
||||
#endif
|
||||
|
||||
//! A namespace for all TagLib related classes and functions
|
||||
|
||||
/*!
|
||||
@@ -75,10 +60,13 @@ namespace TagLib {
|
||||
|
||||
class String;
|
||||
|
||||
typedef wchar_t wchar;
|
||||
typedef unsigned char uchar;
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned int uint;
|
||||
typedef wchar_t wchar; // Assumed to be sufficient to store a UTF-16 char.
|
||||
typedef unsigned char uchar;
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long long ulonglong;
|
||||
|
||||
// long/ulong can be either 32-bit or 64-bit wide.
|
||||
typedef unsigned long ulong;
|
||||
|
||||
/*!
|
||||
@@ -86,50 +74,6 @@ namespace TagLib {
|
||||
* so I'm providing something here that should be constant.
|
||||
*/
|
||||
typedef std::basic_string<wchar> wstring;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class.
|
||||
/*!
|
||||
* \internal
|
||||
* This is just used as a base class for shared classes in TagLib.
|
||||
*
|
||||
* \warning This <b>is not</b> part of the TagLib public API!
|
||||
*/
|
||||
|
||||
class RefCounter
|
||||
{
|
||||
public:
|
||||
RefCounter() : refCount(1) {}
|
||||
|
||||
#ifdef TAGLIB_ATOMIC_MAC
|
||||
void ref() { OSAtomicIncrement32Barrier(const_cast<int32_t*>(&refCount)); }
|
||||
bool deref() { return ! OSAtomicDecrement32Barrier(const_cast<int32_t*>(&refCount)); }
|
||||
int32_t count() { return refCount; }
|
||||
private:
|
||||
volatile int32_t refCount;
|
||||
#elif defined(TAGLIB_ATOMIC_WIN)
|
||||
void ref() { InterlockedIncrement(&refCount); }
|
||||
bool deref() { return ! InterlockedDecrement(&refCount); }
|
||||
long count() { return refCount; }
|
||||
private:
|
||||
volatile long refCount;
|
||||
#elif defined(TAGLIB_ATOMIC_GCC)
|
||||
void ref() { __sync_add_and_fetch(&refCount, 1); }
|
||||
bool deref() { return ! __sync_sub_and_fetch(&refCount, 1); }
|
||||
int count() { return refCount; }
|
||||
private:
|
||||
volatile int refCount;
|
||||
#else
|
||||
void ref() { refCount++; }
|
||||
bool deref() { return ! --refCount; }
|
||||
int count() { return refCount; }
|
||||
private:
|
||||
uint refCount;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // DO_NOT_DOCUMENT
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -143,7 +87,7 @@ namespace TagLib {
|
||||
* - A clean, high level, C++ API to handling audio meta data.
|
||||
* - Format specific APIs for advanced API users.
|
||||
* - ID3v1, ID3v2, APE, FLAC, Xiph, iTunes-style MP4 and WMA tag formats.
|
||||
* - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis and Speex file formats.
|
||||
* - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis, Speex and Opus file formats.
|
||||
* - Basic audio file properties such as length, sample rate, etc.
|
||||
* - Long term binary and source compatibility.
|
||||
* - Extensible design, notably the ability to add other formats or extend current formats as a library user.
|
||||
|
||||
@@ -23,230 +23,322 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include <string.h>
|
||||
#include "trefcounter.h"
|
||||
#include "tutils.h"
|
||||
|
||||
#include "tbytevector.h"
|
||||
|
||||
// This is a bit ugly to keep writing over and over again.
|
||||
|
||||
// A rather obscure feature of the C++ spec that I hadn't thought of that makes
|
||||
// working with C libs much more effecient. There's more here:
|
||||
// working with C libs much more efficient. There's more here:
|
||||
//
|
||||
// http://www.informit.com/isapi/product_id~{9C84DAB4-FE6E-49C5-BB0A-FB50331233EA}/content/index.asp
|
||||
|
||||
#define DATA(x) (&(x->data[0]))
|
||||
#define DATA(x) (&(x->data->data[0]))
|
||||
|
||||
namespace TagLib {
|
||||
static const char hexTable[17] = "0123456789abcdef";
|
||||
|
||||
static const uint crcTable[256] = {
|
||||
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
||||
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
|
||||
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
||||
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
|
||||
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
|
||||
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
||||
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
|
||||
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
||||
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
||||
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
|
||||
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
||||
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
|
||||
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
|
||||
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
||||
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
|
||||
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
||||
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
||||
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
|
||||
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
||||
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
|
||||
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
|
||||
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
||||
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
|
||||
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
||||
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
||||
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
|
||||
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
||||
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
|
||||
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
|
||||
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
||||
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
|
||||
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
||||
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
||||
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
||||
};
|
||||
static const char hexTable[17] = "0123456789abcdef";
|
||||
|
||||
/*!
|
||||
* A templatized KMP find that works both with a ByteVector and a ByteVectorMirror.
|
||||
*/
|
||||
|
||||
template <class Vector>
|
||||
int vectorFind(const Vector &v, const Vector &pattern, uint offset, int byteAlign)
|
||||
{
|
||||
if(pattern.size() > v.size() || offset > v.size() - 1)
|
||||
return -1;
|
||||
|
||||
// Let's go ahead and special case a pattern of size one since that's common
|
||||
// and easy to make fast.
|
||||
|
||||
if(pattern.size() == 1) {
|
||||
char p = pattern[0];
|
||||
for(uint i = offset; i < v.size(); i++) {
|
||||
if(v[i] == p && (i - offset) % byteAlign == 0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uchar lastOccurrence[256];
|
||||
|
||||
for(uint i = 0; i < 256; ++i)
|
||||
lastOccurrence[i] = uchar(pattern.size());
|
||||
|
||||
for(uint i = 0; i < pattern.size() - 1; ++i)
|
||||
lastOccurrence[uchar(pattern[i])] = uchar(pattern.size() - i - 1);
|
||||
|
||||
for(uint i = pattern.size() - 1 + offset; i < v.size(); i += lastOccurrence[uchar(v.at(i))]) {
|
||||
int iBuffer = i;
|
||||
int iPattern = pattern.size() - 1;
|
||||
|
||||
while(iPattern >= 0 && v.at(iBuffer) == pattern[iPattern]) {
|
||||
--iBuffer;
|
||||
--iPattern;
|
||||
}
|
||||
|
||||
if(-1 == iPattern && (iBuffer + 1 - offset) % byteAlign == 0)
|
||||
return iBuffer + 1;
|
||||
}
|
||||
static const uint crcTable[256] = {
|
||||
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
||||
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
|
||||
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
||||
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
|
||||
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
|
||||
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
||||
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
|
||||
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
||||
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
||||
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
|
||||
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
||||
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
|
||||
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
|
||||
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
||||
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
|
||||
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
||||
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
||||
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
|
||||
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
||||
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
|
||||
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
|
||||
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
||||
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
|
||||
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
||||
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
||||
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
|
||||
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
||||
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
|
||||
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
|
||||
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
||||
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
|
||||
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
||||
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
||||
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
||||
};
|
||||
|
||||
/*!
|
||||
* A templatized straightforward find that works with the types
|
||||
* std::vector<char>::iterator and std::vector<char>::reverse_iterator.
|
||||
*/
|
||||
template <class TIterator>
|
||||
int findChar(
|
||||
const TIterator dataBegin, const TIterator dataEnd,
|
||||
char c, uint offset, int byteAlign)
|
||||
{
|
||||
const size_t dataSize = dataEnd - dataBegin;
|
||||
if(dataSize == 0 || offset > dataSize - 1)
|
||||
return -1;
|
||||
|
||||
// n % 0 is invalid
|
||||
|
||||
if(byteAlign == 0)
|
||||
return -1;
|
||||
|
||||
for(TIterator it = dataBegin + offset; it < dataEnd; it += byteAlign) {
|
||||
if(*it == c)
|
||||
return (it - dataBegin);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Wraps the accessors to a ByteVector to make the search algorithm access the
|
||||
* elements in reverse.
|
||||
*
|
||||
* \see vectorFind()
|
||||
* \see ByteVector::rfind()
|
||||
*/
|
||||
|
||||
class ByteVectorMirror
|
||||
{
|
||||
public:
|
||||
ByteVectorMirror(const ByteVector &source) : v(source) {}
|
||||
|
||||
char operator[](int index) const
|
||||
{
|
||||
return v[v.size() - index - 1];
|
||||
}
|
||||
|
||||
char at(int index) const
|
||||
{
|
||||
return v.at(v.size() - index - 1);
|
||||
}
|
||||
|
||||
ByteVectorMirror mid(uint index, uint length = 0xffffffff) const
|
||||
{
|
||||
return length == 0xffffffff ? v.mid(0, index) : v.mid(index - length, length);
|
||||
}
|
||||
|
||||
uint size() const
|
||||
{
|
||||
return v.size();
|
||||
}
|
||||
|
||||
int find(const ByteVectorMirror &pattern, uint offset = 0, int byteAlign = 1) const
|
||||
{
|
||||
ByteVectorMirror v(*this);
|
||||
|
||||
if(offset > 0) {
|
||||
offset = size() - offset - pattern.size();
|
||||
if(offset >= size())
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
const int pos = vectorFind<ByteVectorMirror>(v, pattern, offset, byteAlign);
|
||||
|
||||
// If the offset is zero then we need to adjust the location in the search
|
||||
// to be appropriately reversed. If not we need to account for the fact
|
||||
// that the recursive call (called from the above line) has already ajusted
|
||||
// for this but that the normal templatized find above will add the offset
|
||||
// to the returned value.
|
||||
//
|
||||
// This is a little confusing at first if you don't first stop to think
|
||||
// through the logic involved in the forward search.
|
||||
|
||||
if(pos == -1)
|
||||
return -1;
|
||||
|
||||
return size() - pos - pattern.size();
|
||||
}
|
||||
|
||||
private:
|
||||
const ByteVector &v;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
T toNumber(const std::vector<char> &data, bool mostSignificantByteFirst)
|
||||
{
|
||||
T sum = 0;
|
||||
|
||||
if(data.size() <= 0) {
|
||||
debug("ByteVectorMirror::toNumber<T>() -- data is empty, returning 0");
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint size = sizeof(T);
|
||||
uint last = data.size() > size ? size - 1 : data.size() - 1;
|
||||
|
||||
for(uint i = 0; i <= last; i++)
|
||||
sum |= (T) uchar(data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
ByteVector fromNumber(T value, bool mostSignificantByteFirst)
|
||||
{
|
||||
int size = sizeof(T);
|
||||
|
||||
ByteVector v(size, 0);
|
||||
|
||||
for(int i = 0; i < size; i++)
|
||||
v[i] = uchar(value >> ((mostSignificantByteFirst ? size - 1 - i : i) * 8) & 0xff);
|
||||
|
||||
return v;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
using namespace TagLib;
|
||||
/*!
|
||||
* A templatized KMP find that works with the types
|
||||
* std::vector<char>::iterator and std::vector<char>::reverse_iterator.
|
||||
*/
|
||||
template <class TIterator>
|
||||
int findVector(
|
||||
const TIterator dataBegin, const TIterator dataEnd,
|
||||
const TIterator patternBegin, const TIterator patternEnd,
|
||||
uint offset, int byteAlign)
|
||||
{
|
||||
const size_t dataSize = dataEnd - dataBegin;
|
||||
const size_t patternSize = patternEnd - patternBegin;
|
||||
if(patternSize > dataSize || offset > dataSize - 1)
|
||||
return -1;
|
||||
|
||||
// n % 0 is invalid
|
||||
|
||||
if(byteAlign == 0)
|
||||
return -1;
|
||||
|
||||
// Special case that pattern contains just single char.
|
||||
|
||||
if(patternSize == 1)
|
||||
return findChar(dataBegin, dataEnd, *patternBegin, offset, byteAlign);
|
||||
|
||||
size_t lastOccurrence[256];
|
||||
|
||||
for(size_t i = 0; i < 256; ++i)
|
||||
lastOccurrence[i] = patternSize;
|
||||
|
||||
for(size_t i = 0; i < patternSize - 1; ++i)
|
||||
lastOccurrence[static_cast<uchar>(*(patternBegin + i))] = patternSize - i - 1;
|
||||
|
||||
TIterator it = dataBegin + patternSize - 1 + offset;
|
||||
while(true)
|
||||
{
|
||||
TIterator itBuffer = it;
|
||||
TIterator itPattern = patternBegin + patternSize - 1;
|
||||
|
||||
while(*itBuffer == *itPattern)
|
||||
{
|
||||
if(itPattern == patternBegin)
|
||||
{
|
||||
if((itBuffer - dataBegin - offset) % byteAlign == 0)
|
||||
return (itBuffer - dataBegin);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
--itBuffer;
|
||||
--itPattern;
|
||||
}
|
||||
|
||||
const size_t step = lastOccurrence[static_cast<uchar>(*it)];
|
||||
if(dataEnd - step <= it)
|
||||
break;
|
||||
|
||||
it += step;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T toNumber(const ByteVector &v, size_t offset, size_t length, bool mostSignificantByteFirst)
|
||||
{
|
||||
if(offset >= v.size()) {
|
||||
debug("toNumber<T>() -- No data to convert. Returning 0.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
length = std::min(length, v.size() - offset);
|
||||
|
||||
T sum = 0;
|
||||
for(size_t i = 0; i < length; i++) {
|
||||
const size_t shift = (mostSignificantByteFirst ? length - 1 - i : i) * 8;
|
||||
sum |= static_cast<T>(static_cast<uchar>(v[offset + i])) << shift;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T toNumber(const ByteVector &v, size_t offset, bool mostSignificantByteFirst)
|
||||
{
|
||||
static const bool isBigEndian = (Utils::SystemByteOrder == Utils::BigEndian);
|
||||
const bool swap = (mostSignificantByteFirst != isBigEndian);
|
||||
|
||||
if(offset + sizeof(T) > v.size())
|
||||
return toNumber<T>(v, offset, v.size() - offset, mostSignificantByteFirst);
|
||||
|
||||
// Uses memcpy instead of reinterpret_cast to avoid an alignment exception.
|
||||
T tmp;
|
||||
::memcpy(&tmp, v.data() + offset, sizeof(T));
|
||||
|
||||
if(swap)
|
||||
return Utils::byteSwap(tmp);
|
||||
else
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
ByteVector fromNumber(T value, bool mostSignificantByteFirst)
|
||||
{
|
||||
static const bool isBigEndian = (Utils::SystemByteOrder == Utils::BigEndian);
|
||||
const bool swap = (mostSignificantByteFirst != isBigEndian);
|
||||
|
||||
if(swap)
|
||||
value = Utils::byteSwap(value);
|
||||
|
||||
return ByteVector(reinterpret_cast<const char *>(&value), sizeof(T));
|
||||
}
|
||||
|
||||
class DataPrivate : public RefCounter
|
||||
{
|
||||
public:
|
||||
DataPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
DataPrivate(const std::vector<char> &v, uint offset, uint length)
|
||||
: data(v.begin() + offset, v.begin() + offset + length)
|
||||
{
|
||||
}
|
||||
|
||||
// A char* can be an iterator.
|
||||
DataPrivate(const char *begin, const char *end)
|
||||
: data(begin, end)
|
||||
{
|
||||
}
|
||||
|
||||
DataPrivate(uint len, char c)
|
||||
: data(len, c)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
class ByteVector::ByteVectorPrivate : public RefCounter
|
||||
{
|
||||
public:
|
||||
ByteVectorPrivate() : RefCounter(), size(0) {}
|
||||
ByteVectorPrivate(const std::vector<char> &v) : RefCounter(), data(v), size(v.size()) {}
|
||||
ByteVectorPrivate(TagLib::uint len, char value) : RefCounter(), data(len, value), size(len) {}
|
||||
ByteVectorPrivate()
|
||||
: RefCounter()
|
||||
, data(new DataPrivate())
|
||||
, offset(0)
|
||||
, length(0)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<char> data;
|
||||
ByteVectorPrivate(ByteVectorPrivate *d, uint o, uint l)
|
||||
: RefCounter()
|
||||
, data(d->data)
|
||||
, offset(d->offset + o)
|
||||
, length(l)
|
||||
{
|
||||
data->ref();
|
||||
}
|
||||
|
||||
// std::vector<T>::size() is very slow, so we'll cache the value
|
||||
ByteVectorPrivate(const std::vector<char> &v, uint o, uint l)
|
||||
: RefCounter()
|
||||
, data(new DataPrivate(v, o, l))
|
||||
, offset(0)
|
||||
, length(l)
|
||||
{
|
||||
}
|
||||
|
||||
uint size;
|
||||
ByteVectorPrivate(uint l, char c)
|
||||
: RefCounter()
|
||||
, data(new DataPrivate(l, c))
|
||||
, offset(0)
|
||||
, length(l)
|
||||
{
|
||||
}
|
||||
|
||||
ByteVectorPrivate(const char *s, uint l)
|
||||
: RefCounter()
|
||||
, data(new DataPrivate(s, s + l))
|
||||
, offset(0)
|
||||
, length(l)
|
||||
{
|
||||
}
|
||||
|
||||
void detach()
|
||||
{
|
||||
if(data->count() > 1) {
|
||||
data->deref();
|
||||
data = new DataPrivate(data->data, offset, length);
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
~ByteVectorPrivate()
|
||||
{
|
||||
if(data->deref())
|
||||
delete data;
|
||||
}
|
||||
|
||||
ByteVectorPrivate &operator=(const ByteVectorPrivate &x)
|
||||
{
|
||||
if(&x != this)
|
||||
{
|
||||
if(data->deref())
|
||||
delete data;
|
||||
|
||||
data = x.data;
|
||||
data->ref();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataPrivate *data;
|
||||
uint offset;
|
||||
uint length;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -257,14 +349,10 @@ ByteVector ByteVector::null;
|
||||
|
||||
ByteVector ByteVector::fromCString(const char *s, uint length)
|
||||
{
|
||||
ByteVector v;
|
||||
|
||||
if(length == 0xffffffff)
|
||||
v.setData(s);
|
||||
return ByteVector(s, ::strlen(s));
|
||||
else
|
||||
v.setData(s, length);
|
||||
|
||||
return v;
|
||||
return ByteVector(s, length);
|
||||
}
|
||||
|
||||
ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst)
|
||||
@@ -274,12 +362,12 @@ ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst)
|
||||
|
||||
ByteVector ByteVector::fromShort(short value, bool mostSignificantByteFirst)
|
||||
{
|
||||
return fromNumber<short>(value, mostSignificantByteFirst);
|
||||
return fromNumber<ushort>(value, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFirst)
|
||||
{
|
||||
return fromNumber<long long>(value, mostSignificantByteFirst);
|
||||
return fromNumber<unsigned long long>(value, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -287,37 +375,39 @@ ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFir
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ByteVector::ByteVector()
|
||||
: d(new ByteVectorPrivate())
|
||||
{
|
||||
d = new ByteVectorPrivate;
|
||||
}
|
||||
|
||||
ByteVector::ByteVector(uint size, char value)
|
||||
: d(new ByteVectorPrivate(size, value))
|
||||
{
|
||||
d = new ByteVectorPrivate(size, value);
|
||||
}
|
||||
|
||||
ByteVector::ByteVector(const ByteVector &v) : d(v.d)
|
||||
ByteVector::ByteVector(const ByteVector &v)
|
||||
: d(v.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
ByteVector::ByteVector(char c)
|
||||
ByteVector::ByteVector(const ByteVector &v, uint offset, uint length)
|
||||
: d(new ByteVectorPrivate(v.d, offset, length))
|
||||
{
|
||||
}
|
||||
|
||||
ByteVector::ByteVector(char c)
|
||||
: d(new ByteVectorPrivate(1, c))
|
||||
{
|
||||
d = new ByteVectorPrivate;
|
||||
d->data.push_back(c);
|
||||
d->size = 1;
|
||||
}
|
||||
|
||||
ByteVector::ByteVector(const char *data, uint length)
|
||||
: d(new ByteVectorPrivate(data, length))
|
||||
{
|
||||
d = new ByteVectorPrivate;
|
||||
setData(data, length);
|
||||
}
|
||||
|
||||
ByteVector::ByteVector(const char *data)
|
||||
: d(new ByteVectorPrivate(data, ::strlen(data)))
|
||||
{
|
||||
d = new ByteVectorPrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
ByteVector::~ByteVector()
|
||||
@@ -326,74 +416,68 @@ ByteVector::~ByteVector()
|
||||
delete d;
|
||||
}
|
||||
|
||||
ByteVector &ByteVector::setData(const char *data, uint length)
|
||||
ByteVector &ByteVector::setData(const char *s, uint length)
|
||||
{
|
||||
detach();
|
||||
|
||||
resize(length);
|
||||
|
||||
if(length > 0)
|
||||
::memcpy(DATA(d), data, length);
|
||||
|
||||
*this = ByteVector(s, length);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteVector &ByteVector::setData(const char *data)
|
||||
{
|
||||
return setData(data, ::strlen(data));
|
||||
*this = ByteVector(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
char *ByteVector::data()
|
||||
{
|
||||
detach();
|
||||
return size() > 0 ? DATA(d) : 0;
|
||||
return size() > 0 ? (DATA(d) + d->offset) : 0;
|
||||
}
|
||||
|
||||
const char *ByteVector::data() const
|
||||
{
|
||||
return size() > 0 ? DATA(d) : 0;
|
||||
return size() > 0 ? (DATA(d) + d->offset) : 0;
|
||||
}
|
||||
|
||||
ByteVector ByteVector::mid(uint index, uint length) const
|
||||
{
|
||||
ByteVector v;
|
||||
index = std::min(index, size());
|
||||
length = std::min(length, size() - index);
|
||||
|
||||
if(index > size())
|
||||
return v;
|
||||
|
||||
ConstIterator endIt;
|
||||
|
||||
if(length < size() - index)
|
||||
endIt = d->data.begin() + index + length;
|
||||
else
|
||||
endIt = d->data.end();
|
||||
|
||||
v.d->data.insert(v.d->data.begin(), ConstIterator(d->data.begin() + index), endIt);
|
||||
v.d->size = v.d->data.size();
|
||||
|
||||
return v;
|
||||
return ByteVector(*this, index, length);
|
||||
}
|
||||
|
||||
char ByteVector::at(uint index) const
|
||||
{
|
||||
return index < size() ? d->data[index] : 0;
|
||||
return index < size() ? DATA(d)[d->offset + index] : 0;
|
||||
}
|
||||
|
||||
int ByteVector::find(const ByteVector &pattern, uint offset, int byteAlign) const
|
||||
{
|
||||
return vectorFind<ByteVector>(*this, pattern, offset, byteAlign);
|
||||
return findVector<ConstIterator>(
|
||||
begin(), end(), pattern.begin(), pattern.end(), offset, byteAlign);
|
||||
}
|
||||
|
||||
int ByteVector::find(char c, uint offset, int byteAlign) const
|
||||
{
|
||||
return findChar<ConstIterator>(begin(), end(), c, offset, byteAlign);
|
||||
}
|
||||
|
||||
int ByteVector::rfind(const ByteVector &pattern, uint offset, int byteAlign) const
|
||||
{
|
||||
// Ok, this is a little goofy, but pretty cool after it sinks in. Instead of
|
||||
// reversing the find method's Boyer-Moore search algorithm I created a "mirror"
|
||||
// for a ByteVector to reverse the behavior of the accessors.
|
||||
if(offset > 0) {
|
||||
offset = size() - offset - pattern.size();
|
||||
if(offset >= size())
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
ByteVectorMirror v(*this);
|
||||
ByteVectorMirror p(pattern);
|
||||
const int pos = findVector<ConstReverseIterator>(
|
||||
rbegin(), rend(), pattern.rbegin(), pattern.rend(), offset, byteAlign);
|
||||
|
||||
return v.find(p, offset, byteAlign);
|
||||
if(pos == -1)
|
||||
return -1;
|
||||
else
|
||||
return size() - pos - pattern.size();
|
||||
}
|
||||
|
||||
bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint patternOffset, uint patternLength) const
|
||||
@@ -402,18 +486,11 @@ bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint pattern
|
||||
patternLength = pattern.size();
|
||||
|
||||
// do some sanity checking -- all of these things are needed for the search to be valid
|
||||
|
||||
if(patternLength > size() || offset >= size() || patternOffset >= pattern.size() || patternLength == 0)
|
||||
const uint compareLength = patternLength - patternOffset;
|
||||
if(offset + compareLength > size() || patternOffset >= pattern.size() || patternLength == 0)
|
||||
return false;
|
||||
|
||||
// loop through looking for a mismatch
|
||||
|
||||
for(uint i = 0; i < patternLength - patternOffset; i++) {
|
||||
if(at(i + offset) != pattern[i + patternOffset])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
return (::memcmp(data() + offset, pattern.data() + patternOffset, compareLength) == 0);
|
||||
}
|
||||
|
||||
bool ByteVector::startsWith(const ByteVector &pattern) const
|
||||
@@ -511,74 +588,92 @@ int ByteVector::endsWithPartialMatch(const ByteVector &pattern) const
|
||||
|
||||
ByteVector &ByteVector::append(const ByteVector &v)
|
||||
{
|
||||
if(v.d->size == 0)
|
||||
return *this; // Simply return if appending nothing.
|
||||
if(v.d->length != 0)
|
||||
{
|
||||
detach();
|
||||
|
||||
detach();
|
||||
|
||||
uint originalSize = d->size;
|
||||
resize(d->size + v.d->size);
|
||||
::memcpy(DATA(d) + originalSize, DATA(v.d), v.size());
|
||||
uint originalSize = size();
|
||||
resize(originalSize + v.size());
|
||||
::memcpy(data() + originalSize, v.data(), v.size());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteVector &ByteVector::clear()
|
||||
{
|
||||
detach();
|
||||
d->data.clear();
|
||||
d->size = 0;
|
||||
|
||||
*this = ByteVector();
|
||||
return *this;
|
||||
}
|
||||
|
||||
TagLib::uint ByteVector::size() const
|
||||
{
|
||||
return d->size;
|
||||
return d->length;
|
||||
}
|
||||
|
||||
ByteVector &ByteVector::resize(uint size, char padding)
|
||||
{
|
||||
if(d->size < size) {
|
||||
d->data.reserve(size);
|
||||
d->data.insert(d->data.end(), size - d->size, padding);
|
||||
if(size != d->length) {
|
||||
detach();
|
||||
d->data->data.resize(d->offset + size, padding);
|
||||
d->length = size;
|
||||
}
|
||||
else
|
||||
d->data.erase(d->data.begin() + size, d->data.end());
|
||||
|
||||
d->size = size;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteVector::Iterator ByteVector::begin()
|
||||
{
|
||||
return d->data.begin();
|
||||
return d->data->data.begin() + d->offset;
|
||||
}
|
||||
|
||||
ByteVector::ConstIterator ByteVector::begin() const
|
||||
{
|
||||
return d->data.begin();
|
||||
return d->data->data.begin() + d->offset;
|
||||
}
|
||||
|
||||
ByteVector::Iterator ByteVector::end()
|
||||
{
|
||||
return d->data.end();
|
||||
return d->data->data.begin() + d->offset + d->length;
|
||||
}
|
||||
|
||||
ByteVector::ConstIterator ByteVector::end() const
|
||||
{
|
||||
return d->data.end();
|
||||
return d->data->data.begin() + d->offset + d->length;
|
||||
}
|
||||
|
||||
ByteVector::ReverseIterator ByteVector::rbegin()
|
||||
{
|
||||
std::vector<char> &v = d->data->data;
|
||||
return v.rbegin() + (v.size() - (d->offset + d->length));
|
||||
}
|
||||
|
||||
ByteVector::ConstReverseIterator ByteVector::rbegin() const
|
||||
{
|
||||
std::vector<char> &v = d->data->data;
|
||||
return v.rbegin() + (v.size() - (d->offset + d->length));
|
||||
}
|
||||
|
||||
ByteVector::ReverseIterator ByteVector::rend()
|
||||
{
|
||||
std::vector<char> &v = d->data->data;
|
||||
return v.rbegin() + (v.size() - d->offset);
|
||||
}
|
||||
|
||||
ByteVector::ConstReverseIterator ByteVector::rend() const
|
||||
{
|
||||
std::vector<char> &v = d->data->data;
|
||||
return v.rbegin() + (v.size() - d->offset);
|
||||
}
|
||||
|
||||
bool ByteVector::isNull() const
|
||||
{
|
||||
return d == null.d;
|
||||
return (d == null.d);
|
||||
}
|
||||
|
||||
bool ByteVector::isEmpty() const
|
||||
{
|
||||
return d->data.size() == 0;
|
||||
return (d->length == 0);
|
||||
}
|
||||
|
||||
TagLib::uint ByteVector::checksum() const
|
||||
@@ -591,42 +686,66 @@ TagLib::uint ByteVector::checksum() const
|
||||
|
||||
TagLib::uint ByteVector::toUInt(bool mostSignificantByteFirst) const
|
||||
{
|
||||
return toNumber<uint>(d->data, mostSignificantByteFirst);
|
||||
return toNumber<uint>(*this, 0, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
TagLib::uint ByteVector::toUInt(uint offset, bool mostSignificantByteFirst) const
|
||||
{
|
||||
return toNumber<uint>(*this, offset, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
TagLib::uint ByteVector::toUInt(uint offset, uint length, bool mostSignificantByteFirst) const
|
||||
{
|
||||
return toNumber<uint>(*this, offset, length, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
short ByteVector::toShort(bool mostSignificantByteFirst) const
|
||||
{
|
||||
return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
|
||||
return toNumber<unsigned short>(*this, 0, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
short ByteVector::toShort(uint offset, bool mostSignificantByteFirst) const
|
||||
{
|
||||
return toNumber<unsigned short>(*this, offset, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
unsigned short ByteVector::toUShort(bool mostSignificantByteFirst) const
|
||||
{
|
||||
return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
|
||||
return toNumber<unsigned short>(*this, 0, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
unsigned short ByteVector::toUShort(uint offset, bool mostSignificantByteFirst) const
|
||||
{
|
||||
return toNumber<unsigned short>(*this, offset, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
long long ByteVector::toLongLong(bool mostSignificantByteFirst) const
|
||||
{
|
||||
return toNumber<unsigned long long>(d->data, mostSignificantByteFirst);
|
||||
return toNumber<unsigned long long>(*this, 0, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
long long ByteVector::toLongLong(uint offset, bool mostSignificantByteFirst) const
|
||||
{
|
||||
return toNumber<unsigned long long>(*this, offset, mostSignificantByteFirst);
|
||||
}
|
||||
|
||||
const char &ByteVector::operator[](int index) const
|
||||
{
|
||||
return d->data[index];
|
||||
return d->data->data[d->offset + index];
|
||||
}
|
||||
|
||||
char &ByteVector::operator[](int index)
|
||||
{
|
||||
detach();
|
||||
|
||||
return d->data[index];
|
||||
return d->data->data[d->offset + index];
|
||||
}
|
||||
|
||||
bool ByteVector::operator==(const ByteVector &v) const
|
||||
{
|
||||
if(d->size != v.d->size)
|
||||
if(size() != v.size())
|
||||
return false;
|
||||
|
||||
return ::memcmp(data(), v.data(), size()) == 0;
|
||||
return (::memcmp(data(), v.data(), size()) == 0);
|
||||
}
|
||||
|
||||
bool ByteVector::operator!=(const ByteVector &v) const
|
||||
@@ -636,10 +755,10 @@ bool ByteVector::operator!=(const ByteVector &v) const
|
||||
|
||||
bool ByteVector::operator==(const char *s) const
|
||||
{
|
||||
if(d->size != ::strlen(s))
|
||||
if(size() != ::strlen(s))
|
||||
return false;
|
||||
|
||||
return ::memcmp(data(), s, d->size) == 0;
|
||||
return (::memcmp(data(), s, size()) == 0);
|
||||
}
|
||||
|
||||
bool ByteVector::operator!=(const char *s) const
|
||||
@@ -649,8 +768,7 @@ bool ByteVector::operator!=(const char *s) const
|
||||
|
||||
bool ByteVector::operator<(const ByteVector &v) const
|
||||
{
|
||||
int result = ::memcmp(data(), v.data(), d->size < v.d->size ? d->size : v.d->size);
|
||||
|
||||
const int result = ::memcmp(data(), v.data(), std::min(size(), v.size()));
|
||||
if(result != 0)
|
||||
return result < 0;
|
||||
else
|
||||
@@ -697,12 +815,12 @@ ByteVector &ByteVector::operator=(const char *data)
|
||||
ByteVector ByteVector::toHex() const
|
||||
{
|
||||
ByteVector encoded(size() * 2);
|
||||
char *p = encoded.data();
|
||||
|
||||
uint j = 0;
|
||||
for(uint i = 0; i < size(); i++) {
|
||||
unsigned char c = d->data[i];
|
||||
encoded[j++] = hexTable[(c >> 4) & 0x0F];
|
||||
encoded[j++] = hexTable[(c ) & 0x0F];
|
||||
unsigned char c = data()[i];
|
||||
*p++ = hexTable[(c >> 4) & 0x0F];
|
||||
*p++ = hexTable[(c ) & 0x0F];
|
||||
}
|
||||
|
||||
return encoded;
|
||||
@@ -714,17 +832,24 @@ ByteVector ByteVector::toHex() const
|
||||
|
||||
void ByteVector::detach()
|
||||
{
|
||||
if(d->data->count() > 1) {
|
||||
d->data->deref();
|
||||
d->data = new DataPrivate(d->data->data, d->offset, d->length);
|
||||
d->offset = 0;
|
||||
}
|
||||
|
||||
if(d->count() > 1) {
|
||||
d->deref();
|
||||
d = new ByteVectorPrivate(d->data);
|
||||
d = new ByteVectorPrivate(d->data->data, d->offset, d->length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// related functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const ByteVector &v)
|
||||
std::ostream &operator<<(std::ostream &s, const TagLib::ByteVector &v)
|
||||
{
|
||||
for(TagLib::uint i = 0; i < v.size(); i++)
|
||||
s << v[i];
|
||||
|
||||
@@ -48,6 +48,8 @@ namespace TagLib {
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
typedef std::vector<char>::iterator Iterator;
|
||||
typedef std::vector<char>::const_iterator ConstIterator;
|
||||
typedef std::vector<char>::reverse_iterator ReverseIterator;
|
||||
typedef std::vector<char>::const_reverse_iterator ConstReverseIterator;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@@ -62,12 +64,17 @@ namespace TagLib {
|
||||
ByteVector(uint size, char value = 0);
|
||||
|
||||
/*!
|
||||
* Contructs a byte vector that is a copy of \a v.
|
||||
* Constructs a byte vector that is a copy of \a v.
|
||||
*/
|
||||
ByteVector(const ByteVector &v);
|
||||
|
||||
/*!
|
||||
* Contructs a byte vector that contains \a c.
|
||||
* Constructs a byte vector that is a copy of \a v.
|
||||
*/
|
||||
ByteVector(const ByteVector &v, uint offset, uint length);
|
||||
|
||||
/*!
|
||||
* Constructs a byte vector that contains \a c.
|
||||
*/
|
||||
ByteVector(char c);
|
||||
|
||||
@@ -135,6 +142,14 @@ namespace TagLib {
|
||||
*/
|
||||
int find(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
|
||||
|
||||
/*!
|
||||
* Searches the char for \a c starting at \a offset and returns
|
||||
* the offset. Returns \a npos if the pattern was not found. If \a byteAlign is
|
||||
* specified the pattern will only be matched if it starts on a byte divisible
|
||||
* by \a byteAlign (starting from \a offset).
|
||||
*/
|
||||
int find(char c, uint offset = 0, int byteAlign = 1) const;
|
||||
|
||||
/*!
|
||||
* Searches the ByteVector for \a pattern starting from either the end of the
|
||||
* vector or \a offset and returns the offset. Returns -1 if the pattern was
|
||||
@@ -222,6 +237,26 @@ namespace TagLib {
|
||||
*/
|
||||
ConstIterator end() const;
|
||||
|
||||
/*!
|
||||
* Returns a ReverseIterator that points to the front of the vector.
|
||||
*/
|
||||
ReverseIterator rbegin();
|
||||
|
||||
/*!
|
||||
* Returns a ConstReverseIterator that points to the front of the vector.
|
||||
*/
|
||||
ConstReverseIterator rbegin() const;
|
||||
|
||||
/*!
|
||||
* Returns a ReverseIterator that points to the back of the vector.
|
||||
*/
|
||||
ReverseIterator rend();
|
||||
|
||||
/*!
|
||||
* Returns a ConstReverseIterator that points to the back of the vector.
|
||||
*/
|
||||
ConstReverseIterator rend() const;
|
||||
|
||||
/*!
|
||||
* Returns true if the vector is null.
|
||||
*
|
||||
@@ -256,7 +291,32 @@ namespace TagLib {
|
||||
uint toUInt(bool mostSignificantByteFirst = true) const;
|
||||
|
||||
/*!
|
||||
* Converts the first 2 bytes of the vector to a short.
|
||||
* Converts the 4 bytes at \a offset of the vector to an unsigned integer.
|
||||
*
|
||||
* If \a mostSignificantByteFirst is true this will operate left to right
|
||||
* evaluating the integer. For example if \a mostSignificantByteFirst is
|
||||
* true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 ==
|
||||
* 0x01000000 == 1.
|
||||
*
|
||||
* \see fromUInt()
|
||||
*/
|
||||
uint toUInt(uint offset, bool mostSignificantByteFirst = true) const;
|
||||
|
||||
/*!
|
||||
* Converts the \a length bytes at \a offset of the vector to an unsigned
|
||||
* integer. If \a length is larger than 4, the excess is ignored.
|
||||
*
|
||||
* If \a mostSignificantByteFirst is true this will operate left to right
|
||||
* evaluating the integer. For example if \a mostSignificantByteFirst is
|
||||
* true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 ==
|
||||
* 0x01000000 == 1.
|
||||
*
|
||||
* \see fromUInt()
|
||||
*/
|
||||
uint toUInt(uint offset, uint length, bool mostSignificantByteFirst = true) const;
|
||||
|
||||
/*!
|
||||
* Converts the first 2 bytes of the vector to a (signed) short.
|
||||
*
|
||||
* If \a mostSignificantByteFirst is true this will operate left to right
|
||||
* evaluating the integer. For example if \a mostSignificantByteFirst is
|
||||
@@ -266,6 +326,17 @@ namespace TagLib {
|
||||
*/
|
||||
short toShort(bool mostSignificantByteFirst = true) const;
|
||||
|
||||
/*!
|
||||
* Converts the 2 bytes at \a offset of the vector to a (signed) short.
|
||||
*
|
||||
* If \a mostSignificantByteFirst is true this will operate left to right
|
||||
* evaluating the integer. For example if \a mostSignificantByteFirst is
|
||||
* true then $00 $01 == 0x0001 == 1, if false, $01 00 == 0x01000000 == 1.
|
||||
*
|
||||
* \see fromShort()
|
||||
*/
|
||||
short toShort(uint offset, bool mostSignificantByteFirst = true) const;
|
||||
|
||||
/*!
|
||||
* Converts the first 2 bytes of the vector to a unsigned short.
|
||||
*
|
||||
@@ -277,6 +348,17 @@ namespace TagLib {
|
||||
*/
|
||||
unsigned short toUShort(bool mostSignificantByteFirst = true) const;
|
||||
|
||||
/*!
|
||||
* Converts the 2 bytes at \a offset of the vector to a unsigned short.
|
||||
*
|
||||
* If \a mostSignificantByteFirst is true this will operate left to right
|
||||
* evaluating the integer. For example if \a mostSignificantByteFirst is
|
||||
* true then $00 $01 == 0x0001 == 1, if false, $01 00 == 0x01000000 == 1.
|
||||
*
|
||||
* \see fromShort()
|
||||
*/
|
||||
unsigned short toUShort(uint offset, bool mostSignificantByteFirst = true) const;
|
||||
|
||||
/*!
|
||||
* Converts the first 8 bytes of the vector to a (signed) long long.
|
||||
*
|
||||
@@ -289,6 +371,18 @@ namespace TagLib {
|
||||
*/
|
||||
long long toLongLong(bool mostSignificantByteFirst = true) const;
|
||||
|
||||
/*!
|
||||
* Converts the 8 bytes at \a offset of the vector to a (signed) long long.
|
||||
*
|
||||
* If \a mostSignificantByteFirst is true this will operate left to right
|
||||
* evaluating the integer. For example if \a mostSignificantByteFirst is
|
||||
* true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1,
|
||||
* if false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
|
||||
*
|
||||
* \see fromUInt()
|
||||
*/
|
||||
long long toLongLong(uint offset, bool mostSignificantByteFirst = true) const;
|
||||
|
||||
/*!
|
||||
* Creates a 4 byte ByteVector based on \a value. If
|
||||
* \a mostSignificantByteFirst is true, then this will operate left to right
|
||||
@@ -413,7 +507,6 @@ namespace TagLib {
|
||||
class ByteVectorPrivate;
|
||||
ByteVectorPrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -23,33 +23,77 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tstring.h"
|
||||
#include "tdebuglistener.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
void TagLib::debug(const String &s)
|
||||
namespace
|
||||
{
|
||||
std::cerr << "TagLib: " << s << std::endl;
|
||||
}
|
||||
String format(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
void TagLib::debugData(const ByteVector &v)
|
||||
{
|
||||
for(uint i = 0; i < v.size(); i++) {
|
||||
char buf[256];
|
||||
|
||||
std::cout << "*** [" << i << "] - '" << char(v[i]) << "' - int " << int(v[i])
|
||||
<< std::endl;
|
||||
#if defined(HAVE_SNPRINTF)
|
||||
|
||||
std::bitset<8> b(v[i]);
|
||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
|
||||
for(int j = 0; j < 8; j++)
|
||||
std::cout << i << ":" << j << " " << b.test(j) << std::endl;
|
||||
#elif defined(HAVE_SPRINTF_S)
|
||||
|
||||
std::cout << std::endl;
|
||||
vsprintf_s(buf, fmt, args);
|
||||
|
||||
#else
|
||||
|
||||
// Be careful. May cause a buffer overflow.
|
||||
vsprintf(buf, fmt, args);
|
||||
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
|
||||
return String(buf);
|
||||
}
|
||||
}
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
// The instance is defined in tdebuglistener.cpp.
|
||||
extern DebugListener *debugListener;
|
||||
|
||||
void debug(const String &s)
|
||||
{
|
||||
#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE)
|
||||
|
||||
debugListener->printMessage("TagLib: " + s + "\n");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void debugData(const ByteVector &v)
|
||||
{
|
||||
#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE)
|
||||
|
||||
for(size_t i = 0; i < v.size(); ++i)
|
||||
{
|
||||
std::string bits = std::bitset<8>(v[i]).to_string();
|
||||
String msg = format("*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n",
|
||||
i, v[i], v[i], v[i], bits.c_str());
|
||||
|
||||
debugListener->printMessage(msg);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@ namespace TagLib {
|
||||
class ByteVector;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
#ifndef NDEBUG
|
||||
|
||||
/*!
|
||||
* A simple function that prints debugging output to cerr if debugging is
|
||||
* not disabled.
|
||||
* A simple function that outputs the debug messages to the listener.
|
||||
* The default listener redirects the messages to \a stderr when NDEBUG is
|
||||
* not defined.
|
||||
*
|
||||
* \warning Do not use this outside of TagLib, it could lead to undefined
|
||||
* symbols in your build if TagLib is built with NDEBUG defined and your
|
||||
@@ -45,7 +45,7 @@ namespace TagLib {
|
||||
* \internal
|
||||
*/
|
||||
void debug(const String &s);
|
||||
|
||||
|
||||
/*!
|
||||
* For debugging binary data.
|
||||
*
|
||||
@@ -56,16 +56,7 @@ namespace TagLib {
|
||||
* \internal
|
||||
*/
|
||||
void debugData(const ByteVector &v);
|
||||
|
||||
#else
|
||||
|
||||
// Define these to an empty statement if debugging is disabled.
|
||||
|
||||
#define debug(x)
|
||||
#define debugData(x)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user