mirror of
https://github.com/taglib/taglib.git
synced 2026-06-07 14:59:24 -04:00
Compare commits
379 Commits
v2.0beta
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65a6a4e225 | ||
|
|
02090f335d | ||
|
|
96a4d896ba | ||
|
|
15163b7af2 | ||
|
|
19ee7c86c4 | ||
|
|
fc31a09c03 | ||
|
|
2052cda5fb | ||
|
|
6048cdef3e | ||
|
|
ff34e42aef | ||
|
|
bac14180e9 | ||
|
|
c30bae9b00 | ||
|
|
4dcdaaf92a | ||
|
|
507f1a96a7 | ||
|
|
81945efdff | ||
|
|
2075d865cd | ||
|
|
89f06af3f7 | ||
|
|
3ae0d4aa90 | ||
|
|
7e9f019a49 | ||
|
|
10e8866fec | ||
|
|
bd4a45b07e | ||
|
|
4328f934c8 | ||
|
|
1c20f92a8f | ||
|
|
d8114059ee | ||
|
|
eea1a1b200 | ||
|
|
ef1ae1a8fe | ||
|
|
7eab1bf6df | ||
|
|
6a2e276767 | ||
|
|
e188a39b5c | ||
|
|
e77e20597d | ||
|
|
0b9a2df3ec | ||
|
|
bd5688ae5f | ||
|
|
13223ad497 | ||
|
|
5c8cb9b86b | ||
|
|
3a6f0d46aa | ||
|
|
ef6d76889b | ||
|
|
28470221c0 | ||
|
|
f355110d18 | ||
|
|
921a68ae55 | ||
|
|
d9df59306f | ||
|
|
fea7e3d4a2 | ||
|
|
b6d21ce890 | ||
|
|
51b85abc0b | ||
|
|
9452970528 | ||
|
|
53ac43b5f6 | ||
|
|
a64772a832 | ||
|
|
c352425ee7 | ||
|
|
0f096af504 | ||
|
|
a16c95b33f | ||
|
|
821ff14a43 | ||
|
|
33c0ece830 | ||
|
|
57e849b0d5 | ||
|
|
b972f24193 | ||
|
|
1a73c82a76 | ||
|
|
c3c862e911 | ||
|
|
75159614fc | ||
|
|
ed0305bd3f | ||
|
|
f72b0ecebf | ||
|
|
f25a54a177 | ||
|
|
ff94a6e4a0 | ||
|
|
1f5ed5fb28 | ||
|
|
0e4f9f4e94 | ||
|
|
6857926da2 | ||
|
|
afa137e1c1 | ||
|
|
d36550f96d | ||
|
|
6f5874a035 | ||
|
|
2f29f0e1d0 | ||
|
|
7baa7e70fe | ||
|
|
faca7ac8f4 | ||
|
|
1063110701 | ||
|
|
973fb49cde | ||
|
|
15775d4135 | ||
|
|
52774d66ab | ||
|
|
b7e3ff8cb5 | ||
|
|
b4a33b0306 | ||
|
|
aa9e5c3c2f | ||
|
|
0556b6ca33 | ||
|
|
b3014f0878 | ||
|
|
6566352728 | ||
|
|
1d0552cab1 | ||
|
|
bb49005267 | ||
|
|
47cb23d738 | ||
|
|
d37c081cda | ||
|
|
dbe3a03b62 | ||
|
|
5da35f5a51 | ||
|
|
176e133f32 | ||
|
|
722f317f97 | ||
|
|
c2753f8d3c | ||
|
|
da7424636b | ||
|
|
0b2a3ce400 | ||
|
|
1ff30e55da | ||
|
|
0e981adcc6 | ||
|
|
a18d402bd9 | ||
|
|
dc994a65f0 | ||
|
|
4998dedcf4 | ||
|
|
794d9fd27b | ||
|
|
35cf4afdba | ||
|
|
8ecfba0c30 | ||
|
|
ace203a6e9 | ||
|
|
ccb1d036f2 | ||
|
|
6dbc340899 | ||
|
|
34931b1d3f | ||
|
|
612c84731c | ||
|
|
9e95156319 | ||
|
|
7ed8763a33 | ||
|
|
3ea69ed165 | ||
|
|
71e8915568 | ||
|
|
9b8f774fb3 | ||
|
|
30551864fa | ||
|
|
ba2167ef92 | ||
|
|
dc0cc4e7fa | ||
|
|
ef09a707b8 | ||
|
|
400fa04b1c | ||
|
|
e6e11c957d | ||
|
|
ab2389819e | ||
|
|
8e35b43e32 | ||
|
|
b85a0d0710 | ||
|
|
87040570c0 | ||
|
|
e1b5e2c9c3 | ||
|
|
51449f131f | ||
|
|
c4c4a28daa | ||
|
|
d52baafa24 | ||
|
|
bdfd7dc003 | ||
|
|
0884945567 | ||
|
|
93c2758ca6 | ||
|
|
2ec7a884c3 | ||
|
|
a5c56d31ac | ||
|
|
f0e8f39de1 | ||
|
|
efcd751a1c | ||
|
|
34f5a7da0d | ||
|
|
7345b7569f | ||
|
|
f93397fa7b | ||
|
|
81f806317f | ||
|
|
8e6e580c60 | ||
|
|
909d28d4fe | ||
|
|
6be3b7a1c7 | ||
|
|
29d5f75d0d | ||
|
|
8936fb2fc3 | ||
|
|
1dc2471d01 | ||
|
|
5687c70d80 | ||
|
|
3f19a522fc | ||
|
|
3cea7f267a | ||
|
|
1a9c49e42b | ||
|
|
76c688bffd | ||
|
|
fbbebfcec6 | ||
|
|
7f20c95cfc | ||
|
|
98c6088f91 | ||
|
|
22f8b527f7 | ||
|
|
65beb8cc06 | ||
|
|
c94d482215 | ||
|
|
d05101153f | ||
|
|
bceb568ea8 | ||
|
|
8390a91e48 | ||
|
|
4dca1ab68c | ||
|
|
3ced5e92b7 | ||
|
|
dce1664f30 | ||
|
|
a303d88782 | ||
|
|
8b77051ecf | ||
|
|
329abdbce7 | ||
|
|
d3eefdde34 | ||
|
|
c5353ed5ef | ||
|
|
5199a3cbb6 | ||
|
|
d93b4af8ca | ||
|
|
dd1a885f27 | ||
|
|
50b55a8a94 | ||
|
|
5d519c5207 | ||
|
|
bd84c8928a | ||
|
|
279d71c1e8 | ||
|
|
d8f5937091 | ||
|
|
be041ef2aa | ||
|
|
d643878e9f | ||
|
|
0c2e21024f | ||
|
|
dad73ebfc5 | ||
|
|
37b0ba6989 | ||
|
|
733b537c63 | ||
|
|
caf705958e | ||
|
|
ebaecc47f4 | ||
|
|
69bcc52ef3 | ||
|
|
644b47910f | ||
|
|
97b0f9e5a5 | ||
|
|
3cdbdfe0b9 | ||
|
|
7ff61ff5ac | ||
|
|
f7d2935d7b | ||
|
|
8a22c7ce75 | ||
|
|
6dc1ce7fe7 | ||
|
|
28cede5965 | ||
|
|
634d2ede3b | ||
|
|
753c63e0e6 | ||
|
|
637a08b8b0 | ||
|
|
0d45ddaf25 | ||
|
|
499d503aa2 | ||
|
|
116b1fcff9 | ||
|
|
85e1ef1fba | ||
|
|
88d55057fc | ||
|
|
cd540cbee2 | ||
|
|
0acdd2379e | ||
|
|
ced5f262ba | ||
|
|
e5c2232b10 | ||
|
|
8fc110dfd0 | ||
|
|
b949e23be3 | ||
|
|
43240b8ecc | ||
|
|
c09ea21ae3 | ||
|
|
57d12de7dd | ||
|
|
0d1ed55c7e | ||
|
|
e7cfbfed76 | ||
|
|
c63dcdc459 | ||
|
|
b7ffa04f20 | ||
|
|
fde99fa2fb | ||
|
|
5c9360afa2 | ||
|
|
60590c0a1a | ||
|
|
70e58dcb21 | ||
|
|
47561d8350 | ||
|
|
829f460c3c | ||
|
|
bb622bc163 | ||
|
|
e17b8e14de | ||
|
|
f6c23ac92c | ||
|
|
dbb4b47be3 | ||
|
|
9f9af997ee | ||
|
|
314cc33585 | ||
|
|
6d40cbc04f | ||
|
|
6d89689c0e | ||
|
|
48191904f1 | ||
|
|
45e8e83041 | ||
|
|
c066ccd32d | ||
|
|
90d43956ec | ||
|
|
09b574a19a | ||
|
|
8be47ec8dc | ||
|
|
2316a9661a | ||
|
|
2c770600c4 | ||
|
|
d3920f4ab6 | ||
|
|
c9904354f9 | ||
|
|
804f882e38 | ||
|
|
c6f323357f | ||
|
|
25590fe70c | ||
|
|
5f5c670f31 | ||
|
|
8fcc992815 | ||
|
|
7c64b1966a | ||
|
|
6e335694c9 | ||
|
|
4be506fae2 | ||
|
|
6bf8177796 | ||
|
|
409bba5936 | ||
|
|
ea6ab84c42 | ||
|
|
d982b846c4 | ||
|
|
1f08a8d7c6 | ||
|
|
c972b0c080 | ||
|
|
a30f992106 | ||
|
|
e2a168af7a | ||
|
|
8f31d882b0 | ||
|
|
a23d41c509 | ||
|
|
ca48435ab6 | ||
|
|
0a4756b527 | ||
|
|
437b61a311 | ||
|
|
6c2f99c89a | ||
|
|
2b5fee2df1 | ||
|
|
dd7758c76e | ||
|
|
5ed8d12478 | ||
|
|
09e7df8509 | ||
|
|
befa5724f1 | ||
|
|
23418c25a4 | ||
|
|
f07bf28f5f | ||
|
|
2bdded5597 | ||
|
|
8826ae2484 | ||
|
|
2d0644dca7 | ||
|
|
4ce7ebe520 | ||
|
|
72bb1a887e | ||
|
|
04e07ad4b0 | ||
|
|
c6315d8371 | ||
|
|
de04f4eb19 | ||
|
|
73e5acdae9 | ||
|
|
7e1f3545cd | ||
|
|
8457be8ab4 | ||
|
|
70044534aa | ||
|
|
3e7dc496be | ||
|
|
43cfbd568b | ||
|
|
b3a3aa8a95 | ||
|
|
36512745cf | ||
|
|
269f34ff2a | ||
|
|
c50a0ae1dc | ||
|
|
6b56510f99 | ||
|
|
91d3b8513d | ||
|
|
fbe329bb70 | ||
|
|
ffc7bcfef0 | ||
|
|
93db8aa135 | ||
|
|
6182378c87 | ||
|
|
36ceaadfaa | ||
|
|
362900c721 | ||
|
|
fb71d7341a | ||
|
|
3dc37a482e | ||
|
|
3d3c97b5ef | ||
|
|
ea968ae3be | ||
|
|
82e616101a | ||
|
|
3d8bb4d4b9 | ||
|
|
454abde101 | ||
|
|
f97a422bc2 | ||
|
|
4fc2a3bdd8 | ||
|
|
10b4bd61e2 | ||
|
|
c3efe19d8a | ||
|
|
1390860929 | ||
|
|
c44351aab5 | ||
|
|
5c96ff42c3 | ||
|
|
bb61b43ba0 | ||
|
|
2f711e26ca | ||
|
|
781aa05f0e | ||
|
|
c265c2a874 | ||
|
|
5171de33e4 | ||
|
|
2aaf41e0bb | ||
|
|
27115557a0 | ||
|
|
86cee56f74 | ||
|
|
843a12a0c6 | ||
|
|
a0e8963fa0 | ||
|
|
265013ef5b | ||
|
|
39e02ad21b | ||
|
|
fc2b69a81e | ||
|
|
1618833a6b | ||
|
|
56c85a2d68 | ||
|
|
83b6fdef72 | ||
|
|
cd082cd8e3 | ||
|
|
b744f66819 | ||
|
|
2ed7a59d95 | ||
|
|
7d5ce5624a | ||
|
|
e93696f573 | ||
|
|
914e5f9fd8 | ||
|
|
b1dcdc5bd8 | ||
|
|
b4df82a0b0 | ||
|
|
be081d23d3 | ||
|
|
0acc22e84b | ||
|
|
62efb9ff17 | ||
|
|
ca5ce53b1e | ||
|
|
8cae2b9f28 | ||
|
|
d719eb0ac6 | ||
|
|
cab68e2152 | ||
|
|
96cf908232 | ||
|
|
42a74babb5 | ||
|
|
4e05923479 | ||
|
|
c86ea7bdff | ||
|
|
a842220fe6 | ||
|
|
19ce4d0dfa | ||
|
|
6e3639de9e | ||
|
|
0792eedd12 | ||
|
|
86b7cabf44 | ||
|
|
de19ad72ab | ||
|
|
9b19453059 | ||
|
|
dbd7c151d6 | ||
|
|
d3af7c0b02 | ||
|
|
64447598e5 | ||
|
|
fba8f42588 | ||
|
|
5578843220 | ||
|
|
90fd336a22 | ||
|
|
fa38c805f5 | ||
|
|
b60b444d7b | ||
|
|
db892c43e7 | ||
|
|
f2b56a5511 | ||
|
|
2a86da4df5 | ||
|
|
f6741b65e4 | ||
|
|
60ba972244 | ||
|
|
e8ae4ecd93 | ||
|
|
f407d1456c | ||
|
|
50e616df8d | ||
|
|
9440055eb1 | ||
|
|
cb8b8d50f6 | ||
|
|
c3f9c63542 | ||
|
|
aa801e58ec | ||
|
|
9e41939eb1 | ||
|
|
0d49e6bff0 | ||
|
|
6e2d5bc1dc | ||
|
|
9cede44af6 | ||
|
|
ae11b3db38 | ||
|
|
b3c112ed99 | ||
|
|
5b604f41be | ||
|
|
8599c1bd38 | ||
|
|
27a078fe9b | ||
|
|
2b31fc2037 | ||
|
|
b01f45e141 | ||
|
|
c46ecd186d | ||
|
|
c2eecc0804 | ||
|
|
4dcf0b41c6 | ||
|
|
9577784bae | ||
|
|
f52bab8a01 | ||
|
|
738e2ae7a7 | ||
|
|
231eb08396 |
@@ -12,7 +12,7 @@ insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Trim trailing whitespaces
|
||||
# Trim traling whitespaces
|
||||
[*.{h,cpp,tcc,cmake,yml}]
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
|
||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
59
.github/workflows/build.yml
vendored
59
.github/workflows/build.yml
vendored
@@ -1,59 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
include:
|
||||
- os: windows-latest
|
||||
cmake_extra_args: '-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"'
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ubuntu
|
||||
run: sudo apt install -y libcppunit-dev libutfcpp-dev zlib1g-dev
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Set up macOS
|
||||
run: |
|
||||
brew update
|
||||
brew install cppunit utf8cpp
|
||||
if: matrix.os == 'macos-latest'
|
||||
|
||||
- name: Set up Windows
|
||||
run: vcpkg install cppunit utfcpp zlib --triplet x64-windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Configure
|
||||
run: >
|
||||
cmake -B${{github.workspace}}/build
|
||||
-DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON
|
||||
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
${{ matrix.cmake_extra_args }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
|
||||
if: matrix.os != 'windows-latest'
|
||||
|
||||
- name: Test Windows
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: |
|
||||
$env:Path += ";$PWD\taglib\Release;$PWD\bindings\c\Release"
|
||||
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\cppunit_x64-windows\bin"
|
||||
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\utfcpp_x64-windows\bin"
|
||||
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\zlib_x64-windows\bin"
|
||||
ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
|
||||
if: matrix.os == 'windows-latest'
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -3,7 +3,6 @@ cmake_uninstall.cmake
|
||||
Makefile
|
||||
CTestTestfile.cmake
|
||||
CMakeFiles/
|
||||
CMakeLists.txt.user*
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
@@ -13,7 +12,6 @@ CMakeLists.txt.user*
|
||||
*.suo
|
||||
*.user
|
||||
.*
|
||||
!.github/workflows/
|
||||
*~
|
||||
/CMakeCache.txt
|
||||
/Doxyfile
|
||||
@@ -49,9 +47,3 @@ CMakeLists.txt.user*
|
||||
taglib.h.stamp
|
||||
taglib.xcodeproj
|
||||
CMakeScripts
|
||||
/.clang-format
|
||||
/compile_commands.json
|
||||
/build/
|
||||
.clangd
|
||||
.cache
|
||||
.idea
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "3rdparty/utfcpp"]
|
||||
path = 3rdparty/utfcpp
|
||||
url = https://github.com/nemtrif/utfcpp.git
|
||||
30
.travis.yml
Normal file
30
.travis.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
language: cpp
|
||||
|
||||
sudo: false
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
dist: trusty
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcppunit-dev
|
||||
- zlib1g-dev
|
||||
- libboost-dev
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
|
||||
install:
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install cppunit; fi
|
||||
|
||||
script: cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DCMAKE_CXX_FLAGS="-std=c++11" . && make && make check
|
||||
327
3rdparty/utf8-cpp/checked.h
vendored
Normal file
327
3rdparty/utf8-cpp/checked.h
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
// Copyright 2006-2016 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include "core.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// Base for the exceptions that may be thrown from the library
|
||||
class exception : public ::std::exception {
|
||||
};
|
||||
|
||||
// Exceptions that may be thrown from the library functions.
|
||||
class invalid_code_point : public exception {
|
||||
uint32_t cp;
|
||||
public:
|
||||
invalid_code_point(uint32_t codepoint) : cp(codepoint) {}
|
||||
virtual const char* what() const throw() { return "Invalid code point"; }
|
||||
uint32_t code_point() const {return cp;}
|
||||
};
|
||||
|
||||
class invalid_utf8 : public exception {
|
||||
uint8_t u8;
|
||||
public:
|
||||
invalid_utf8 (uint8_t u) : u8(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-8"; }
|
||||
uint8_t utf8_octet() const {return u8;}
|
||||
};
|
||||
|
||||
class invalid_utf16 : public exception {
|
||||
uint16_t u16;
|
||||
public:
|
||||
invalid_utf16 (uint16_t u) : u16(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-16"; }
|
||||
uint16_t utf16_word() const {return u16;}
|
||||
};
|
||||
|
||||
class not_enough_room : public exception {
|
||||
public:
|
||||
virtual const char* what() const throw() { return "Not enough space"; }
|
||||
};
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator append(uint32_t cp, octet_iterator result)
|
||||
{
|
||||
if (!utf8::internal::is_code_point_valid(cp))
|
||||
throw invalid_code_point(cp);
|
||||
|
||||
if (cp < 0x80) // one octet
|
||||
*(result++) = static_cast<uint8_t>(cp);
|
||||
else if (cp < 0x800) { // two octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 0x10000) { // three octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else { // four octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
|
||||
{
|
||||
while (start != end) {
|
||||
octet_iterator sequence_start = start;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(start, end);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
for (octet_iterator it = sequence_start; it != start; ++it)
|
||||
*out++ = *it;
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM:
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
break;
|
||||
case internal::INCOMPLETE_SEQUENCE:
|
||||
case internal::OVERLONG_SEQUENCE:
|
||||
case internal::INVALID_CODE_POINT:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
// just one replacement mark for the sequence
|
||||
while (start != end && utf8::internal::is_trail(*start))
|
||||
++start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
|
||||
{
|
||||
static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
|
||||
return utf8::replace_invalid(start, end, out, replacement_marker);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t next(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
uint32_t cp = 0;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM :
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD :
|
||||
case internal::INCOMPLETE_SEQUENCE :
|
||||
case internal::OVERLONG_SEQUENCE :
|
||||
throw invalid_utf8(*it);
|
||||
case internal::INVALID_CODE_POINT :
|
||||
throw invalid_code_point(cp);
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t peek_next(octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t prior(octet_iterator& it, octet_iterator start)
|
||||
{
|
||||
// can't do much if it == start
|
||||
if (it == start)
|
||||
throw not_enough_room();
|
||||
|
||||
octet_iterator end = it;
|
||||
// Go back until we hit either a lead octet or start
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
return utf8::peek_next(it, end);
|
||||
}
|
||||
|
||||
/// Deprecated in versions that include "prior"
|
||||
template <typename octet_iterator>
|
||||
uint32_t previous(octet_iterator& it, octet_iterator pass_start)
|
||||
{
|
||||
octet_iterator end = it;
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == pass_start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename distance_type>
|
||||
void advance (octet_iterator& it, distance_type n, octet_iterator end)
|
||||
{
|
||||
for (distance_type i = 0; i < n; ++i)
|
||||
utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
typename std::iterator_traits<octet_iterator>::difference_type
|
||||
distance (octet_iterator first, octet_iterator last)
|
||||
{
|
||||
typename std::iterator_traits<octet_iterator>::difference_type dist;
|
||||
for (dist = 0; first < last; ++dist)
|
||||
utf8::next(first, last);
|
||||
return dist;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end) {
|
||||
uint32_t cp = utf8::internal::mask16(*start++);
|
||||
// Take care of surrogate pairs first
|
||||
if (utf8::internal::is_lead_surrogate(cp)) {
|
||||
if (start != end) {
|
||||
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
|
||||
if (utf8::internal::is_trail_surrogate(trail_surrogate))
|
||||
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
|
||||
}
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
}
|
||||
// Lone trail surrogate
|
||||
else if (utf8::internal::is_trail_surrogate(cp))
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
result = utf8::append(cp, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
|
||||
{
|
||||
while (start < end) {
|
||||
uint32_t cp = utf8::next(start, end);
|
||||
if (cp > 0xffff) { //make a surrogate pair
|
||||
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
|
||||
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
|
||||
}
|
||||
else
|
||||
*result++ = static_cast<uint16_t>(cp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
result = utf8::append(*(start++), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
|
||||
{
|
||||
while (start < end)
|
||||
(*result++) = utf8::next(start, end);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// The iterator class
|
||||
template <typename octet_iterator>
|
||||
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
|
||||
octet_iterator it;
|
||||
octet_iterator range_start;
|
||||
octet_iterator range_end;
|
||||
public:
|
||||
iterator () {}
|
||||
explicit iterator (const octet_iterator& octet_it,
|
||||
const octet_iterator& rangestart,
|
||||
const octet_iterator& rangeend) :
|
||||
it(octet_it), range_start(rangestart), range_end(rangeend)
|
||||
{
|
||||
if (it < range_start || it > range_end)
|
||||
throw std::out_of_range("Invalid utf-8 iterator position");
|
||||
}
|
||||
// the default "big three" are OK
|
||||
octet_iterator base () const { return it; }
|
||||
uint32_t operator * () const
|
||||
{
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, range_end);
|
||||
}
|
||||
bool operator == (const iterator& rhs) const
|
||||
{
|
||||
if (range_start != rhs.range_start || range_end != rhs.range_end)
|
||||
throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
|
||||
return (it == rhs.it);
|
||||
}
|
||||
bool operator != (const iterator& rhs) const
|
||||
{
|
||||
return !(operator == (rhs));
|
||||
}
|
||||
iterator& operator ++ ()
|
||||
{
|
||||
utf8::next(it, range_end);
|
||||
return *this;
|
||||
}
|
||||
iterator operator ++ (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::next(it, range_end);
|
||||
return temp;
|
||||
}
|
||||
iterator& operator -- ()
|
||||
{
|
||||
utf8::prior(it, range_start);
|
||||
return *this;
|
||||
}
|
||||
iterator operator -- (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::prior(it, range_start);
|
||||
return temp;
|
||||
}
|
||||
}; // class iterator
|
||||
|
||||
} // namespace utf8
|
||||
|
||||
#endif //header guard
|
||||
|
||||
|
||||
332
3rdparty/utf8-cpp/core.h
vendored
Normal file
332
3rdparty/utf8-cpp/core.h
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
|
||||
// You may need to change them to match your system.
|
||||
// These typedefs have the same names as ones from cstdint, or boost/cstdint
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
|
||||
// Helper code - not intended to be directly called by the library users. May be changed at any time
|
||||
namespace internal
|
||||
{
|
||||
// Unicode constants
|
||||
// Leading (high) surrogates: 0xd800 - 0xdbff
|
||||
// Trailing (low) surrogates: 0xdc00 - 0xdfff
|
||||
const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
|
||||
const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
|
||||
const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
|
||||
const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
|
||||
const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
|
||||
const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
|
||||
|
||||
// Maximum valid value for a Unicode code point
|
||||
const uint32_t CODE_POINT_MAX = 0x0010ffffu;
|
||||
|
||||
template<typename octet_type>
|
||||
inline uint8_t mask8(octet_type oc)
|
||||
{
|
||||
return static_cast<uint8_t>(0xff & oc);
|
||||
}
|
||||
template<typename u16_type>
|
||||
inline uint16_t mask16(u16_type oc)
|
||||
{
|
||||
return static_cast<uint16_t>(0xffff & oc);
|
||||
}
|
||||
template<typename octet_type>
|
||||
inline bool is_trail(octet_type oc)
|
||||
{
|
||||
return ((utf8::internal::mask8(oc) >> 6) == 0x2);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_lead_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_trail_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u32>
|
||||
inline bool is_code_point_valid(u32 cp)
|
||||
{
|
||||
return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline typename std::iterator_traits<octet_iterator>::difference_type
|
||||
sequence_length(octet_iterator lead_it)
|
||||
{
|
||||
uint8_t lead = utf8::internal::mask8(*lead_it);
|
||||
if (lead < 0x80)
|
||||
return 1;
|
||||
else if ((lead >> 5) == 0x6)
|
||||
return 2;
|
||||
else if ((lead >> 4) == 0xe)
|
||||
return 3;
|
||||
else if ((lead >> 3) == 0x1e)
|
||||
return 4;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename octet_difference_type>
|
||||
inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
|
||||
{
|
||||
if (cp < 0x80) {
|
||||
if (length != 1)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x800) {
|
||||
if (length != 2)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x10000) {
|
||||
if (length != 3)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
|
||||
|
||||
/// Helper for get_sequence_x
|
||||
template <typename octet_iterator>
|
||||
utf_error increase_safely(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
if (++it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
if (!utf8::internal::is_trail(*it))
|
||||
return INCOMPLETE_SEQUENCE;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}
|
||||
|
||||
/// get_sequence_x functions decode utf-8 sequences of the length x
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
// Save the original value of it so we can go back in case of failure
|
||||
// Of course, it does not make much sense with i.e. stream iterators
|
||||
octet_iterator original_it = it;
|
||||
|
||||
uint32_t cp = 0;
|
||||
// Determine the sequence length based on the lead octet
|
||||
typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
|
||||
const octet_difference_type length = utf8::internal::sequence_length(it);
|
||||
|
||||
// Get trail octets and calculate the code point
|
||||
utf_error err = UTF8_OK;
|
||||
switch (length) {
|
||||
case 0:
|
||||
return INVALID_LEAD;
|
||||
case 1:
|
||||
err = utf8::internal::get_sequence_1(it, end, cp);
|
||||
break;
|
||||
case 2:
|
||||
err = utf8::internal::get_sequence_2(it, end, cp);
|
||||
break;
|
||||
case 3:
|
||||
err = utf8::internal::get_sequence_3(it, end, cp);
|
||||
break;
|
||||
case 4:
|
||||
err = utf8::internal::get_sequence_4(it, end, cp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err == UTF8_OK) {
|
||||
// Decoding succeeded. Now, security checks...
|
||||
if (utf8::internal::is_code_point_valid(cp)) {
|
||||
if (!utf8::internal::is_overlong_sequence(cp, length)){
|
||||
// Passed! Return here.
|
||||
code_point = cp;
|
||||
++it;
|
||||
return UTF8_OK;
|
||||
}
|
||||
else
|
||||
err = OVERLONG_SEQUENCE;
|
||||
}
|
||||
else
|
||||
err = INVALID_CODE_POINT;
|
||||
}
|
||||
|
||||
// Failure branch - restore the original value of the iterator
|
||||
it = original_it;
|
||||
return err;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
|
||||
uint32_t ignored;
|
||||
return utf8::internal::validate_next(it, end, ignored);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
// Byte order mark
|
||||
const uint8_t bom[] = {0xef, 0xbb, 0xbf};
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator find_invalid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
octet_iterator result = start;
|
||||
while (result != end) {
|
||||
utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
|
||||
if (err_code != internal::UTF8_OK)
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool is_valid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
return (utf8::find_invalid(start, end) == end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool starts_with_bom (octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return (
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it)) == bom[2])
|
||||
);
|
||||
}
|
||||
|
||||
//Deprecated in release 2.3
|
||||
template <typename octet_iterator>
|
||||
inline bool is_bom (octet_iterator it)
|
||||
{
|
||||
return (
|
||||
(utf8::internal::mask8(*it++)) == bom[0] &&
|
||||
(utf8::internal::mask8(*it++)) == bom[1] &&
|
||||
(utf8::internal::mask8(*it)) == bom[2]
|
||||
);
|
||||
}
|
||||
} // namespace utf8
|
||||
|
||||
#endif // header guard
|
||||
|
||||
|
||||
1
3rdparty/utfcpp
vendored
1
3rdparty/utfcpp
vendored
Submodule 3rdparty/utfcpp deleted from df857efc5b
2
AUTHORS
2
AUTHORS
@@ -16,8 +16,6 @@ Mathias Panzenböck <grosser.meister.morti@gmx.net>
|
||||
Mod, S3M, IT and XM metadata implementations
|
||||
Damien Plisson <damien78@audirvana.com>
|
||||
DSDIFF metadata implementation
|
||||
Urs Fleisch <ufleisch@users.sourceforge.net>
|
||||
Bug fixes, maintainer.
|
||||
|
||||
Please send all patches and questions to taglib-devel@kde.org rather than to
|
||||
individual developers!
|
||||
|
||||
132
CMakeLists.txt
132
CMakeLists.txt
@@ -1,14 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
|
||||
|
||||
project(taglib)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
cmake_policy(SET CMP0022 OLD)
|
||||
endif()
|
||||
|
||||
include(CTest)
|
||||
include(FeatureSummary)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
if(DEFINED ENABLE_STATIC)
|
||||
message(FATAL_ERROR "This option is no longer available, use BUILD_SHARED_LIBS instead")
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
if(APPLE)
|
||||
@@ -34,6 +36,7 @@ if(ENABLE_CCACHE)
|
||||
endif()
|
||||
|
||||
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
|
||||
option(BUILD_TESTS "Build the test suite" OFF)
|
||||
option(BUILD_EXAMPLES "Build the examples" OFF)
|
||||
option(BUILD_BINDINGS "Build the bindings" ON)
|
||||
|
||||
@@ -44,26 +47,25 @@ if(PLATFORM_WINRT)
|
||||
add_definitions(-DPLATFORM_WINRT)
|
||||
endif()
|
||||
|
||||
set(TAGLIB_INSTALL_SUFFIX "" CACHE STRING
|
||||
"Suffix added to installed files (include directory, libraries, .pc)")
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
|
||||
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
endif()
|
||||
## the following are directories where stuff will be installed to
|
||||
set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
|
||||
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries")
|
||||
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)")
|
||||
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})")
|
||||
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix")
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
if(ENABLE_STATIC_RUNTIME)
|
||||
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||
endforeach(flag_var)
|
||||
endif()
|
||||
if(MSVC AND ENABLE_STATIC_RUNTIME)
|
||||
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||
endforeach(flag_var)
|
||||
endif()
|
||||
|
||||
# Read version information from file taglib/toolkit/taglib.h into variables
|
||||
@@ -88,96 +90,57 @@ else()
|
||||
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
|
||||
endif()
|
||||
|
||||
# Major version: increase it if you break ABI compatibility.
|
||||
# Minor version: increase it if you add ABI compatible features.
|
||||
# Patch version: increase it for bug fix releases.
|
||||
set(TAGLIB_SOVERSION_MAJOR 2)
|
||||
set(TAGLIB_SOVERSION_MINOR 0)
|
||||
set(TAGLIB_SOVERSION_PATCH 0)
|
||||
# 1. If the library source code has changed at all since the last update, then increment revision.
|
||||
# 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
|
||||
# 3. If any interfaces have been added since the last public release, then increment age.
|
||||
# 4. If any interfaces have been removed since the last public release, then set age to 0.
|
||||
set(TAGLIB_SOVERSION_CURRENT 18)
|
||||
set(TAGLIB_SOVERSION_REVISION 0)
|
||||
set(TAGLIB_SOVERSION_AGE 17)
|
||||
|
||||
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
|
||||
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
|
||||
math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
|
||||
# Determine whether zlib is installed.
|
||||
option(WITH_ZLIB "Build with ZLIB" ON)
|
||||
|
||||
if(WITH_ZLIB)
|
||||
find_package("ZLIB")
|
||||
set(HAVE_ZLIB ${ZLIB_FOUND})
|
||||
if(ZLIB_FOUND)
|
||||
set(ZLIB_LIBRARIES_FLAGS -lz)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
# When linking TagLib statically, zlib has to be linked explicitly.
|
||||
set(ZLIB_INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" @ONLY)
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config")
|
||||
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 "${CMAKE_INSTALL_BINDIR}"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config.cmd")
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${BIN_INSTALL_DIR}")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_FRAMEWORK)
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
else()
|
||||
set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR})
|
||||
else()
|
||||
set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}.pc")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${LIB_INSTALL_DIR}/pkgconfig")
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_ZLIB AND ZLIB_SOURCE)
|
||||
set(HAVE_ZLIB 1)
|
||||
set(HAVE_ZLIB_SOURCE 1)
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||
|
||||
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
|
||||
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")
|
||||
|
||||
find_package(utf8cpp QUIET)
|
||||
if(NOT utf8cpp_FOUND)
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/utfcpp/CMakeLists.txt)
|
||||
add_subdirectory("3rdparty/utfcpp")
|
||||
message(STATUS "Using utfcpp from ${utf8cpp_SOURCE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"utfcpp not found. Either install package (probably utfcpp, utf8cpp, or libutfcpp-dev) "
|
||||
"or fetch the git submodule using\n"
|
||||
"git submodule update --init")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Using utfcpp ${utf8cpp_VERSION} from ${utf8cpp_CONFIG}")
|
||||
endif()
|
||||
|
||||
add_subdirectory(taglib)
|
||||
|
||||
if(BUILD_BINDINGS)
|
||||
add_subdirectory(bindings)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(CppUnit)
|
||||
if(CppUnit_FOUND)
|
||||
add_subdirectory(tests)
|
||||
else()
|
||||
message(WARNING "BUILD_TESTING requested, but CppUnit not found, skipping tests.")
|
||||
endif()
|
||||
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(BUILD_EXAMPLES)
|
||||
@@ -185,6 +148,7 @@ if(BUILD_EXAMPLES)
|
||||
endif()
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
|
||||
file(COPY doc/taglib.png DESTINATION doc)
|
||||
add_custom_target(docs doxygen)
|
||||
|
||||
# uninstall target
|
||||
@@ -193,5 +157,3 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_C
|
||||
if(NOT TARGET uninstall)
|
||||
add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
endif()
|
||||
|
||||
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
include(CheckLibraryExists)
|
||||
include(CheckTypeSize)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(TestLargeFiles)
|
||||
|
||||
# Check if the size of numeric types are suitable.
|
||||
|
||||
@@ -34,6 +36,92 @@ if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports large files.
|
||||
|
||||
if(NOT WIN32)
|
||||
test_large_files(SUPPORT_LARGE_FILES)
|
||||
if(NOT SUPPORT_LARGE_FILES)
|
||||
MESSAGE(FATAL_ERROR "TagLib requires large files support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Enable check_cxx_source_compiles() to work with Boost "header-only" libraries.
|
||||
|
||||
find_package(Boost)
|
||||
if(Boost_FOUND)
|
||||
set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${Boost_INCLUDE_DIRS}")
|
||||
endif()
|
||||
|
||||
# Determine which kind of atomic operations your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic_int x(1);
|
||||
++x;
|
||||
--x;
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_ATOMIC)
|
||||
|
||||
if(NOT HAVE_STD_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
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()
|
||||
|
||||
# Determine which kind of smart pointers your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <memory>
|
||||
int main() {
|
||||
std::shared_ptr<int> x;
|
||||
std::unique_ptr<int> y;
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_SMART_PTR)
|
||||
|
||||
# Determine which kind of byte swap functions your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
@@ -58,7 +146,7 @@ if(NOT HAVE_GCC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GLIBC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdlib>
|
||||
#include <stdlib.h>
|
||||
int main() {
|
||||
_byteswap_ushort(0);
|
||||
_byteswap_ulong(0);
|
||||
@@ -93,6 +181,32 @@ if(NOT HAVE_GCC_BYTESWAP)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports some safer version of vsprintf.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
int main() {
|
||||
char buf[20];
|
||||
va_list args;
|
||||
vsnprintf(buf, 20, \"%d\", args);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_VSNPRINTF)
|
||||
|
||||
if(NOT HAVE_VSNPRINTF)
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
int main() {
|
||||
char buf[20];
|
||||
va_list args;
|
||||
vsprintf_s(buf, \"%d\", args);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_VSPRINTF_S)
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports ISO _strdup.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
@@ -103,7 +217,28 @@ check_cxx_source_compiles("
|
||||
}
|
||||
" HAVE_ISO_STRDUP)
|
||||
|
||||
# Determine whether zlib is installed.
|
||||
|
||||
if(NOT ZLIB_SOURCE)
|
||||
find_package(ZLIB)
|
||||
if(ZLIB_FOUND)
|
||||
set(HAVE_ZLIB 1)
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether CppUnit is installed.
|
||||
|
||||
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
|
||||
find_package(CppUnit)
|
||||
if(NOT CppUnit_FOUND)
|
||||
message(STATUS "CppUnit not found, disabling tests.")
|
||||
set(BUILD_TESTS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Detect WinRT mode
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(PLATFORM_WINRT 1)
|
||||
set(PLATFORM_WINRT 1)
|
||||
endif()
|
||||
|
||||
264
Doxyfile.cmake
264
Doxyfile.cmake
@@ -1,79 +1,37 @@
|
||||
# Doxyfile 1.9.1
|
||||
# Doxyfile 1.3.4
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = TagLib
|
||||
PROJECT_NUMBER = ${TAGLIB_LIB_VERSION_STRING}
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_LOGO = @CMAKE_SOURCE_DIR@/doc/taglib.svg
|
||||
OUTPUT_DIRECTORY = doc
|
||||
CREATE_SUBDIRS = NO
|
||||
ALLOW_UNICODE_NAMES = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
OUTPUT_TEXT_DIRECTION = None
|
||||
USE_WINDOWS_ENCODING = NO
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ABBREVIATE_BRIEF = "The $name class" \
|
||||
"The $name widget" \
|
||||
"The $name file" \
|
||||
is \
|
||||
provides \
|
||||
specifies \
|
||||
contains \
|
||||
represents \
|
||||
a \
|
||||
an \
|
||||
the
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
FULL_PATH_NAMES = NO
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_INC_PATH =
|
||||
STRIP_FROM_PATH =
|
||||
SHORT_NAMES = NO
|
||||
JAVADOC_AUTOBRIEF = NO
|
||||
JAVADOC_BANNER = NO
|
||||
QT_AUTOBRIEF = NO
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
PYTHON_DOCSTRING = YES
|
||||
DETAILS_AT_TOP = NO
|
||||
INHERIT_DOCS = YES
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
TAB_SIZE = 4
|
||||
ALIASES =
|
||||
ALIASES =
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
OPTIMIZE_FOR_FORTRAN = NO
|
||||
OPTIMIZE_OUTPUT_VHDL = NO
|
||||
OPTIMIZE_OUTPUT_SLICE = NO
|
||||
EXTENSION_MAPPING =
|
||||
MARKDOWN_SUPPORT = YES
|
||||
TOC_INCLUDE_HEADINGS = 5
|
||||
AUTOLINK_SUPPORT = YES
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
CPP_CLI_SUPPORT = NO
|
||||
SIP_SUPPORT = NO
|
||||
IDL_PROPERTY_SUPPORT = YES
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
GROUP_NESTED_COMPOUNDS = NO
|
||||
SUBGROUPING = YES
|
||||
INLINE_GROUPED_CLASSES = NO
|
||||
INLINE_SIMPLE_STRUCTS = NO
|
||||
TYPEDEF_HIDES_STRUCT = NO
|
||||
LOOKUP_CACHE_SIZE = 0
|
||||
NUM_PROC_THREADS = 1
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_PRIVATE = NO
|
||||
EXTRACT_PRIV_VIRTUAL = NO
|
||||
EXTRACT_PACKAGE = NO
|
||||
EXTRACT_STATIC = NO
|
||||
EXTRACT_LOCAL_CLASSES = NO
|
||||
EXTRACT_LOCAL_METHODS = NO
|
||||
EXTRACT_ANON_NSPACES = NO
|
||||
RESOLVE_UNNAMED_PARAMS = YES
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
@@ -81,268 +39,172 @@ HIDE_IN_BODY_DOCS = NO
|
||||
INTERNAL_DOCS = YES
|
||||
CASE_SENSE_NAMES = YES
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
HIDE_COMPOUND_REFERENCE= NO
|
||||
SHOW_INCLUDE_FILES = YES
|
||||
SHOW_GROUPED_MEMB_INC = NO
|
||||
FORCE_LOCAL_INCLUDES = NO
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_BRIEF_DOCS = NO
|
||||
SORT_MEMBERS_CTORS_1ST = NO
|
||||
SORT_GROUP_NAMES = NO
|
||||
SORT_BY_SCOPE_NAME = NO
|
||||
STRICT_PROTO_MATCHING = NO
|
||||
GENERATE_TODOLIST = NO
|
||||
GENERATE_TESTLIST = NO
|
||||
GENERATE_BUGLIST = NO
|
||||
GENERATE_DEPRECATEDLIST= YES
|
||||
ENABLED_SECTIONS =
|
||||
GENERATE_DEPRECATEDLIST= NO
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
SHOW_USED_FILES = YES
|
||||
SHOW_FILES = YES
|
||||
SHOW_NAMESPACES = YES
|
||||
FILE_VERSION_FILTER =
|
||||
LAYOUT_FILE =
|
||||
CITE_BIB_FILES =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to warning and progress messages
|
||||
# configuration options related to warning and progress messages
|
||||
#---------------------------------------------------------------------------
|
||||
QUIET = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_NO_PARAMDOC = NO
|
||||
WARN_AS_ERROR = NO
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
WARN_LOGFILE =
|
||||
WARN_LOGFILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the input files
|
||||
# configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = @CMAKE_SOURCE_DIR@/taglib
|
||||
INPUT_ENCODING = UTF-8
|
||||
FILE_PATTERNS = *.h \
|
||||
*.hh \
|
||||
*.H \
|
||||
*.dox
|
||||
*.H
|
||||
RECURSIVE = YES
|
||||
EXCLUDE =
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS =
|
||||
EXCLUDE_SYMBOLS =
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS = *
|
||||
EXCLUDE_PATTERNS =
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS =
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_PATTERNS =
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
FILTER_SOURCE_PATTERNS =
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
# configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
SOURCE_BROWSER = NO
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCES_RELATION = YES
|
||||
REFERENCES_LINK_SOURCE = YES
|
||||
SOURCE_TOOLTIPS = YES
|
||||
USE_HTAGS = NO
|
||||
VERBATIM_HEADERS = YES
|
||||
CLANG_ASSISTED_PARSING = NO
|
||||
CLANG_ADD_INC_PATHS = YES
|
||||
CLANG_OPTIONS =
|
||||
CLANG_DATABASE_PATH =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the alphabetical class index
|
||||
# configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
ALPHABETICAL_INDEX = YES
|
||||
IGNORE_PREFIX =
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
IGNORE_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the HTML output
|
||||
# configuration options related to the HTML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_HTML = YES
|
||||
HTML_OUTPUT = html
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER = @CMAKE_SOURCE_DIR@/doc/api-header.html
|
||||
HTML_FOOTER = @CMAKE_SOURCE_DIR@/doc/api-footer.html
|
||||
HTML_STYLESHEET =
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
HTML_EXTRA_FILES =
|
||||
HTML_COLORSTYLE_HUE = 220
|
||||
HTML_COLORSTYLE_SAT = 100
|
||||
HTML_COLORSTYLE_GAMMA = 80
|
||||
HTML_TIMESTAMP = NO
|
||||
HTML_DYNAMIC_MENUS = YES
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
HTML_INDEX_NUM_ENTRIES = 100
|
||||
GENERATE_DOCSET = NO
|
||||
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||
DOCSET_BUNDLE_ID = org.doxygen.Project
|
||||
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
|
||||
DOCSET_PUBLISHER_NAME = Publisher
|
||||
HTML_STYLESHEET = @CMAKE_SOURCE_DIR@/doc/taglib-api.css
|
||||
HTML_ALIGN_MEMBERS = YES
|
||||
GENERATE_HTMLHELP = NO
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
GENERATE_CHI = NO
|
||||
CHM_INDEX_ENCODING =
|
||||
BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
GENERATE_QHP = NO
|
||||
QCH_FILE =
|
||||
QHP_NAMESPACE = org.doxygen.Project
|
||||
QHP_VIRTUAL_FOLDER = doc
|
||||
QHP_CUST_FILTER_NAME =
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
QHG_LOCATION =
|
||||
GENERATE_ECLIPSEHELP = NO
|
||||
ECLIPSE_DOC_ID = org.doxygen.Project
|
||||
DISABLE_INDEX = NO
|
||||
GENERATE_TREEVIEW = NO
|
||||
DISABLE_INDEX = YES
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
GENERATE_TREEVIEW = NO
|
||||
TREEVIEW_WIDTH = 250
|
||||
EXT_LINKS_IN_WINDOW = NO
|
||||
HTML_FORMULA_FORMAT = png
|
||||
FORMULA_FONTSIZE = 10
|
||||
FORMULA_TRANSPARENT = YES
|
||||
FORMULA_MACROFILE =
|
||||
USE_MATHJAX = NO
|
||||
MATHJAX_FORMAT = HTML-CSS
|
||||
MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
|
||||
MATHJAX_EXTENSIONS =
|
||||
MATHJAX_CODEFILE =
|
||||
SEARCHENGINE = NO
|
||||
SERVER_BASED_SEARCH = NO
|
||||
EXTERNAL_SEARCH = NO
|
||||
SEARCHENGINE_URL =
|
||||
SEARCHDATA_FILE = searchdata.xml
|
||||
EXTERNAL_SEARCH_ID =
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the LaTeX output
|
||||
# configuration options related to the LaTeX output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_LATEX = NO
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
LATEX_MAKEINDEX_CMD = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = letter
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
LATEX_FOOTER =
|
||||
LATEX_EXTRA_STYLESHEET =
|
||||
LATEX_EXTRA_FILES =
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
PDF_HYPERLINKS = YES
|
||||
USE_PDFLATEX = YES
|
||||
LATEX_BATCHMODE = NO
|
||||
LATEX_HIDE_INDICES = NO
|
||||
LATEX_SOURCE_CODE = NO
|
||||
LATEX_BIB_STYLE = plain
|
||||
LATEX_TIMESTAMP = NO
|
||||
LATEX_EMOJI_DIRECTORY =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the RTF output
|
||||
# configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_RTF = NO
|
||||
RTF_OUTPUT = rtf
|
||||
COMPACT_RTF = NO
|
||||
RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
RTF_SOURCE_CODE = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the man page output
|
||||
# configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_MAN = NO
|
||||
MAN_OUTPUT = man
|
||||
MAN_EXTENSION = .3
|
||||
MAN_SUBDIR =
|
||||
MAN_LINKS = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the XML output
|
||||
# configuration options related to the XML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_XML = NO
|
||||
XML_OUTPUT = xml
|
||||
XML_PROGRAMLISTING = YES
|
||||
XML_NS_MEMB_FILE_SCOPE = NO
|
||||
XML_SCHEMA =
|
||||
XML_DTD =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the DOCBOOK output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_DOCBOOK = NO
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
# configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the Perl module output
|
||||
# configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_PERLMOD = NO
|
||||
PERLMOD_LATEX = NO
|
||||
PERLMOD_PRETTY = YES
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = NO
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED = DO_NOT_DOCUMENT \
|
||||
DOXYGEN
|
||||
EXPAND_AS_DEFINED =
|
||||
DOXYGEN \
|
||||
WITH_MP4 \
|
||||
WITH_ASF
|
||||
EXPAND_AS_DEFINED =
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to external references
|
||||
# Configuration::addtions related to external references
|
||||
#---------------------------------------------------------------------------
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE =
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE =
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_PAGES = YES
|
||||
PERL_PATH = /usr/bin/perl
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
CLASS_DIAGRAMS = YES
|
||||
DIA_PATH =
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = YES
|
||||
DOT_NUM_THREADS = 0
|
||||
DOT_FONTNAME = Helvetica
|
||||
DOT_FONTSIZE = 10
|
||||
DOT_FONTPATH =
|
||||
CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = YES
|
||||
GROUP_GRAPHS = YES
|
||||
UML_LOOK = NO
|
||||
UML_LIMIT_NUM_FIELDS = 10
|
||||
DOT_UML_DETAILS = NO
|
||||
DOT_WRAP_THRESHOLD = 17
|
||||
TEMPLATE_RELATIONS = YES
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
CALL_GRAPH = NO
|
||||
CALLER_GRAPH = NO
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DIRECTORY_GRAPH = YES
|
||||
DOT_IMAGE_FORMAT = svg
|
||||
INTERACTIVE_SVG = NO
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
MSCFILE_DIRS =
|
||||
DIAFILE_DIRS =
|
||||
PLANTUML_JAR_PATH =
|
||||
PLANTUML_CFG_FILE =
|
||||
PLANTUML_INCLUDE_PATH =
|
||||
DOT_GRAPH_MAX_NODES = 100
|
||||
DOT_IMAGE_FORMAT = png
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
MAX_DOT_GRAPH_WIDTH = 1024
|
||||
MAX_DOT_GRAPH_HEIGHT = 1024
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
DOT_TRANSPARENT = NO
|
||||
DOT_MULTI_TARGETS = NO
|
||||
GENERATE_LEGEND = YES
|
||||
DOT_CLEANUP = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::addtions related to the search engine
|
||||
#---------------------------------------------------------------------------
|
||||
SEARCHENGINE = NO
|
||||
|
||||
527
INSTALL.md
527
INSTALL.md
@@ -1,4 +1,5 @@
|
||||
# TagLib Installation
|
||||
TagLib Installation
|
||||
===================
|
||||
|
||||
TagLib uses the CMake build system. As a user, you will most likely want to
|
||||
build TagLib in release mode and install it into a system-wide location.
|
||||
@@ -12,429 +13,163 @@ In order to build the included examples, use the `BUILD_EXAMPLES` option:
|
||||
|
||||
cmake -DBUILD_EXAMPLES=ON [...]
|
||||
|
||||
If you want to build TagLib without ZLib, you can use
|
||||
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
|
||||
running CMake.
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DWITH_ZLIB=OFF .
|
||||
make
|
||||
sudo make install
|
||||
Mac OS X
|
||||
--------
|
||||
|
||||
See [cmake(1)](https://cmake.org/cmake/help/latest/manual/cmake.1.html) for
|
||||
generic help on running CMake.
|
||||
|
||||
## Build Options
|
||||
|
||||
These are the most important build options. For details, have a look into the
|
||||
CMakeLists.txt file.
|
||||
|
||||
| Option | Description |
|
||||
| ----------------------- | -------------------------------------------------- |
|
||||
| `BUILD_SHARED_LIBS` | Build shared libraries |
|
||||
| `CMAKE_BUILD_TYPE` | Debug, Release, RelWithDebInfo, MinSizeRel |
|
||||
| `BUILD_EXAMPLES` | Build examples |
|
||||
| `BUILD_BINDINGS` | Build C bindings |
|
||||
| `BUILD_TESTING` | Build unit tests |
|
||||
| `TRACE_IN_RELEASE` | Enable debug output in release builds |
|
||||
| `WITH_ZLIB` | Whether to build with ZLib (default ON) |
|
||||
| `ZLIB_ROOT` | Where to find ZLib's root directory |
|
||||
| `ZLIB_INCLUDE_DIR` | Where to find ZLib's include directory |
|
||||
| `ZLIB_LIBRARY` | Where to find ZLib's library |
|
||||
| `CMAKE_INSTALL_PREFIX` | Where to install Taglib |
|
||||
| `TAGLIB_INSTALL_SUFFIX` | Suffix added to installed libraries, includes, ... |
|
||||
| `ENABLE_STATIC_RUNTIME` | Link with MSVC runtime statically |
|
||||
| `BUILD_FRAMEWORK` | Build a macOS framework |
|
||||
|
||||
If you want to install TagLib 2 alongside TagLib 1, you can use
|
||||
`-DTAGLIB_INSTALL_SUFFIX=-2` and make sure that `BUILD_EXAMPLES` is not `ON`
|
||||
for both versions. The installed files will then include bin/taglib-2-config,
|
||||
include/taglib-2, cmake/taglib-2, pkgconfig/taglib-2.pc,
|
||||
pkgconfig/taglib_c-2.pc and the libraries have a suffix "-2".
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
A required dependency is [utf8cpp](https://github.com/nemtrif/utfcpp). You can
|
||||
install the corresponding package (libutfcpp-dev on Ubuntu, utf8cpp in Homebrew,
|
||||
utfcpp in vcpkg) or fetch the Git submodule with `git submodule update --init`.
|
||||
|
||||
Optional dependencies are
|
||||
- [zlib](https://www.zlib.net/): You can disable it with `-DWITH_ZLIB=OFF`,
|
||||
build and install it from the sources or use a package (zlib1g-dev on Ubuntu,
|
||||
zlib in vcpkg). It is needed for compressed ID3v2 frames.
|
||||
- [CppUnit](https://wiki.documentfoundation.org/Cppunit): Is required for unit
|
||||
tests, which are disabled by default. If you enable them with
|
||||
`-DBUILD_TESTING=ON`, you can build and install it from the sources or use a
|
||||
package (libcppunit-dev on Ubuntu, cppunit in Homebrew, cppunit in vcpkg).
|
||||
If the unit tests are enabled, you can run them with your build tool
|
||||
(`make check`, `ninja check`) or via CMake
|
||||
`cmake --build /path/to/build-dir --target check`.
|
||||
|
||||
## UNIX (Including Linux, BSD and macOS)
|
||||
|
||||
#### Building TagLib
|
||||
|
||||
On Linux, you can install the dependencies using the package manager of your
|
||||
distribution. On macOS with Homebrew, you can use `brew install cppunit utf8cpp`
|
||||
to install the dependencies.
|
||||
|
||||
```
|
||||
# Adapt these environment variables to your directories
|
||||
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
|
||||
TAGLIB_DST_DIR=$HOME/projects/taglib/src/build
|
||||
cd $TAGLIB_SRC_DIR
|
||||
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build $TAGLIB_DST_DIR --config Release
|
||||
cmake --build $TAGLIB_DST_DIR --config Release --target check
|
||||
|
||||
# Install to ~/pkg folder
|
||||
cmake --install $TAGLIB_DST_DIR --config Release --prefix $HOME/pkg --strip
|
||||
|
||||
# Run example from installed package
|
||||
LD_LIBRARY_PATH=$HOME/pkg/lib $HOME/pkg/bin/tagreader /path/to/audio-file
|
||||
```
|
||||
|
||||
#### Building a Project Using TagLib
|
||||
|
||||
As an example for an external application using TagLib, we create a folder
|
||||
taglib-client and copy the file tagreader.cpp from the examples folder
|
||||
of the TagLib sources into it. We then add this simple CMakeLists.txt.
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.5.0)
|
||||
project(taglib-client)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
find_package(ZLIB)
|
||||
find_package(TagLib 2.0.0 REQUIRED)
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader TagLib::tag)
|
||||
```
|
||||
|
||||
To build into a folder `build` inside this directory, just run
|
||||
|
||||
```
|
||||
# Set this to the path where TagLib is installed
|
||||
TAGLIB_PREFIX=$HOME/pkg
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX
|
||||
cmake --build build --config Release
|
||||
|
||||
build/tagreader /path/to/audio-file
|
||||
```
|
||||
|
||||
If TagLib is installed in a standard location (e.g. /usr, /usr/local), its CMake
|
||||
configuration is found without setting CMAKE_INSTALL_PREFIX (or alternatives
|
||||
like CMAKE_PREFIX_PATH, CMAKE_SYSTEM_PREFIX_PATH).
|
||||
|
||||
To use the C-bindings with tagreader_c.c, you can change the last two lines in
|
||||
CMakeLists.txt to
|
||||
|
||||
```
|
||||
add_executable(tagreader_c tagreader_c.c)
|
||||
target_link_libraries(tagreader_c TagLib::tag_c)
|
||||
```
|
||||
|
||||
#### Building a Project Using pkg-config
|
||||
|
||||
Before version 2.0, TagLib did not export CMake configuration, therefore
|
||||
`pkg-config` could be used. This is still possible.
|
||||
|
||||
Note, however, that `pkg-config` makes it more difficult to use packages which
|
||||
are not installed in a standard location. You can point `pkg-config` to search
|
||||
in non-standard locations for .pc-files, but they still contain the install
|
||||
locations defined when building TagLib. Since we did not give a
|
||||
`CMAKE_INSTALL_PREFIX` in the example above, the default `/usr/local/` is used.
|
||||
|
||||
```
|
||||
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --libs --cflags taglib
|
||||
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
|
||||
```
|
||||
|
||||
The following examples use the same build example with additional CMake
|
||||
parameters affecting the installation location.
|
||||
|
||||
- Using the default prefix `-DCMAKE_INSTALL_PREFIX=/usr/local`:
|
||||
```
|
||||
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
|
||||
```
|
||||
- Using an absolute prefix `-DCMAKE_INSTALL_PREFIX=/usr`:
|
||||
```
|
||||
-I/usr/include/taglib -ltag -lz
|
||||
```
|
||||
- Using absolute lib and include directories
|
||||
`-DCMAKE_INSTALL_LIBDIR=/abs-lib -DCMAKE_INSTALL_INCLUDEDIR=/abs-include -DCMAKE_INSTALL_PREFIX=/usr`:
|
||||
```
|
||||
-I/abs-include -I/abs-include/taglib -L/abs-lib -ltag -lz
|
||||
```
|
||||
- Using relative lib and include directories
|
||||
`-DCMAKE_INSTALL_LIBDIR=rel-lib -DCMAKE_INSTALL_INCLUDEDIR=rel-include -DCMAKE_INSTALL_PREFIX=/usr`:
|
||||
```
|
||||
-I/usr/rel-include -I/usr/rel-include/taglib -L/usr/rel-lib -ltag -lz
|
||||
```
|
||||
This is the output of
|
||||
```
|
||||
PKG_CONFIG_PATH=$HOME/pkg/rel-lib/pkgconfig pkg-config --libs --cflags taglib
|
||||
```
|
||||
You could add `--define-prefix` to the `pkg-config` arguments to have the
|
||||
effective location `$HOME/pkg/` instead of `/usr/` in the output.
|
||||
|
||||
Therefore, the correct paths for our example package would be given when using
|
||||
the `--define-prefix` feature.
|
||||
|
||||
```
|
||||
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --define-prefix --libs --cflags taglib
|
||||
```
|
||||
|
||||
You can use pkg-config from CMake, however, relocation with `--define-prefix`
|
||||
is not supported.
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.6.0)
|
||||
project(taglib-client)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib)
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader PkgConfig::TAGLIB)
|
||||
```
|
||||
|
||||
#### Framework on macOS
|
||||
|
||||
On macOS, you might want to build a framework that can be easily integrated
|
||||
On Mac OS X, you might want to build a framework that can be easily integrated
|
||||
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
|
||||
TagLib as a framework. For example, the following command can be used to build
|
||||
a framework with macOS 10.10 as the deployment target:
|
||||
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
|
||||
|
||||
mkdir build; cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_TESTING=OFF \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_FRAMEWORK=ON \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
|
||||
make
|
||||
-DCMAKE_C_COMPILER=/usr/bin/gcc-4.0 \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
|
||||
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="ppc;i386;x86_64"
|
||||
|
||||
For a 10.10 static library, use:
|
||||
For a 10.6 Snow Leopard static library with both 32-bit and 64-bit code, use:
|
||||
|
||||
mkdir build; cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_TESTING=OFF \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
|
||||
make
|
||||
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
|
||||
|
||||
After `make`, and `make install`, add `libtag.` to your XCode project, and add
|
||||
the include folder to the project's User Header Search Paths.
|
||||
|
||||
## Windows
|
||||
Windows
|
||||
-------
|
||||
|
||||
### Using Visual Studio Build Tools
|
||||
It's Windows ... Systems vary!
|
||||
This means you need to adjust things to suit your system, especially paths.
|
||||
|
||||
For this example, we assume that you have the Visual Studio Build Tools 2022
|
||||
installed. Additionally, you need [cmake](https://cmake.org/), which can be
|
||||
installed for example using [scoop](https://scoop.sh/) with
|
||||
`scoop install cmake`.
|
||||
Tested with:
|
||||
* Microsoft Visual Studio 2010, 2015, 2017
|
||||
* Microsoft C++ Build Tools 2015, 2017 (standalone packages not requiring Visual Studio)
|
||||
* Gcc by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
|
||||
* MinGW32-4.8.0
|
||||
|
||||
#### Building TagLib (MSVC)
|
||||
Requirements:
|
||||
* Tool chain, build environment, whatever ya want to call it ...
|
||||
Installed and working.
|
||||
* CMake program. (Available at: www.cmake.org)
|
||||
Installed and working.
|
||||
|
||||
You can build and install the dependencies
|
||||
[utf8cpp](https://github.com/nemtrif/utfcpp) and optionally
|
||||
[zlib](https://www.zlib.net/) and
|
||||
[CppUnit](https://wiki.documentfoundation.org/Cppunit) yourself, but it is
|
||||
probably easier using a package manager such as [vcpkg](https://vcpkg.io/),
|
||||
which can be installed using `scoop install vcpkg` and then install the
|
||||
dependencies using `vcpkg install utfcpp zlib cppunit`.
|
||||
Optional:
|
||||
* Zlib library.
|
||||
Available in some tool chains, not all.
|
||||
Search the web, take your choice.
|
||||
|
||||
Now you can build TagLib from PowerShell.
|
||||
Useful configuration options used with CMake (GUI and/or command line):
|
||||
Any of the `ZLIB_` variables may be used at the command line, `ZLIB_ROOT` is only
|
||||
available on the command line.
|
||||
|
||||
| option | description |
|
||||
---------------------| ------------|
|
||||
`ZLIB_ROOT=` | Where to find ZLib's root directory. Assumes parent of: `\include` and `\lib.`|
|
||||
`ZLIB_INCLUDE_DIR=` | Where to find ZLib's Include directory.|
|
||||
`ZLIB_LIBRARY=` | Where to find ZLib's Library.
|
||||
`ZLIB_SOURCE=` | Where to find ZLib's Source Code. Alternative to `ZLIB_INCLUDE_DIR` and `ZLIB_LIBRARY`.
|
||||
`CMAKE_INSTALL_PREFIX=` | Where to install Taglib. |
|
||||
`CMAKE_BUILD_TYPE=` | Release, Debug, etc ... (Not available in MSVC) |
|
||||
|
||||
```
|
||||
# Adapt these environment variables to your directories
|
||||
$env:TAGLIB_SRC_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/taglib"
|
||||
$env:TAGLIB_DST_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/msvs_vcpkg_build"
|
||||
cd $env:TAGLIB_SRC_DIR
|
||||
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON `
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
|
||||
-G "Visual Studio 17 2022"
|
||||
cmake --build $env:TAGLIB_DST_DIR --config Release
|
||||
The easiest way is at the command prompt (Visual C++ command prompt for MSVS users – batch file and/or shortcuts are your friends).
|
||||
|
||||
# Add directories containing DLL dependencies to path
|
||||
$env:Path += -join(";$env:TAGLIB_DST_DIR\taglib\Release;",
|
||||
"$env:TAGLIB_DST_DIR\bindings\c\Release;",
|
||||
"$env:VCPKG_ROOT\packages\cppunit_x64-windows\bin;",
|
||||
"$env:VCPKG_ROOT\packages\utfcpp_x64-windows\bin;",
|
||||
"$env:VCPKG_ROOT\packages\zlib_x64-windows\bin")
|
||||
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
|
||||
1. **Build the Makefiles:**
|
||||
|
||||
# Install to \pkg folder on current drive
|
||||
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg --strip
|
||||
Replace "GENERATOR" with your needs.
|
||||
* For MSVS: `Visual Studio XX YYYY`, e.g. `Visual Studio 14 2015`.
|
||||
|
||||
**Note**: As Visual Studio 2017 supports CMake, you can skip this step and open the taglib
|
||||
folder in VS instead.
|
||||
* For MinGW: `MinGW Makefiles`
|
||||
|
||||
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
|
||||
|
||||
# Static library
|
||||
$env:TAGLIB_DST_DIR = "C:/Users/fle/projects/taglib/src/msvs_vcpkg_static_build"
|
||||
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=OFF -DVISIBILITY_HIDDEN=ON `
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
|
||||
-G "Visual Studio 17 2022"
|
||||
cmake --build $env:TAGLIB_DST_DIR --config Release
|
||||
Or use the CMake GUI:
|
||||
1. Open CMake GUI.
|
||||
2. Set paths: *Where is the source code* and *Where to build the binaries*.
|
||||
|
||||
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
|
||||
In the 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.
|
||||
8. 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`
|
||||
9. Select: Generate
|
||||
|
||||
# Install to \pkg_static folder on current drive
|
||||
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg_static --strip
|
||||
```
|
||||
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
|
||||
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)
|
||||
* MinGW:
|
||||
|
||||
C:\GitRoot\taglib> gmake
|
||||
|
||||
OR (Depending on MinGW install)
|
||||
|
||||
C:\GitRoot\taglib> mingw32-make
|
||||
|
||||
|
||||
|
||||
3. **Install the project**
|
||||
|
||||
(Change `install` to `uninstall` to uninstall the project)
|
||||
* MSVS:
|
||||
|
||||
C:\GitRoot\taglib> msbuild install.vcxproj
|
||||
OR (Depending on MSVC version or personal choice)
|
||||
|
||||
C:\GitRoot\taglib> devenv install.vcxproj
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open project.
|
||||
2. Open Solution Explorer.
|
||||
3. Right Click: INSTALL
|
||||
4. Select: Project Only
|
||||
5. Select: Build Only INSTALL
|
||||
* MinGW:
|
||||
|
||||
C:\GitRoot\taglib> gmake install
|
||||
OR (Depending on MinGW install)
|
||||
|
||||
C:\GitRoot\taglib> mingw32-make install
|
||||
|
||||
|
||||
To build a static library, set the following two options with CMake:
|
||||
|
||||
-DBUILD_SHARED_LIBS=OFF -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.
|
||||
|
||||
#### Building a Project Using TagLib (MSVC)
|
||||
Unit Tests
|
||||
----------
|
||||
|
||||
As an example for an external application using TagLib, we create a folder
|
||||
taglib-client and copy the file tagreader.cpp from the examples folder
|
||||
of the TagLib sources into it. We then add this simple CMakeLists.txt.
|
||||
If you want to run the test suite to make sure TagLib works properly on your
|
||||
system, you need to have cppunit installed. To build the tests, include
|
||||
the option `-DBUILD_TESTS=on` when running cmake.
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.5.0)
|
||||
project(taglib-client)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
find_package(ZLIB)
|
||||
find_package(TagLib 2.0.0 REQUIRED)
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader TagLib::tag)
|
||||
```
|
||||
The test suite has a custom target in the build system, so you can run
|
||||
the tests using make:
|
||||
|
||||
To build into a folder build inside this directory, just run
|
||||
|
||||
```
|
||||
# Set this to the path where TagLib is installed
|
||||
$env:TAGLIB_PREFIX = "/pkg"
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$env:TAGLIB_PREFIX" `
|
||||
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
|
||||
-G "Visual Studio 17 2022"
|
||||
cmake --build build --config Release
|
||||
|
||||
# Add directories containing DLL dependencies to path
|
||||
$env:Path += ";$env:TAGLIB_PREFIX\bin;$env:VCPKG_ROOT\packages\zlib_x64-windows\bin"
|
||||
build\Release\tagreader /path/to/audio-file
|
||||
```
|
||||
|
||||
To use the C-bindings with tagreader_c.c, you can change the last two lines in
|
||||
CMakeLists.txt to
|
||||
|
||||
```
|
||||
add_executable(tagreader_c tagreader_c.c)
|
||||
target_link_libraries(tagreader_c TagLib::tag_c)
|
||||
```
|
||||
|
||||
If you link against a static TagLib, you have to adapt the installation path
|
||||
(e.g. "/pkg_static") and add the following line to CMakeLists.txt
|
||||
|
||||
```
|
||||
target_compile_definitions(tagreader_c PRIVATE TAGLIB_STATIC)
|
||||
```
|
||||
|
||||
### Using MSYS2
|
||||
|
||||
#### Building TagLib (MSYS2)
|
||||
|
||||
To build TagLib using Clang from MSYS, install [msys2](https://www.msys2.org/),
|
||||
then start the MSYS CLANG64 shell and install the dependencies as they are
|
||||
specified in the [MSYS recipe for TagLib](
|
||||
https://packages.msys2.org/package/mingw-w64-clang-x86_64-taglib?repo=clang64).
|
||||
|
||||
```
|
||||
pacman -Suy
|
||||
pacman -S mingw-w64-clang-x86_64-gcc-libs mingw-w64-clang-x86_64-zlib \
|
||||
mingw-w64-clang-x86_64-cc mingw-w64-clang-x86_64-cmake \
|
||||
mingw-w64-clang-x86_64-cppunit mingw-w64-clang-x86_64-ninja
|
||||
```
|
||||
|
||||
Then you can build TagLib from the MSYS CLANG64 Bash.
|
||||
|
||||
```
|
||||
# Adapt these environment variables to your directories
|
||||
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
|
||||
TAGLIB_DST_DIR=$HOME/projects/taglib/src/msys_build
|
||||
cd $TAGLIB_SRC_DIR
|
||||
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
cmake --build $TAGLIB_DST_DIR --config Release
|
||||
|
||||
PATH=$PATH:/clang64/bin:$TAGLIB_DST_DIR/taglib:$TAGLIB_DST_DIR/bindings/c \
|
||||
cmake --build $TAGLIB_DST_DIR --config Release --target check
|
||||
|
||||
# Install to /pkg_msys folder inside MSYS root (e.g. C:/msys64/pkg_msys/)
|
||||
cmake --install $TAGLIB_DST_DIR --config Release --prefix /pkg_msys --strip
|
||||
```
|
||||
|
||||
#### Building a Project Using TagLib (MSYS2)
|
||||
|
||||
As an example for an external application using TagLib, we create a folder
|
||||
taglib-client and copy the file tagreader.cpp from the examples folder
|
||||
of the TagLib sources into it. We then add this simple CMakeLists.txt.
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.5.0)
|
||||
project(taglib-client)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
find_package(ZLIB)
|
||||
find_package(TagLib 2.0.0 REQUIRED)
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader TagLib::tag)
|
||||
```
|
||||
|
||||
To build into a folder build_msys inside this directory, just run
|
||||
|
||||
```
|
||||
# Set this to the path where TagLib is installed
|
||||
TAGLIB_PREFIX=/pkg_msys
|
||||
cmake -B build_msys -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G Ninja
|
||||
cmake --build build_msys --config Release
|
||||
|
||||
PATH=$PATH:$TAGLIB_PREFIX/bin
|
||||
build_msys/tagreader /path/to/audio-file
|
||||
```
|
||||
|
||||
To use the C-bindings with tagreader_c.c, you can change the last two lines in
|
||||
CMakeLists.txt to
|
||||
|
||||
```
|
||||
add_executable(tagreader_c tagreader_c.c)
|
||||
target_link_libraries(tagreader_c TagLib::tag_c)
|
||||
```
|
||||
|
||||
### Using MinGW
|
||||
|
||||
The instructions for MSYS2 can also be used to build with MinGW. You could use
|
||||
Git Bash together with the MinGW provided by Qt.
|
||||
|
||||
```
|
||||
# Adapt these environment variables to your directories
|
||||
PATH=$PATH:/c/Qt/Tools/mingw1120_64/bin
|
||||
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
|
||||
TAGLIB_DST_DIR=$HOME/projects/taglib/src/mingw_build
|
||||
cd $TAGLIB_SRC_DIR
|
||||
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
|
||||
-DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DWITH_ZLIB=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release -G 'MinGW Makefiles'
|
||||
cmake --build $TAGLIB_DST_DIR --config Release
|
||||
|
||||
PATH=$PATH:$TAGLIB_DST_DIR/taglib \
|
||||
$TAGLIB_DST_DIR/examples/tagreader /path/to/audio-file
|
||||
|
||||
# Install to C:\pkg_mingw
|
||||
cmake --install $TAGLIB_DST_DIR --config Release --prefix /c/pkg_mingw --strip
|
||||
```
|
||||
|
||||
The installed package can then be used by other projects.
|
||||
|
||||
```
|
||||
TAGLIB_PREFIX=/c/pkg_mingw
|
||||
cmake -B build_mingw -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G 'MinGW Makefiles'
|
||||
cmake --build build_mingw --config Release
|
||||
|
||||
PATH=$PATH:$TAGLIB_PREFIX/bin
|
||||
build_mingw/tagreader /path/to/audio-file
|
||||
```
|
||||
make check
|
||||
|
||||
@@ -1,194 +1,24 @@
|
||||
TagLib 2.0 (Jan 24, 2024)
|
||||
=========================
|
||||
|
||||
* New major version, binary incompatible, but source-compatible with the
|
||||
latest 1.x release if no deprecated features are used.
|
||||
* Requires a C++17 compiler and uses features of C++17.
|
||||
* Major code cleanup, fixed warnings issued by compilers and static analyzers.
|
||||
* Made methods virtual which should have been virtual but could not be
|
||||
changed to keep binary compatibility, remove related workarounds.
|
||||
* Removed deprecated functions:
|
||||
- APE::Item::Item(const String &, const String &)
|
||||
- APE::Item::toStringList(): Use values()
|
||||
- APE::Item::value(): Use binaryData()
|
||||
- ASF::Properties::setLength()
|
||||
- ByteVector::checksum()
|
||||
- ByteVector::isNull(): Use isEmpty()
|
||||
- ByteVector::null
|
||||
- FLAC::File::setID3v2FrameFactory()
|
||||
- FLAC::File::streamInfoData()
|
||||
- FLAC::File::streamLength()
|
||||
- FLAC::Properties::Properties(File *, ReadStyle)
|
||||
- FLAC::Properties::sampleWidth(): Use bitsPerSample()
|
||||
- File::isReadable(): Use system functions
|
||||
- File::isWritable(): Use system functions
|
||||
- FileName::str()
|
||||
- FileRef::create(): Use constructor
|
||||
- MP4::Tag::itemListMap(): Use itemMap()
|
||||
- MPC::File::remove(): Use strip()
|
||||
- MPC::Properties::Properties(const ByteVector &, long, ReadStyle)
|
||||
- MPEG::File::save(int, ...): Use overload
|
||||
- MPEG::File::setID3v2FrameFactory(): Use constructor
|
||||
- MPEG::ID3v2::Frame::Header::Header(const ByteVector &, bool)
|
||||
- MPEG::ID3v2::Frame::Header::frameAlterPreservation(): Use
|
||||
fileAlterPreservation()
|
||||
- MPEG::ID3v2::Frame::Header::setData(const ByteVector &, bool)
|
||||
- MPEG::ID3v2::Frame::Header::size(unsigned int): Use size()
|
||||
- MPEG::ID3v2::Frame::Header::unsycronisation(): use unsynchronisation()
|
||||
- MPEG::ID3v2::Frame::checkEncoding(const StringList &, String::Type): Use
|
||||
checkTextEncoding(const StringList &, String::Type)
|
||||
- MPEG::ID3v2::Frame::headerSize(): Use Header::size()
|
||||
- MPEG::ID3v2::Frame::headerSize(unsigned int): Use
|
||||
Header::size(unsigned int)
|
||||
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, bool)
|
||||
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, unsigned int):
|
||||
Use createFrame(const ByteVector &, const Header *)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::channelType()
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::peakVolume(): Use peakVolume(ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::setChannelType()
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::setPeakVolume(const PeakVolume &): Use
|
||||
setPeakVolume(const PeakVolume &, ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustment(float): Use
|
||||
setVolumeAdjustment(float, ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustmentIndex(short): Use
|
||||
setVolumeAdjustmentIndex(short, ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustment(): Use
|
||||
volumeAdjustment(ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustmentIndex(): Use
|
||||
volumeAdjustmentIndex(ChannelType)
|
||||
- MPEG::ID3v2::Tag::footer()
|
||||
- MPEG::ID3v2::Tag::render(int): Use render(Version)
|
||||
- MPEG::XingHeader::xingHeaderOffset()
|
||||
- Ogg::Page::getCopyWithNewPageSequenceNumber()
|
||||
- Ogg::XiphComment::removeField(): Use removeFields()
|
||||
- PropertyMap::unsupportedData(): Returns now const reference, use
|
||||
addUnsupportedData() to add keys
|
||||
- RIFF::AIFF::Properties::Properties(const ByteVector &, ReadStyle)
|
||||
- RIFF::AIFF::Properties::Properties(const ByteVector &, int, ReadStyle)
|
||||
- RIFF::AIFF::Properties::sampleWidth(): Use bitsPerSample()
|
||||
- RIFF::WAV::File::save(TagTypes, bool, int): Use
|
||||
save(TagTypes, StripTags, Version)
|
||||
- RIFF::WAV::File::tag(): Returns now a TagUnion, use ID3v2Tag() to get an
|
||||
ID3v2::Tag
|
||||
- String::isNull(): Use isEmpty()
|
||||
- String::null
|
||||
- TrueAudio::File::setID3v2FrameFactory(): Use constructor
|
||||
- WavPack::Properties::Properties(const ByteVector &, long, ReadStyle)
|
||||
* Behavioral changes:
|
||||
- The basic tag methods (e.g. genre()) separate multiple values with " / "
|
||||
instead of " ".
|
||||
- The stream operator for String uses UTF-8 instead of ISO-8859-1 encoding.
|
||||
- MP4 property ORIGINALDATE is mapped to "----:com.apple.iTunes:ORIGINALDATE"
|
||||
instead of "----:com.apple.iTunes:originaldate".
|
||||
- MP4 property ENCODEDBY is mapped to "©enc" instead of "©too", which is now
|
||||
mapped to ENCODING.
|
||||
* Unified interface for complex properties like pictures.
|
||||
* Simplified the unified properties interface by providing its methods on
|
||||
FileRef.
|
||||
* C bindings: Support for properties (taglib_property_...) and complex
|
||||
properties like cover art (taglib_complex_property_...), memory I/O streams.
|
||||
* Support for Direct Stream Digital (DSD) stream files (DSF) and interchange
|
||||
file format (DSDIFF, DFF), ADTS (AAC) files.
|
||||
* The runtime version can be queried.
|
||||
* Additional utility functions ByteVector::fromUShort(),
|
||||
ByteVector::fromULongLong(), ByteVector::toULongLong(),
|
||||
ByteVector::toULongLong(), List::sort().
|
||||
* Fixed List::setAutoDelete() affecting implicitly shared copies.
|
||||
* Build system: Direct support for CMake, find_package(TagLib) exports target
|
||||
TagLib::tag.
|
||||
* Build system: Fixed PackageConfig to support both relative and absolute paths.
|
||||
* Build system: utf8cpp is no longer included, it can be provided via a system
|
||||
package or a Git submodule.
|
||||
* ASF: Support additional properties ARTISTWEBPAGE, ENCODING, ENCODINGTIME,
|
||||
FILEWEBPAGE, INITIALKEY, ORIGINALALBUM, ORIGINALARTIST, ORIGINALFILENAME,
|
||||
ORIGINALLYRICIST.
|
||||
* ID3v2: Fixed extensibility of FrameFactory, use it also for WAV and AIFF
|
||||
files.
|
||||
* MP4: Support additional properties OWNER, RELEASEDATE.
|
||||
* MP4: Introduced ItemFactory allowing clients to support new atom types.
|
||||
* MP4: Detect duration from mvhd atom if not present in mdhd atom.
|
||||
* MP4: Fixed type of hdvd atom to be integer instead of boolean.
|
||||
* MP4: Tolerate trailing garbage in M4A files.
|
||||
* MPC: Fixed content check in presence of an ID3v2 tag.
|
||||
* MPEG: Do not scan full file for ID3v2 tag when ReadStyle Fast is used.
|
||||
* RIFF: Support properties ALBUM, ARRANGER, ARTIST, ARTISTWEBPAGE, BPM,
|
||||
COMMENT, COMPOSER, COPYRIGHT, DATE, DISCSUBTITLE, ENCODEDBY, ENCODING,
|
||||
ENCODINGTIME, GENRE, ISRC, LABEL, LANGUAGE, LYRICIST, MEDIA, PERFORMER,
|
||||
RELEASECOUNTRY, REMIXER, TITLE, TRACKNUMBER.
|
||||
* WAV: Fixed crash with files having the "id3 " chunk as the only valid chunk.
|
||||
* Windows: Fixed support for files larger than 2GB.
|
||||
|
||||
TagLib 1.13.1 (Jul 1, 2023)
|
||||
===========================
|
||||
|
||||
* Fixed parsing of TXXX frames without description.
|
||||
* Detect MP4 atoms with invalid length or type.
|
||||
* Do not miss ID3v2 frames when an extended header is present.
|
||||
* Use property "DISCSUBTITLE" for ID3v2 "TSST" frame.
|
||||
* Build system improvements: Use absolute path for macOS dylib install name,
|
||||
support --define-prefix when using pkg-config, fixed minimum required
|
||||
CppUnit version.
|
||||
* Code clean up using clang-tidy.
|
||||
|
||||
TagLib 1.13 (Oct 27, 2022)
|
||||
==========================
|
||||
|
||||
* Added interface StreamTypeResolver to support streams which cannot be
|
||||
fopen()'ed, e.g. network files.
|
||||
* Added MP4::File::strip() to remove meta atom from MP4 file.
|
||||
* Added Map::value() to look up without creating entry.
|
||||
* Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame,
|
||||
use property "WORK" for ASF "WM/ContentGroupDescription",
|
||||
use property "COMPILATION" for ID3v2 "TCMP" frame.
|
||||
* Build system improvements: option WITH_ZLIB, BUILD_TESTING instead of
|
||||
BUILD_TESTS, GNUInstallDirs, FeatureSummary, tests with BUILD_SHARED_LIBS,
|
||||
cross compilation with Buildroot, systems without HAVE_GCC_ATOMIC, Clang.
|
||||
* Fixed heap-buffer-overflows when handling ASF, APE, FLAC, ID3v2, MP4, MPC
|
||||
tags.
|
||||
* Fixed detection of invalid file by extension when correct type can be
|
||||
detected by contents.
|
||||
* Fixed unnecessary creation of map entries in APE and FLAC tags if looked up
|
||||
tag does not exist.
|
||||
* Fixed parsing of MP4 non-full meta atoms.
|
||||
* Fixed potential ID3v1 false positive in the presence of an APE tag.
|
||||
* Fixed ID3v2 version handling for frames embedded in CHAP or CTOC frames.
|
||||
* Fixed parsing of multiple strings with a single BOM in ID3v2.4.0.
|
||||
* Fixed several smaller issues reported by clang-tidy.
|
||||
|
||||
TagLib 1.12 (Feb 16, 2021)
|
||||
==========================
|
||||
============================
|
||||
|
||||
* Added support for DSF and DSDIFF files.
|
||||
* Added support for WinRT.
|
||||
* Added support for Linux on POWER.
|
||||
* Added support for classical music tags of iTunes 12.5.
|
||||
* Added support for file descriptor to FileStream.
|
||||
* Added support for 'cmID', 'purl', 'egid' MP4 atoms.
|
||||
* Added support for 'GRP1' ID3v2 frame.
|
||||
* Added support for extensible WAV subformat.
|
||||
* Enabled FileRef to detect file types based on the stream content.
|
||||
* Dropped support for Windows 9x and NT 4.0 or older.
|
||||
* Check for mandatory header objects in ASF files.
|
||||
* More tolerant handling of RIFF padding, WAV files, broken MPEG streams.
|
||||
* Improved calculation of Ogg, Opus, Speex, WAV, MP4 bitrates.
|
||||
* Improved Windows compatibility by storing FLAC picture after comments.
|
||||
* Fixed numerical genres in ID3v2.3.0 'TCON' frames.
|
||||
* Fixed consistency of API removing MP4 items when empty values are set.
|
||||
* Fixed consistency of API preferring COMM frames with no description.
|
||||
* Fixed OOB read on invalid Ogg FLAC files (CVE-2018-11439).
|
||||
* Fixed handling of empty MPEG files.
|
||||
* Fixed parsing MP4 mdhd timescale.
|
||||
* Fixed reading MP4 atoms with zero length.
|
||||
* Fixed reading FLAC files with zero-sized seektables.
|
||||
* Fixed handling of lowercase field names in Vorbis Comments.
|
||||
* Fixed handling of 'rate' atoms in MP4 files.
|
||||
* Fixed handling of invalid UTF-8 sequences.
|
||||
* Fixed possible file corruptions when saving Ogg files.
|
||||
* Fixed handling of non-audio blocks, sampling rates, DSD audio in WavPack files.
|
||||
* TableOfContentsFrame::toString() improved.
|
||||
* UserTextIdentificationFrame::toString() improved.
|
||||
* Marked FileRef::create() deprecated.
|
||||
* Marked MPEG::File::save() with boolean parameters deprecated,
|
||||
provide overloads with enum parameters.
|
||||
* Several smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.11.1 (Oct 24, 2016)
|
||||
@@ -340,7 +170,7 @@ TagLib 1.8 (Sep 6, 2012)
|
||||
|
||||
* Added support for OWNE ID3 frames.
|
||||
* Changed key validation in the new PropertyMap API.
|
||||
* ID3v1::Tag::setStringHandler will no longer delete the previous handler,
|
||||
* ID3v1::Tag::setStringHandler will no londer delete the previous handler,
|
||||
the caller is responsible for this.
|
||||
* File objects will also no longer delete the passed IOStream objects. It
|
||||
should be done in the caller code after the File object is no longer
|
||||
@@ -415,7 +245,7 @@ TagLib 1.6.3 (Apr 17, 2010)
|
||||
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
|
||||
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
|
||||
* New method `int String::toInt(bool *ok)` which can return whether the
|
||||
conversion to a number was successful.
|
||||
conversion to a number was successfull.
|
||||
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
|
||||
compressed frames). (BUG:231075)
|
||||
|
||||
@@ -423,7 +253,7 @@ TagLib 1.6.2 (Apr 9, 2010)
|
||||
==========================
|
||||
|
||||
* Read Vorbis Comments from the first FLAC metadata block, if there are
|
||||
multiple ones. (BUG:211089)
|
||||
multipe ones. (BUG:211089)
|
||||
* Fixed a memory leak in FileRef's OGA format detection.
|
||||
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
|
||||
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
|
||||
@@ -458,7 +288,7 @@ TagLib 1.6 (Sep 13, 2009)
|
||||
* Added support for disabling dllimport/dllexport on Windows using the
|
||||
TAGLIB_STATIC macro.
|
||||
* Support for parsing the obsolete 'gnre' MP4 atom.
|
||||
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determine if
|
||||
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determin if
|
||||
TagLib was built with MP4/ASF support.
|
||||
|
||||
1.6 RC1:
|
||||
@@ -483,7 +313,7 @@ TagLib 1.6 (Sep 13, 2009)
|
||||
and in String::operator+=. (BUG:169389)
|
||||
* Added support for PRIV ID3v2 frames.
|
||||
* Empty ID3v2 genres are no longer treated as numeric ID3v1 genres.
|
||||
* Added support for the POPM (rating/play count) ID3v2 frame.
|
||||
* Added support for the POPM (rating/playcount) ID3v2 frame.
|
||||
* Generic RIFF file format support:
|
||||
* Support for AIFF files with ID3v2 tags.
|
||||
* Support for WAV files with ID3v2 tags.
|
||||
19
README.md
19
README.md
@@ -1,16 +1,16 @@
|
||||
# TagLib
|
||||
|
||||
[](../../actions)
|
||||
[](https://travis-ci.org/taglib/taglib)
|
||||
|
||||
### TagLib Audio Metadata Library
|
||||
|
||||
https://taglib.org/
|
||||
http://taglib.org/
|
||||
|
||||
TagLib is a library for reading and editing the metadata of several
|
||||
popular audio formats. Currently, it supports both ID3v1 and [ID3v2][]
|
||||
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
|
||||
popular audio formats. Currently it supports both ID3v1 and [ID3v2][]
|
||||
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
|
||||
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE,
|
||||
and ASF files.
|
||||
DSF, DFF, and ASF files.
|
||||
|
||||
TagLib is distributed under the [GNU Lesser General Public License][]
|
||||
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
|
||||
@@ -18,8 +18,9 @@ it may be used in proprietary applications, but if changes are made to
|
||||
TagLib they must be contributed back to the project. Please review the
|
||||
licenses if you are considering using TagLib in your project.
|
||||
|
||||
[ID3v2]: https://id3.org/
|
||||
[Ogg Vorbis]: https://xiph.org/vorbis/
|
||||
[ID3v2]: http://www.id3.org
|
||||
[Ogg Vorbis]: http://vorbis.com/
|
||||
[FLAC]: https://xiph.org/flac/
|
||||
[GNU Lesser General Public License]: https://www.gnu.org/licenses/lgpl.html
|
||||
[Mozilla Public License]: https://www.mozilla.org/MPL/MPL-1.1.html
|
||||
[GNU Lesser General Public License]: http://www.gnu.org/licenses/lgpl.html
|
||||
[Mozilla Public License]: http://www.mozilla.org/MPL/MPL-1.1.html
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
add_subdirectory(c)
|
||||
add_subdirectory(c)
|
||||
|
||||
@@ -14,34 +14,20 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/aiff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/wav
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff
|
||||
)
|
||||
|
||||
set(tag_c_HDRS tag_c.h)
|
||||
|
||||
add_library(tag_c tag_c.cpp ${tag_c_HDRS})
|
||||
|
||||
target_include_directories(tag_c INTERFACE
|
||||
$<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}>
|
||||
)
|
||||
|
||||
target_link_libraries(tag_c PRIVATE tag)
|
||||
target_link_libraries(tag_c tag)
|
||||
set_target_properties(tag_c PROPERTIES
|
||||
PUBLIC_HEADER "${tag_c_HDRS}"
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden
|
||||
)
|
||||
endif()
|
||||
if(BUILD_FRAMEWORK)
|
||||
set_target_properties(tag_c PROPERTIES FRAMEWORK TRUE)
|
||||
@@ -59,7 +45,7 @@ if(HAVE_CRUN_LIB)
|
||||
# the only game in town, the three available STLs -- Cstd,
|
||||
# stlport4 and stdcxx -- make this a mess. The KDE-Solaris
|
||||
# team supports stdcxx (Apache RogueWave stdcxx 4.1.3).
|
||||
|
||||
|
||||
# According to http://bugs.kde.org/show_bug.cgi?id=215225 the library can have the following two names:
|
||||
find_library(ROGUEWAVE_STDCXX_LIBRARY NAMES stdcxx4 stdcxx)
|
||||
if(NOT ROGUEWAVE_STDCXX_LIBRARY)
|
||||
@@ -69,42 +55,21 @@ if(HAVE_CRUN_LIB)
|
||||
endif()
|
||||
|
||||
set_target_properties(tag_c PROPERTIES
|
||||
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
|
||||
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
|
||||
VERSION 0.0.0
|
||||
SOVERSION 0
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
|
||||
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
|
||||
)
|
||||
|
||||
if(TAGLIB_INSTALL_SUFFIX)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
else()
|
||||
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
endif()
|
||||
set_target_properties(tag_c PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
|
||||
install(TARGETS tag_c
|
||||
EXPORT taglibTargets
|
||||
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
|
||||
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
|
||||
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
|
||||
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
|
||||
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
|
||||
)
|
||||
|
||||
if(NOT BUILD_FRAMEWORK)
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
else()
|
||||
set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR})
|
||||
else()
|
||||
set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
RENAME taglib${TAGLIB_INSTALL_SUFFIX}_c.pc)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -19,45 +19,28 @@
|
||||
* USA *
|
||||
***************************************************************************/
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include "tstringlist.h"
|
||||
#include "tbytevectorlist.h"
|
||||
#include "tbytevectorstream.h"
|
||||
#include "tiostream.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "fileref.h"
|
||||
#include "asffile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "mpegfile.h"
|
||||
#include "flacfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "mpcfile.h"
|
||||
#include "wavpackfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "trueaudiofile.h"
|
||||
#include "mp4file.h"
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#include "apefile.h"
|
||||
#include "itfile.h"
|
||||
#include "modfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "opusfile.h"
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
#include "tag.h"
|
||||
#include "id3v2framefactory.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
#include <asffile.h>
|
||||
#include <vorbisfile.h>
|
||||
#include <mpegfile.h>
|
||||
#include <flacfile.h>
|
||||
#include <oggflacfile.h>
|
||||
#include <mpcfile.h>
|
||||
#include <wavpackfile.h>
|
||||
#include <speexfile.h>
|
||||
#include <trueaudiofile.h>
|
||||
#include <mp4file.h>
|
||||
#include <tag.h>
|
||||
#include <string.h>
|
||||
#include <id3v2framefactory.h>
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -86,7 +69,7 @@ namespace
|
||||
{
|
||||
return String(s, unicodeStrings ? String::UTF8 : String::Latin1);
|
||||
}
|
||||
} // namespace
|
||||
}
|
||||
|
||||
void taglib_set_strings_unicode(BOOL unicode)
|
||||
{
|
||||
@@ -104,22 +87,7 @@ void taglib_free(void* pointer)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TagLib::IOStream wrapper
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size)
|
||||
{
|
||||
return reinterpret_cast<TagLib_IOStream *>(
|
||||
new ByteVectorStream(ByteVector(data, size)));
|
||||
}
|
||||
|
||||
void taglib_iostream_free(TagLib_IOStream *stream)
|
||||
{
|
||||
delete reinterpret_cast<IOStream *>(stream);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TagLib::FileRef wrapper
|
||||
// TagLib::File wrapper
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TagLib_File *taglib_file_new(const char *filename)
|
||||
@@ -129,78 +97,30 @@ TagLib_File *taglib_file_new(const char *filename)
|
||||
|
||||
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
{
|
||||
File *file = NULL;
|
||||
switch(type) {
|
||||
case TagLib_File_MPEG:
|
||||
file = new MPEG::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new MPEG::File(filename)));
|
||||
case TagLib_File_OggVorbis:
|
||||
file = new Ogg::Vorbis::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Vorbis::File(filename)));
|
||||
case TagLib_File_FLAC:
|
||||
file = new FLAC::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new FLAC::File(filename)));
|
||||
case TagLib_File_MPC:
|
||||
file = new MPC::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new MPC::File(filename)));
|
||||
case TagLib_File_OggFlac:
|
||||
file = new Ogg::FLAC::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::FLAC::File(filename)));
|
||||
case TagLib_File_WavPack:
|
||||
file = new WavPack::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new WavPack::File(filename)));
|
||||
case TagLib_File_Speex:
|
||||
file = new Ogg::Speex::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Speex::File(filename)));
|
||||
case TagLib_File_TrueAudio:
|
||||
file = new TrueAudio::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new TrueAudio::File(filename)));
|
||||
case TagLib_File_MP4:
|
||||
file = new MP4::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new MP4::File(filename)));
|
||||
case TagLib_File_ASF:
|
||||
file = new ASF::File(filename);
|
||||
break;
|
||||
case TagLib_File_AIFF:
|
||||
file = new RIFF::AIFF::File(filename);
|
||||
break;
|
||||
case TagLib_File_WAV:
|
||||
file = new RIFF::WAV::File(filename);
|
||||
break;
|
||||
case TagLib_File_APE:
|
||||
file = new APE::File(filename);
|
||||
break;
|
||||
case TagLib_File_IT:
|
||||
file = new IT::File(filename);
|
||||
break;
|
||||
case TagLib_File_Mod:
|
||||
file = new Mod::File(filename);
|
||||
break;
|
||||
case TagLib_File_S3M:
|
||||
file = new S3M::File(filename);
|
||||
break;
|
||||
case TagLib_File_XM:
|
||||
file = new XM::File(filename);
|
||||
break;
|
||||
case TagLib_File_Opus:
|
||||
file = new Ogg::Opus::File(filename);
|
||||
break;
|
||||
case TagLib_File_DSF:
|
||||
file = new DSF::File(filename);
|
||||
break;
|
||||
case TagLib_File_DSDIFF:
|
||||
file = new DSDIFF::File(filename);
|
||||
break;
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new ASF::File(filename)));
|
||||
default:
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
return file ? reinterpret_cast<TagLib_File *>(new FileRef(file)) : NULL;
|
||||
}
|
||||
|
||||
TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(
|
||||
new FileRef(reinterpret_cast<IOStream *>(stream)));
|
||||
}
|
||||
|
||||
void taglib_file_free(TagLib_File *file)
|
||||
@@ -210,18 +130,18 @@ void taglib_file_free(TagLib_File *file)
|
||||
|
||||
BOOL taglib_file_is_valid(const TagLib_File *file)
|
||||
{
|
||||
return !reinterpret_cast<const FileRef *>(file)->isNull();
|
||||
return reinterpret_cast<const FileRef *>(file)->isValid();
|
||||
}
|
||||
|
||||
TagLib_Tag *taglib_file_tag(const TagLib_File *file)
|
||||
{
|
||||
auto f = reinterpret_cast<const FileRef *>(file);
|
||||
const FileRef *f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<TagLib_Tag *>(f->tag());
|
||||
}
|
||||
|
||||
const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
|
||||
{
|
||||
auto f = reinterpret_cast<const FileRef *>(file);
|
||||
const FileRef *f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
|
||||
}
|
||||
|
||||
@@ -338,8 +258,8 @@ void taglib_tag_free_strings()
|
||||
if(!stringManagementEnabled)
|
||||
return;
|
||||
|
||||
for(auto &string : std::as_const(strings))
|
||||
free(string);
|
||||
for(List<char *>::ConstIterator it = strings.begin(); it != strings.end(); ++it)
|
||||
free(*it);
|
||||
strings.clear();
|
||||
}
|
||||
|
||||
@@ -349,25 +269,25 @@ void taglib_tag_free_strings()
|
||||
|
||||
int taglib_audioproperties_length(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->lengthInSeconds();
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->length();
|
||||
}
|
||||
|
||||
int taglib_audioproperties_bitrate(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->bitrate();
|
||||
}
|
||||
|
||||
int taglib_audioproperties_samplerate(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->sampleRate();
|
||||
}
|
||||
|
||||
int taglib_audioproperties_channels(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->channels();
|
||||
}
|
||||
|
||||
@@ -393,423 +313,3 @@ void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding)
|
||||
|
||||
ID3v2::FrameFactory::instance()->setDefaultTextEncoding(type);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Properties API
|
||||
******************************************************************************/
|
||||
namespace {
|
||||
|
||||
void _taglib_property_set(TagLib_File *file, const char* prop, const char* value, bool append)
|
||||
{
|
||||
if(file == NULL || prop == NULL)
|
||||
return;
|
||||
|
||||
auto tfile = reinterpret_cast<FileRef *>(file);
|
||||
PropertyMap map = tfile->tag()->properties();
|
||||
|
||||
if(value) {
|
||||
auto property = map.find(prop);
|
||||
if(property == map.end()) {
|
||||
map.insert(prop, StringList(value));
|
||||
}
|
||||
else {
|
||||
if(append) {
|
||||
property->second.append(value);
|
||||
}
|
||||
else {
|
||||
property->second = StringList(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
map.erase(prop);
|
||||
}
|
||||
|
||||
tfile->setProperties(map);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void taglib_property_set(TagLib_File *f, const char *prop, const char *value)
|
||||
{
|
||||
_taglib_property_set(f, prop, value, false);
|
||||
}
|
||||
|
||||
void taglib_property_set_append(TagLib_File *f, const char *prop, const char *value)
|
||||
{
|
||||
_taglib_property_set(f, prop, value, true);
|
||||
}
|
||||
|
||||
char** taglib_property_keys(TagLib_File *file)
|
||||
{
|
||||
if(file == NULL)
|
||||
return NULL;
|
||||
|
||||
const PropertyMap map = reinterpret_cast<const FileRef *>(file)->properties();
|
||||
if(map.isEmpty())
|
||||
return NULL;
|
||||
|
||||
auto props = static_cast<char **>(malloc(sizeof(char *) * (map.size() + 1)));
|
||||
char **pp = props;
|
||||
|
||||
for(const auto &i : map) {
|
||||
*pp++ = stringToCharArray(i.first);
|
||||
}
|
||||
*pp = NULL;
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
char **taglib_property_get(TagLib_File *file, const char *prop)
|
||||
{
|
||||
if(file == NULL || prop == NULL)
|
||||
return NULL;
|
||||
|
||||
const PropertyMap map = reinterpret_cast<const FileRef *>(file)->properties();
|
||||
|
||||
auto property = map.find(prop);
|
||||
if(property == map.end())
|
||||
return NULL;
|
||||
|
||||
auto props = static_cast<char **>(malloc(sizeof(char *) * (property->second.size() + 1)));
|
||||
char **pp = props;
|
||||
|
||||
for(const auto &i : property->second) {
|
||||
*pp++ = stringToCharArray(i);
|
||||
}
|
||||
*pp = NULL;
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
void taglib_property_free(char **props)
|
||||
{
|
||||
if(props == NULL)
|
||||
return;
|
||||
|
||||
char **p = props;
|
||||
while(*p) {
|
||||
free(*p++);
|
||||
}
|
||||
free(props);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Complex Properties API
|
||||
******************************************************************************/
|
||||
|
||||
namespace {
|
||||
|
||||
bool _taglib_complex_property_set(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value, bool append)
|
||||
{
|
||||
if(file == NULL || key == NULL)
|
||||
return false;
|
||||
|
||||
auto tfile = reinterpret_cast<FileRef *>(file);
|
||||
|
||||
if(value == NULL) {
|
||||
return tfile->setComplexProperties(key, {});
|
||||
}
|
||||
|
||||
VariantMap map;
|
||||
const TagLib_Complex_Property_Attribute** attrPtr = value;
|
||||
while(*attrPtr) {
|
||||
const TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
String attrKey(attr->key);
|
||||
TagLib_Variant_Type type = attr->value.type;
|
||||
switch(type) {
|
||||
case TagLib_Variant_Void:
|
||||
map.insert(attrKey, Variant());
|
||||
break;
|
||||
case TagLib_Variant_Bool:
|
||||
map.insert(attrKey, attr->value.value.boolValue != 0);
|
||||
break;
|
||||
case TagLib_Variant_Int:
|
||||
map.insert(attrKey, attr->value.value.intValue);
|
||||
break;
|
||||
case TagLib_Variant_UInt:
|
||||
map.insert(attrKey, attr->value.value.uIntValue);
|
||||
break;
|
||||
case TagLib_Variant_LongLong:
|
||||
map.insert(attrKey, attr->value.value.longLongValue);
|
||||
break;
|
||||
case TagLib_Variant_ULongLong:
|
||||
map.insert(attrKey, attr->value.value.uLongLongValue);
|
||||
break;
|
||||
case TagLib_Variant_Double:
|
||||
map.insert(attrKey, attr->value.value.doubleValue);
|
||||
break;
|
||||
case TagLib_Variant_String:
|
||||
map.insert(attrKey, attr->value.value.stringValue);
|
||||
break;
|
||||
case TagLib_Variant_StringList: {
|
||||
StringList strs;
|
||||
if(attr->value.value.stringListValue) {
|
||||
char **s = attr->value.value.stringListValue;;
|
||||
while(*s) {
|
||||
strs.append(*s++);
|
||||
}
|
||||
}
|
||||
map.insert(attrKey, strs);
|
||||
break;
|
||||
}
|
||||
case TagLib_Variant_ByteVector:
|
||||
map.insert(attrKey, ByteVector(attr->value.value.byteVectorValue,
|
||||
attr->value.size));
|
||||
break;
|
||||
}
|
||||
++attrPtr;
|
||||
}
|
||||
|
||||
return append ? tfile->setComplexProperties(key, tfile->complexProperties(key).append(map))
|
||||
: tfile->setComplexProperties(key, {map});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BOOL taglib_complex_property_set(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value)
|
||||
{
|
||||
return _taglib_complex_property_set(file, key, value, false);
|
||||
}
|
||||
|
||||
BOOL taglib_complex_property_set_append(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value)
|
||||
{
|
||||
return _taglib_complex_property_set(file, key, value, true);
|
||||
}
|
||||
|
||||
char** taglib_complex_property_keys(TagLib_File *file)
|
||||
{
|
||||
if(file == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const StringList strs = reinterpret_cast<const FileRef *>(file)->complexPropertyKeys();
|
||||
if(strs.isEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto keys = static_cast<char **>(malloc(sizeof(char *) * (strs.size() + 1)));
|
||||
char **keyPtr = keys;
|
||||
|
||||
for(const auto &str : strs) {
|
||||
*keyPtr++ = stringToCharArray(str);
|
||||
}
|
||||
*keyPtr = NULL;
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
|
||||
TagLib_File *file, const char *key)
|
||||
{
|
||||
if(file == NULL || key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto variantMaps = reinterpret_cast<const FileRef *>(file)->complexProperties(key);
|
||||
if(variantMaps.isEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TagLib_Complex_Property_Attribute ***props = static_cast<TagLib_Complex_Property_Attribute ***>(
|
||||
malloc(sizeof(TagLib_Complex_Property_Attribute **) * (variantMaps.size() + 1)));
|
||||
TagLib_Complex_Property_Attribute ***propPtr = props;
|
||||
|
||||
for(const auto &variantMap : variantMaps) {
|
||||
if(!variantMap.isEmpty()) {
|
||||
TagLib_Complex_Property_Attribute **attrs = static_cast<TagLib_Complex_Property_Attribute **>(
|
||||
malloc(sizeof(TagLib_Complex_Property_Attribute *) * (variantMap.size() + 1)));
|
||||
TagLib_Complex_Property_Attribute *attr = static_cast<TagLib_Complex_Property_Attribute *>(
|
||||
malloc(sizeof(TagLib_Complex_Property_Attribute) * variantMap.size()));
|
||||
TagLib_Complex_Property_Attribute **attrPtr = attrs;
|
||||
// The next assignment is redundant to silence the clang analyzer,
|
||||
// it is done at the end of the loop, which must be entered because
|
||||
// variantMap is not empty.
|
||||
*attrPtr = attr;
|
||||
for(const auto &[k, v] : variantMap) {
|
||||
attr->key = stringToCharArray(k);
|
||||
attr->value.size = 0;
|
||||
switch(v.type()) {
|
||||
case Variant::Void:
|
||||
attr->value.type = TagLib_Variant_Void;
|
||||
attr->value.value.stringValue = NULL;
|
||||
break;
|
||||
case Variant::Bool:
|
||||
attr->value.type = TagLib_Variant_Bool;
|
||||
attr->value.value.boolValue = v.value<bool>();
|
||||
break;
|
||||
case Variant::Int:
|
||||
attr->value.type = TagLib_Variant_Int;
|
||||
attr->value.value.intValue = v.value<int>();
|
||||
break;
|
||||
case Variant::UInt:
|
||||
attr->value.type = TagLib_Variant_UInt;
|
||||
attr->value.value.uIntValue = v.value<unsigned int>();
|
||||
break;
|
||||
case Variant::LongLong:
|
||||
attr->value.type = TagLib_Variant_LongLong;
|
||||
attr->value.value.longLongValue = v.value<long long>();
|
||||
break;
|
||||
case Variant::ULongLong:
|
||||
attr->value.type = TagLib_Variant_ULongLong;
|
||||
attr->value.value.uLongLongValue = v.value<unsigned long long>();
|
||||
break;
|
||||
case Variant::Double:
|
||||
attr->value.type = TagLib_Variant_Double;
|
||||
attr->value.value.doubleValue = v.value<double>();
|
||||
break;
|
||||
case Variant::String: {
|
||||
attr->value.type = TagLib_Variant_String;
|
||||
auto str = v.value<String>();
|
||||
attr->value.value.stringValue = stringToCharArray(str);
|
||||
attr->value.size = str.size();
|
||||
break;
|
||||
}
|
||||
case Variant::StringList: {
|
||||
attr->value.type = TagLib_Variant_StringList;
|
||||
StringList strs = v.value<StringList>();
|
||||
auto strPtr = static_cast<char **>(malloc(sizeof(char *) * (strs.size() + 1)));
|
||||
attr->value.value.stringListValue = strPtr;
|
||||
attr->value.size = strs.size();
|
||||
for(const auto &str : strs) {
|
||||
*strPtr++ = stringToCharArray(str);
|
||||
}
|
||||
*strPtr = NULL;
|
||||
break;
|
||||
}
|
||||
case Variant::ByteVector: {
|
||||
attr->value.type = TagLib_Variant_ByteVector;
|
||||
const ByteVector data = v.value<ByteVector>();
|
||||
auto bytePtr = static_cast<char *>(malloc(data.size()));
|
||||
attr->value.value.byteVectorValue = bytePtr;
|
||||
attr->value.size = data.size();
|
||||
::memcpy(bytePtr, data.data(), data.size());
|
||||
break;
|
||||
}
|
||||
case Variant::ByteVectorList:
|
||||
case Variant::VariantList:
|
||||
case Variant::VariantMap: {
|
||||
attr->value.type = TagLib_Variant_String;
|
||||
std::stringstream ss;
|
||||
ss << v;
|
||||
attr->value.value.stringValue = stringToCharArray(ss.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
*attrPtr++ = attr++;
|
||||
}
|
||||
*attrPtr = NULL;
|
||||
*propPtr++ = attrs;
|
||||
}
|
||||
}
|
||||
*propPtr = NULL;
|
||||
return props;
|
||||
}
|
||||
|
||||
void taglib_picture_from_complex_property(
|
||||
TagLib_Complex_Property_Attribute*** properties,
|
||||
TagLib_Complex_Property_Picture_Data *picture)
|
||||
{
|
||||
if(!properties || !picture) {
|
||||
return;
|
||||
}
|
||||
std::memset(picture, 0, sizeof(*picture));
|
||||
TagLib_Complex_Property_Attribute*** propPtr = properties;
|
||||
while(!picture->data && *propPtr) {
|
||||
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
|
||||
while(*attrPtr) {
|
||||
TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
TagLib_Variant_Type type = attr->value.type;
|
||||
switch(type) {
|
||||
case TagLib_Variant_String:
|
||||
if(strcmp("mimeType", attr->key) == 0) {
|
||||
picture->mimeType = attr->value.value.stringValue;
|
||||
}
|
||||
else if(strcmp("description", attr->key) == 0) {
|
||||
picture->description = attr->value.value.stringValue;
|
||||
}
|
||||
else if(strcmp("pictureType", attr->key) == 0) {
|
||||
picture->pictureType = attr->value.value.stringValue;
|
||||
}
|
||||
break;
|
||||
case TagLib_Variant_ByteVector:
|
||||
if(strcmp("data", attr->key) == 0) {
|
||||
picture->data = attr->value.value.byteVectorValue;
|
||||
picture->size = attr->value.size;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++attrPtr;
|
||||
}
|
||||
++propPtr;
|
||||
}
|
||||
}
|
||||
|
||||
void taglib_complex_property_free_keys(char **keys)
|
||||
{
|
||||
if(keys == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char **k = keys;
|
||||
while(*k) {
|
||||
free(*k++);
|
||||
}
|
||||
free(keys);
|
||||
}
|
||||
|
||||
void taglib_complex_property_free(
|
||||
TagLib_Complex_Property_Attribute ***props)
|
||||
{
|
||||
if(props == NULL) {
|
||||
return;
|
||||
}
|
||||
TagLib_Complex_Property_Attribute*** propPtr = props;
|
||||
while(*propPtr) {
|
||||
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
|
||||
while(*attrPtr) {
|
||||
TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
TagLib_Variant_Type type = attr->value.type;
|
||||
switch(type) {
|
||||
case TagLib_Variant_String:
|
||||
free(attr->value.value.stringValue);
|
||||
break;
|
||||
case TagLib_Variant_StringList:
|
||||
if(attr->value.value.stringListValue) {
|
||||
char **s = attr->value.value.stringListValue;
|
||||
while(*s) {
|
||||
free(*s++);
|
||||
}
|
||||
free(attr->value.value.stringListValue);
|
||||
}
|
||||
break;
|
||||
case TagLib_Variant_ByteVector:
|
||||
free(attr->value.value.byteVectorValue);
|
||||
break;
|
||||
case TagLib_Variant_Void:
|
||||
case TagLib_Variant_Bool:
|
||||
case TagLib_Variant_Int:
|
||||
case TagLib_Variant_UInt:
|
||||
case TagLib_Variant_LongLong:
|
||||
case TagLib_Variant_ULongLong:
|
||||
case TagLib_Variant_Double:
|
||||
break;
|
||||
}
|
||||
free(attr->key);
|
||||
++attrPtr;
|
||||
}
|
||||
free(**propPtr);
|
||||
free(*propPtr++);
|
||||
}
|
||||
free(props);
|
||||
}
|
||||
|
||||
@@ -43,10 +43,7 @@ extern "C" {
|
||||
#define TAGLIB_C_EXPORT
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* minwindef.h contains typedef int BOOL */
|
||||
#include <windows.h>
|
||||
#elif !defined BOOL
|
||||
#ifndef BOOL
|
||||
#define BOOL int
|
||||
#endif
|
||||
|
||||
@@ -60,15 +57,14 @@ extern "C" {
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
* These are used to give the C API some type safety (as opposed to
|
||||
* using void * ), but pointers to them are simply cast to the corresponding C++
|
||||
* These are used for type provide some type safety to the C API (as opposed to
|
||||
* using void *, but pointers to them are simply cast to the corresponding C++
|
||||
* types in the implementation.
|
||||
*/
|
||||
|
||||
typedef struct { int dummy; } TagLib_File;
|
||||
typedef struct { int dummy; } TagLib_Tag;
|
||||
typedef struct { int dummy; } TagLib_AudioProperties;
|
||||
typedef struct { int dummy; } TagLib_IOStream;
|
||||
|
||||
/*!
|
||||
* By default all strings coming into or out of TagLib's C API are in UTF8.
|
||||
@@ -90,22 +86,6 @@ TAGLIB_C_EXPORT void taglib_set_string_management_enabled(BOOL management);
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_free(void* pointer);
|
||||
|
||||
/*******************************************************************************
|
||||
* Stream API
|
||||
******************************************************************************/
|
||||
|
||||
/*!
|
||||
* Creates a byte vector stream from \a size bytes of \a data.
|
||||
* Such a stream can be used with taglib_file_new_iostream() to create a file
|
||||
* from memory.
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size);
|
||||
|
||||
/*!
|
||||
* Frees and closes the stream.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_iostream_free(TagLib_IOStream *stream);
|
||||
|
||||
/*******************************************************************************
|
||||
* File API
|
||||
******************************************************************************/
|
||||
@@ -120,17 +100,7 @@ typedef enum {
|
||||
TagLib_File_Speex,
|
||||
TagLib_File_TrueAudio,
|
||||
TagLib_File_MP4,
|
||||
TagLib_File_ASF,
|
||||
TagLib_File_AIFF,
|
||||
TagLib_File_WAV,
|
||||
TagLib_File_APE,
|
||||
TagLib_File_IT,
|
||||
TagLib_File_Mod,
|
||||
TagLib_File_S3M,
|
||||
TagLib_File_XM,
|
||||
TagLib_File_Opus,
|
||||
TagLib_File_DSF,
|
||||
TagLib_File_DSDIFF
|
||||
TagLib_File_ASF
|
||||
} TagLib_File_Type;
|
||||
|
||||
/*!
|
||||
@@ -148,16 +118,6 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new(const char *filename);
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type);
|
||||
|
||||
/*!
|
||||
* Creates a TagLib file from a \a stream.
|
||||
* A byte vector stream can be used to read a file from memory and write to
|
||||
* memory, e.g. when fetching network data.
|
||||
* The stream has to be created using taglib_memory_iostream_new() and shall be
|
||||
* freed using taglib_iostream_free() \e after this file is freed with
|
||||
* taglib_file_free().
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream);
|
||||
|
||||
/*!
|
||||
* Frees and closes the file.
|
||||
*/
|
||||
@@ -332,275 +292,6 @@ typedef enum {
|
||||
|
||||
TAGLIB_C_EXPORT void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding);
|
||||
|
||||
/******************************************************************************
|
||||
* Properties API
|
||||
******************************************************************************/
|
||||
|
||||
/*!
|
||||
* Sets the property \a prop with \a value. Use \a value = NULL to remove
|
||||
* the property, otherwise it will be replaced.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_property_set(TagLib_File *file, const char *prop, const char *value);
|
||||
|
||||
/*!
|
||||
* Appends \a value to the property \a prop (sets it if non-existing).
|
||||
* Use \a value = NULL to remove all values associated with the property.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value);
|
||||
|
||||
/*!
|
||||
* Get the keys of the property map.
|
||||
*
|
||||
* \return NULL terminated array of C-strings (char *), only NULL if empty.
|
||||
* It must be freed by the client using taglib_property_free().
|
||||
*/
|
||||
TAGLIB_C_EXPORT char** taglib_property_keys(TagLib_File *file);
|
||||
|
||||
/*!
|
||||
* Get value(s) of property \a prop.
|
||||
*
|
||||
* \return NULL terminated array of C-strings (char *), only NULL if empty.
|
||||
* It must be freed by the client using taglib_property_free().
|
||||
*/
|
||||
TAGLIB_C_EXPORT char** taglib_property_get(TagLib_File *file, const char *prop);
|
||||
|
||||
/*!
|
||||
* Frees the NULL terminated array \a props and the C-strings it contains.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_property_free(char **props);
|
||||
|
||||
/******************************************************************************
|
||||
* Complex Properties API
|
||||
******************************************************************************/
|
||||
|
||||
/*!
|
||||
* Types which can be stored in a TagLib_Variant.
|
||||
*
|
||||
* \related TagLib::Variant::Type
|
||||
* These correspond to TagLib::Variant::Type, but ByteVectorList, VariantList,
|
||||
* VariantMap are not supported and will be returned as their string
|
||||
* representation.
|
||||
*/
|
||||
typedef enum {
|
||||
TagLib_Variant_Void,
|
||||
TagLib_Variant_Bool,
|
||||
TagLib_Variant_Int,
|
||||
TagLib_Variant_UInt,
|
||||
TagLib_Variant_LongLong,
|
||||
TagLib_Variant_ULongLong,
|
||||
TagLib_Variant_Double,
|
||||
TagLib_Variant_String,
|
||||
TagLib_Variant_StringList,
|
||||
TagLib_Variant_ByteVector
|
||||
} TagLib_Variant_Type;
|
||||
|
||||
/*!
|
||||
* Discriminated union used in complex property attributes.
|
||||
*
|
||||
* \e type must be set according to the \e value union used.
|
||||
* \e size is only required for TagLib_Variant_ByteVector and must contain
|
||||
* the number of bytes.
|
||||
*
|
||||
* \related TagLib::Variant.
|
||||
*/
|
||||
typedef struct {
|
||||
TagLib_Variant_Type type;
|
||||
unsigned int size;
|
||||
union {
|
||||
char *stringValue;
|
||||
char *byteVectorValue;
|
||||
char **stringListValue;
|
||||
BOOL boolValue;
|
||||
int intValue;
|
||||
unsigned int uIntValue;
|
||||
long long longLongValue;
|
||||
unsigned long long uLongLongValue;
|
||||
double doubleValue;
|
||||
} value;
|
||||
} TagLib_Variant;
|
||||
|
||||
/*!
|
||||
* Attribute of a complex property.
|
||||
* Complex properties consist of a NULL-terminated array of pointers to
|
||||
* this structure with \e key and \e value.
|
||||
*/
|
||||
typedef struct {
|
||||
char *key;
|
||||
TagLib_Variant value;
|
||||
} TagLib_Complex_Property_Attribute;
|
||||
|
||||
/*!
|
||||
* Picture data extracted from a complex property by the convenience function
|
||||
* taglib_picture_from_complex_property().
|
||||
*/
|
||||
typedef struct {
|
||||
char *mimeType;
|
||||
char *description;
|
||||
char *pictureType;
|
||||
char *data;
|
||||
unsigned int size;
|
||||
} TagLib_Complex_Property_Picture_Data;
|
||||
|
||||
/*!
|
||||
* Declare complex property attributes to set a picture.
|
||||
* Can be used to define a variable \a var which can be used with
|
||||
* taglib_complex_property_set() and a "PICTURE" key to set an
|
||||
* embedded picture with the picture data \a dat of size \a siz
|
||||
* and description \a desc, mime type \a mime and picture type
|
||||
* \a typ (size is unsigned int, the other input parameters char *).
|
||||
*/
|
||||
#define TAGLIB_COMPLEX_PROPERTY_PICTURE(var, dat, siz, desc, mime, typ) \
|
||||
const TagLib_Complex_Property_Attribute \
|
||||
var##Attrs[] = { \
|
||||
{ \
|
||||
(char *)"data", \
|
||||
{ \
|
||||
TagLib_Variant_ByteVector, \
|
||||
(siz), \
|
||||
{ \
|
||||
(char *)(dat) \
|
||||
} \
|
||||
} \
|
||||
}, \
|
||||
{ \
|
||||
(char *)"mimeType", \
|
||||
{ \
|
||||
TagLib_Variant_String, \
|
||||
0U, \
|
||||
{ \
|
||||
(char *)(mime) \
|
||||
} \
|
||||
} \
|
||||
}, \
|
||||
{ \
|
||||
(char *)"description", \
|
||||
{ \
|
||||
TagLib_Variant_String, \
|
||||
0U, \
|
||||
{ \
|
||||
(char *)(desc) \
|
||||
} \
|
||||
} \
|
||||
}, \
|
||||
{ \
|
||||
(char *)"pictureType", \
|
||||
{ \
|
||||
TagLib_Variant_String, \
|
||||
0U, \
|
||||
{ \
|
||||
(char *)(typ) \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}; \
|
||||
const TagLib_Complex_Property_Attribute *var[] = { \
|
||||
&var##Attrs[0], &var##Attrs[1], &var##Attrs[2], \
|
||||
&var##Attrs[3], NULL \
|
||||
}
|
||||
|
||||
/*!
|
||||
* Sets the complex property \a key with \a value. Use \a value = NULL to
|
||||
* remove the property, otherwise it will be replaced with the NULL
|
||||
* terminated array of attributes in \a value.
|
||||
*
|
||||
* A picture can be set with the TAGLIB_COMPLEX_PROPERTY_PICTURE macro:
|
||||
*
|
||||
* \code {.c}
|
||||
* TagLib_File *file = taglib_file_new("myfile.mp3");
|
||||
* FILE *fh = fopen("mypicture.jpg", "rb");
|
||||
* if(fh) {
|
||||
* fseek(fh, 0L, SEEK_END);
|
||||
* long size = ftell(fh);
|
||||
* fseek(fh, 0L, SEEK_SET);
|
||||
* char *data = (char *)malloc(size);
|
||||
* fread(data, size, 1, fh);
|
||||
* TAGLIB_COMPLEX_PROPERTY_PICTURE(props, data, size, "Written by TagLib",
|
||||
* "image/jpeg", "Front Cover");
|
||||
* taglib_complex_property_set(file, "PICTURE", props);
|
||||
* taglib_file_save(file);
|
||||
* free(data);
|
||||
* fclose(fh);
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
TAGLIB_C_EXPORT BOOL taglib_complex_property_set(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value);
|
||||
|
||||
/*!
|
||||
* Appends \a value to the complex property \a key (sets it if non-existing).
|
||||
* Use \a value = NULL to remove all values associated with the \a key.
|
||||
*/
|
||||
TAGLIB_C_EXPORT BOOL taglib_complex_property_set_append(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value);
|
||||
|
||||
/*!
|
||||
* Get the keys of the complex properties.
|
||||
*
|
||||
* \return NULL terminated array of C-strings (char *), only NULL if empty.
|
||||
* It must be freed by the client using taglib_complex_property_free_keys().
|
||||
*/
|
||||
TAGLIB_C_EXPORT char** taglib_complex_property_keys(TagLib_File *file);
|
||||
|
||||
/*!
|
||||
* Get value(s) of complex property \a key.
|
||||
*
|
||||
* \return NULL terminated array of property values, which are themselves an
|
||||
* array of property attributes, only NULL if empty.
|
||||
* It must be freed by the client using taglib_complex_property_free().
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
|
||||
TagLib_File *file, const char *key);
|
||||
|
||||
/*!
|
||||
* Extract the complex property values of a picture.
|
||||
*
|
||||
* This function can be used to get the data from a "PICTURE" complex property
|
||||
* without having to traverse the whole variant map. A picture can be
|
||||
* retrieved like this:
|
||||
*
|
||||
* \code {.c}
|
||||
* TagLib_File *file = taglib_file_new("myfile.mp3");
|
||||
* TagLib_Complex_Property_Attribute*** properties =
|
||||
* taglib_complex_property_get(file, "PICTURE");
|
||||
* TagLib_Complex_Property_Picture_Data picture;
|
||||
* taglib_picture_from_complex_property(properties, &picture);
|
||||
* // Do something with picture.mimeType, picture.description,
|
||||
* // picture.pictureType, picture.data, picture.size, e.g. extract it.
|
||||
* FILE *fh = fopen("mypicture.jpg", "wb");
|
||||
* if(fh) {
|
||||
* fwrite(picture.data, picture.size, 1, fh);
|
||||
* fclose(fh);
|
||||
* }
|
||||
* taglib_complex_property_free(properties);
|
||||
* \endcode
|
||||
*
|
||||
* Note that the data in \a picture contains pointers to data in \a properties,
|
||||
* i.e. it only lives as long as the properties, until they are freed with
|
||||
* taglib_complex_property_free().
|
||||
* If you want to access multiple pictures or additional properties of FLAC
|
||||
* pictures ("width", "height", "numColors", "colorDepth" int values), you
|
||||
* have to traverse the \a properties yourself.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_picture_from_complex_property(
|
||||
TagLib_Complex_Property_Attribute*** properties,
|
||||
TagLib_Complex_Property_Picture_Data *picture);
|
||||
|
||||
/*!
|
||||
* Frees the NULL terminated array \a keys (as returned by
|
||||
* taglib_complex_property_keys()) and the C-strings it contains.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_complex_property_free_keys(char **keys);
|
||||
|
||||
/*!
|
||||
* Frees the NULL terminated array \a props of property attribute arrays
|
||||
* (as returned by taglib_complex_property_get()) and the data such as
|
||||
* C-strings and byte vectors contained in these attributes.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_complex_property_free(
|
||||
TagLib_Complex_Property_Attribute ***props);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=@CMAKE_PC_LIBDIR@
|
||||
includedir=@CMAKE_PC_INCLUDEDIR@
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=${CMAKE_INSTALL_PREFIX}
|
||||
libdir=${LIB_INSTALL_DIR}
|
||||
includedir=${INCLUDE_INSTALL_DIR}
|
||||
|
||||
|
||||
Name: TagLib C Bindings
|
||||
Description: Audio meta-data library (C bindings)
|
||||
Requires: taglib
|
||||
Version: @TAGLIB_LIB_VERSION_STRING@
|
||||
Libs: -L${libdir} -ltag_c@TAGLIB_INSTALL_SUFFIX@
|
||||
Cflags: -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@
|
||||
Version: ${TAGLIB_LIB_VERSION_STRING}
|
||||
Libs: -L${LIB_INSTALL_DIR} -ltag_c
|
||||
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
|
||||
9
cmake/TestFileOffsetBits.c
Normal file
9
cmake/TestFileOffsetBits.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Cause a compile-time error if off_t is smaller than 64 bits */
|
||||
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
|
||||
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
|
||||
return 0;
|
||||
}
|
||||
23
cmake/TestLargeFiles.c.cmakein
Normal file
23
cmake/TestLargeFiles.c.cmakein
Normal file
@@ -0,0 +1,23 @@
|
||||
#cmakedefine _LARGEFILE_SOURCE
|
||||
#cmakedefine _LARGEFILE64_SOURCE
|
||||
#cmakedefine _LARGE_FILES
|
||||
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Cause a compile-time error if off_t is smaller than 64 bits,
|
||||
* and make sure we have ftello / fseeko.
|
||||
*/
|
||||
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
|
||||
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
|
||||
FILE *fp = fopen(argv[0],"r");
|
||||
off_t offset = ftello( fp );
|
||||
|
||||
fseeko( fp, offset, SEEK_CUR );
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
include (MacroEnsureVersion)
|
||||
|
||||
if(NOT CPPUNIT_MIN_VERSION)
|
||||
SET(CPPUNIT_MIN_VERSION 1.14.0)
|
||||
SET(CPPUNIT_MIN_VERSION 1.12.0)
|
||||
endif(NOT CPPUNIT_MIN_VERSION)
|
||||
|
||||
FIND_PROGRAM(CPPUNIT_CONFIG_EXECUTABLE cppunit-config )
|
||||
@@ -33,7 +33,7 @@ ELSE(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
|
||||
FIND_PATH(CPPUNIT_CFLAGS cppunit/TestRunner.h PATHS /usr/include /usr/local/include )
|
||||
FIND_LIBRARY(CPPUNIT_LIBRARIES NAMES cppunit PATHS /usr/lib /usr/local/lib )
|
||||
# how can we find cppunit version?
|
||||
MESSAGE (STATUS "Ensure your cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
|
||||
MESSAGE (STATUS "Ensure you cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
|
||||
SET (CPPUNIT_INSTALLED_VERSION ${CPPUNIT_MIN_VERSION})
|
||||
ENDIF(CPPUNIT_CONFIG_EXECUTABLE)
|
||||
|
||||
@@ -56,7 +56,7 @@ IF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
|
||||
macro_ensure_version( ${CPPUNIT_MIN_VERSION} ${CPPUNIT_INSTALLED_VERSION} CPPUNIT_INSTALLED_VERSION_OK )
|
||||
|
||||
IF(NOT CPPUNIT_INSTALLED_VERSION_OK)
|
||||
MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or newer is required")
|
||||
MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or major is required")
|
||||
SET(CppUnit_FOUND FALSE)
|
||||
ENDIF(NOT CPPUNIT_INSTALLED_VERSION_OK)
|
||||
|
||||
|
||||
104
cmake/modules/TestLargeFiles.cmake
Normal file
104
cmake/modules/TestLargeFiles.cmake
Normal file
@@ -0,0 +1,104 @@
|
||||
# - Define macro to check large file support
|
||||
#
|
||||
# TEST_LARGE_FILES(VARIABLE)
|
||||
#
|
||||
# VARIABLE will be set to true if off_t is 64 bits, and fseeko/ftello present.
|
||||
# This macro will also set defines necessary enable large file support, for instance
|
||||
# _LARGE_FILES
|
||||
# _LARGEFILE_SOURCE
|
||||
# _FILE_OFFSET_BITS 64
|
||||
# HAVE_FSEEKO
|
||||
#
|
||||
# However, it is YOUR job to make sure these defines are set in a cmakedefine so they
|
||||
# end up in a config.h file that is included in your source if necessary!
|
||||
|
||||
# This macro skips the Windows specific checks. Because TagLib uses Win32 API.
|
||||
|
||||
MACRO(TEST_LARGE_FILES VARIABLE)
|
||||
IF(NOT DEFINED ${VARIABLE})
|
||||
|
||||
# On most platforms it is probably overkill to first test the flags for 64-bit off_t,
|
||||
# and then separately fseeko. However, in the future we might have 128-bit filesystems
|
||||
# (ZFS), so it might be dangerous to indiscriminately set e.g. _FILE_OFFSET_BITS=64.
|
||||
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t")
|
||||
|
||||
# First check without any special flags
|
||||
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c")
|
||||
if(FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - present")
|
||||
endif(FILE64_OK)
|
||||
|
||||
if(NOT FILE64_OK)
|
||||
# Test with _FILE_OFFSET_BITS=64
|
||||
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
|
||||
COMPILE_DEFINITIONS "-D_FILE_OFFSET_BITS=64" )
|
||||
if(FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - present with _FILE_OFFSET_BITS=64")
|
||||
set(_FILE_OFFSET_BITS 64)
|
||||
endif(FILE64_OK)
|
||||
endif(NOT FILE64_OK)
|
||||
|
||||
if(NOT FILE64_OK)
|
||||
# Test with _LARGE_FILES
|
||||
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
|
||||
COMPILE_DEFINITIONS "-D_LARGE_FILES" )
|
||||
if(FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGE_FILES")
|
||||
set(_LARGE_FILES 1)
|
||||
endif(FILE64_OK)
|
||||
endif(NOT FILE64_OK)
|
||||
|
||||
if(NOT FILE64_OK)
|
||||
# Test with _LARGEFILE_SOURCE
|
||||
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
|
||||
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
|
||||
if(FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGEFILE_SOURCE")
|
||||
set(_LARGEFILE_SOURCE 1)
|
||||
endif(FILE64_OK)
|
||||
endif(NOT FILE64_OK)
|
||||
|
||||
if(NOT FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - not present")
|
||||
else(NOT FILE64_OK)
|
||||
|
||||
# Set the flags we might have determined to be required above
|
||||
configure_file("${CMAKE_SOURCE_DIR}/cmake/TestLargeFiles.c.cmakein"
|
||||
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
|
||||
|
||||
MESSAGE(STATUS "Checking for fseeko/ftello")
|
||||
# Test if ftello/fseeko are available
|
||||
TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}"
|
||||
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
|
||||
if(FSEEKO_COMPILE_OK)
|
||||
MESSAGE(STATUS "Checking for fseeko/ftello - present")
|
||||
endif(FSEEKO_COMPILE_OK)
|
||||
|
||||
if(NOT FSEEKO_COMPILE_OK)
|
||||
# glibc 2.2 neds _LARGEFILE_SOURCE for fseeko (but not 64-bit off_t...)
|
||||
TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}"
|
||||
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c"
|
||||
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
|
||||
if(FSEEKO_COMPILE_OK)
|
||||
MESSAGE(STATUS "Checking for fseeko/ftello - present with _LARGEFILE_SOURCE")
|
||||
set(_LARGEFILE_SOURCE 1)
|
||||
endif(FSEEKO_COMPILE_OK)
|
||||
endif(NOT FSEEKO_COMPILE_OK)
|
||||
|
||||
endif(NOT FILE64_OK)
|
||||
|
||||
if(FSEEKO_COMPILE_OK)
|
||||
SET(${VARIABLE} 1 CACHE INTERNAL "Result of test for large file support" FORCE)
|
||||
set(HAVE_FSEEKO 1)
|
||||
else(FSEEKO_COMPILE_OK)
|
||||
MESSAGE(STATUS "Checking for fseeko/ftello - not found")
|
||||
SET(${VARIABLE} 0 CACHE INTERNAL "Result of test for large file support" FORCE)
|
||||
endif(FSEEKO_COMPILE_OK)
|
||||
|
||||
ENDIF(NOT DEFINED ${VARIABLE})
|
||||
ENDMACRO(TEST_LARGE_FILES VARIABLE)
|
||||
@@ -3,6 +3,11 @@
|
||||
#ifndef TAGLIB_CONFIG_H
|
||||
#define TAGLIB_CONFIG_H
|
||||
|
||||
/* Defined if required for large files support */
|
||||
#cmakedefine _LARGE_FILES ${_LARGE_FILES}
|
||||
#cmakedefine _LARGEFILE_SOURCE ${_LARGEFILE_SOURCE}
|
||||
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
|
||||
|
||||
/* Defined if your compiler supports some byte swap functions */
|
||||
#cmakedefine HAVE_GCC_BYTESWAP 1
|
||||
#cmakedefine HAVE_GLIBC_BYTESWAP 1
|
||||
@@ -10,6 +15,20 @@
|
||||
#cmakedefine HAVE_MAC_BYTESWAP 1
|
||||
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
|
||||
|
||||
/* Defined if your compiler supports some atomic operations */
|
||||
#cmakedefine HAVE_STD_ATOMIC 1
|
||||
#cmakedefine HAVE_GCC_ATOMIC 1
|
||||
#cmakedefine HAVE_MAC_ATOMIC 1
|
||||
#cmakedefine HAVE_WIN_ATOMIC 1
|
||||
#cmakedefine HAVE_IA64_ATOMIC 1
|
||||
|
||||
/* Defined if your compiler supports shared_ptr */
|
||||
#cmakedefine HAVE_STD_SMART_PTR 1
|
||||
|
||||
/* Defined if your compiler supports some safer version of vsprintf */
|
||||
#cmakedefine HAVE_VSNPRINTF 1
|
||||
#cmakedefine HAVE_VSPRINTF_S 1
|
||||
|
||||
/* Defined if your compiler supports ISO _strdup */
|
||||
#cmakedefine HAVE_ISO_STRDUP 1
|
||||
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +1,41 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>$title ($projectname)</title>
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
$extrastylesheet
|
||||
<link href="taglib-api.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="top">
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr style="height: 56px;">
|
||||
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo" width="200px"/></td>
|
||||
<td id="projectalign" style="padding-left: 0.5em;">
|
||||
<div id="projectname">$projectname
|
||||
 <span id="projectnumber">$projectnumber</span>
|
||||
</div>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<td style="padding-left: 0.5em;">
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<td>$searchbox</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="container">
|
||||
|
||||
<table border="0" width="100%">
|
||||
<tr>
|
||||
<td width="1">
|
||||
<img src="../taglib.png">
|
||||
</td>
|
||||
<td>
|
||||
<div id="intro">
|
||||
<table border="0" height="119" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr><td valign="top"><h1>TagLib $projectnumber ($title)</h1></td></tr>
|
||||
<tr>
|
||||
<td valign="bottom">
|
||||
<div id="links">
|
||||
<a href="index.html">Home</a>
|
||||
<a href="inherits.html">Class Hierarchy</a>
|
||||
<a href="namespaces.html">Namespaces</a>
|
||||
<a href="annotated.html">Classes</a>
|
||||
<a href="files.html">Headers</a>
|
||||
<a href="namespacemembers.html">Namespace Members</a>
|
||||
<a href="functions.html">Class Members</a>
|
||||
<a href="globals.html">File Members</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div id="text">
|
||||
|
||||
395
doc/taglib-api.css
Normal file
395
doc/taglib-api.css
Normal file
@@ -0,0 +1,395 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background: white;
|
||||
color: black;
|
||||
margin: 0px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
a:link {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #cccccc;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #cccccc;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
/* container */
|
||||
|
||||
#container {
|
||||
position: absolute;
|
||||
border-width: thin;
|
||||
border-style: solid;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
/* intro */
|
||||
|
||||
#intro {
|
||||
padding: 5px;
|
||||
margin: 0px;
|
||||
background: #cccccc;
|
||||
border-width: medium;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
#intro h1 {
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
/* links */
|
||||
|
||||
#links {
|
||||
font-size: x-small;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
#links a {
|
||||
border-width: thin;
|
||||
border-style: dotted;
|
||||
border-color: white;
|
||||
/* margin: 0px 10px 0px 0px; */
|
||||
margin: 1px;
|
||||
padding: 3px;
|
||||
line-height: 230%
|
||||
}
|
||||
|
||||
#links a:hover {
|
||||
color: black;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#links h3 {
|
||||
outline-width: thin;
|
||||
border-style: solid;
|
||||
padding: 2px;
|
||||
margin: 3px 0px 3px 0px;
|
||||
}
|
||||
|
||||
/* menu */
|
||||
|
||||
#menu h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* text */
|
||||
|
||||
#text {
|
||||
margin: 0px;
|
||||
padding: 5px 5px 0px 5px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#text h3 {
|
||||
border-width: thin;
|
||||
border-style: solid;
|
||||
padding: 2px;
|
||||
margin: 3px 0px 3px 0px;
|
||||
}
|
||||
|
||||
#text li {
|
||||
margin: 0px 0px 10px 0px;
|
||||
}
|
||||
|
||||
#text ul {
|
||||
margin: 5px;
|
||||
padding: 0px 0px 0px 20px;
|
||||
}
|
||||
|
||||
#leftcolumn {
|
||||
float: left;
|
||||
width: 300px;
|
||||
margin: 0px 10px 0px 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#rightcolumn {
|
||||
float: right;
|
||||
width: 210px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
/* vspacer */
|
||||
|
||||
.vspacer {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.silver {
|
||||
border-width: thin;
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
background: #cccccc;
|
||||
}
|
||||
|
||||
a.code {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
color: #4444ee
|
||||
}
|
||||
|
||||
a.codeRef {
|
||||
font-weight: normal;
|
||||
color: #4444ee
|
||||
}
|
||||
|
||||
div.fragment {
|
||||
width: 98%;
|
||||
border: 1px solid #CCCCCC;
|
||||
background-color: #f5f5f5;
|
||||
padding-left: 4px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
div.ah {
|
||||
background-color: black;
|
||||
font-weight: bold; color: #ffffff;
|
||||
margin-bottom: 3px;
|
||||
margin-top: 3px
|
||||
}
|
||||
|
||||
#text td {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
div.memdoc {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px 10px 10px 40px;
|
||||
}
|
||||
|
||||
div.memproto {
|
||||
border: thin solid black;
|
||||
background-color: #f2f2ff;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
td.paramtype {
|
||||
color: #602020;
|
||||
}
|
||||
|
||||
table.memname {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.groupHeader {
|
||||
margin-left: 16px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 6px;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
div.groupText {
|
||||
margin-left: 16px;
|
||||
font-style: italic;
|
||||
font-size: smaller
|
||||
}
|
||||
|
||||
body {
|
||||
background: white;
|
||||
color: black;
|
||||
margin-right: 20px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
td.indexkey {
|
||||
background-color: #eeeeff;
|
||||
font-weight: bold;
|
||||
padding-right : 10px;
|
||||
padding-top : 2px;
|
||||
padding-left : 10px;
|
||||
padding-bottom : 2px;
|
||||
margin-left : 0px;
|
||||
margin-right : 0px;
|
||||
margin-top : 2px;
|
||||
margin-bottom : 2px
|
||||
}
|
||||
|
||||
td.indexvalue {
|
||||
background-color: #eeeeff;
|
||||
font-style: italic;
|
||||
padding-right : 10px;
|
||||
padding-top : 2px;
|
||||
padding-left : 10px;
|
||||
padding-bottom : 2px;
|
||||
margin-left : 0px;
|
||||
margin-right : 0px;
|
||||
margin-top : 2px;
|
||||
margin-bottom : 2px
|
||||
}
|
||||
|
||||
tr.memlist {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
p.formulaDsp {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img.formulaDsp {
|
||||
|
||||
}
|
||||
|
||||
img.formulaInl {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
span.keyword {
|
||||
color: #008000
|
||||
}
|
||||
|
||||
span.keywordtype {
|
||||
color: #604020
|
||||
}
|
||||
|
||||
span.keywordflow {
|
||||
color: #e08000
|
||||
}
|
||||
|
||||
span.comment {
|
||||
color: #800000
|
||||
}
|
||||
|
||||
span.preprocessor {
|
||||
color: #806020
|
||||
}
|
||||
|
||||
span.stringliteral {
|
||||
color: #002080
|
||||
}
|
||||
|
||||
span.charliteral {
|
||||
color: #008080
|
||||
}
|
||||
|
||||
.mdTable {
|
||||
border: 1px solid #868686;
|
||||
background-color: #f2f2ff;
|
||||
}
|
||||
|
||||
.mdRow {
|
||||
padding: 8px 20px;
|
||||
}
|
||||
|
||||
.mdescLeft {
|
||||
font-size: smaller;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
background-color: #FAFAFA;
|
||||
padding-left: 8px;
|
||||
border-top: 1px none #E0E0E0;
|
||||
border-right: 1px none #E0E0E0;
|
||||
border-bottom: 1px none #E0E0E0;
|
||||
border-left: 1px none #E0E0E0;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.mdescRight {
|
||||
font-size: smaller;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-style: italic;
|
||||
background-color: #FAFAFA;
|
||||
padding-left: 4px;
|
||||
border-top: 1px none #E0E0E0;
|
||||
border-right: 1px none #E0E0E0;
|
||||
border-bottom: 1px none #E0E0E0;
|
||||
border-left: 1px none #E0E0E0;
|
||||
margin: 0px;
|
||||
padding-bottom: 0px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.memItemLeft {
|
||||
padding: 1px 0px 0px 8px;
|
||||
margin: 4px;
|
||||
border-top-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-left-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-top-color: #E0E0E0;
|
||||
border-right-color: #E0E0E0;
|
||||
border-bottom-color: #E0E0E0;
|
||||
border-left-color: #E0E0E0;
|
||||
border-right-style: none;
|
||||
border-bottom-style: none;
|
||||
border-left-style: none;
|
||||
background-color: #FAFAFA;
|
||||
font-family: Geneva, Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.memItemRight {
|
||||
padding: 1px 0px 0px 8px;
|
||||
margin: 4px;
|
||||
border-top-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-left-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-top-color: #E0E0E0;
|
||||
border-right-color: #E0E0E0;
|
||||
border-bottom-color: #E0E0E0;
|
||||
border-left-color: #E0E0E0;
|
||||
border-right-style: none;
|
||||
border-bottom-style: none;
|
||||
border-left-style: none;
|
||||
background-color: #FAFAFA;
|
||||
font-family: Geneva, Arial, Helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.search {
|
||||
color: #0000ee;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
form.search {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
input.search {
|
||||
font-size: 75%;
|
||||
color: #000080;
|
||||
font-weight: normal;
|
||||
background-color: #eeeeff;
|
||||
}
|
||||
|
||||
td.tiny {
|
||||
font-size: 75%;
|
||||
}
|
||||
BIN
doc/taglib.png
Normal file
BIN
doc/taglib.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 11 KiB |
@@ -5,7 +5,6 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/
|
||||
)
|
||||
|
||||
@@ -38,8 +37,7 @@ target_link_libraries(framelist tag)
|
||||
add_executable(strip-id3v1 strip-id3v1.cpp)
|
||||
target_link_libraries(strip-id3v1 tag)
|
||||
|
||||
install(TARGETS tagreader tagreader_c tagwriter framelist strip-id3v1
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
########### next target ###############
|
||||
|
||||
add_executable(inspect inspect.cpp)
|
||||
target_link_libraries(inspect tag)
|
||||
|
||||
@@ -23,16 +23,19 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "mpegfile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "id3v2frame.h"
|
||||
#include "id3v2header.h"
|
||||
#include "commentsframe.h"
|
||||
#include "id3v1tag.h"
|
||||
#include "apetag.h"
|
||||
#include <tbytevector.h>
|
||||
|
||||
#include <mpegfile.h>
|
||||
|
||||
#include <id3v2tag.h>
|
||||
#include <id3v2frame.h>
|
||||
#include <id3v2header.h>
|
||||
|
||||
#include <id3v1tag.h>
|
||||
|
||||
#include <apetag.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
@@ -61,16 +64,9 @@ int main(int argc, char *argv[])
|
||||
<< " bytes in tag"
|
||||
<< endl;
|
||||
|
||||
const auto &frames = id3v2tag->frameList();
|
||||
for(auto it = frames.begin(); it != frames.end(); it++) {
|
||||
cout << (*it)->frameID();
|
||||
|
||||
if(auto comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
|
||||
if(!comment->description().isEmpty())
|
||||
cout << " [" << comment->description() << "]";
|
||||
|
||||
cout << " - \"" << (*it)->toString() << "\"" << endl;
|
||||
}
|
||||
ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
|
||||
for(; it != id3v2tag->frameList().end(); it++)
|
||||
cout << (*it)->frameID() << " - \"" << (*it)->toString() << "\"" << endl;
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid id3v2 tag" << endl;
|
||||
@@ -96,8 +92,8 @@ int main(int argc, char *argv[])
|
||||
cout << endl << "APE" << endl;
|
||||
|
||||
if(ape) {
|
||||
const auto &items = ape->itemListMap();
|
||||
for(auto it = items.begin(); it != items.end(); ++it)
|
||||
for(APE::ItemListMap::ConstIterator it = ape->itemListMap().begin();
|
||||
it != ape->itemListMap().end(); ++it)
|
||||
{
|
||||
if((*it).second.type() != APE::Item::Binary)
|
||||
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
|
||||
|
||||
48
examples/inspect.cpp
Normal file
48
examples/inspect.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* Copyright (C) 2012 Lukas Lalinsky <lalinsky@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// process the command line args
|
||||
|
||||
for(int i = 1; i < argc; i++) {
|
||||
|
||||
cout << "******************** \"" << argv[i] << "\"********************" << endl;
|
||||
|
||||
FileRef f(argv[i]);
|
||||
if(!f.isNull() && f.file()) {
|
||||
cout << f.file()->toString().to8Bit(true) << endl;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,8 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "tstring.h"
|
||||
#include "mpegfile.h"
|
||||
#include <mpegfile.h>
|
||||
#include <tstring.h>
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cstdio>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "tpropertymap.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tvariant.h"
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
#include <fileref.h>
|
||||
#include <tag.h>
|
||||
#include <tpicturemap.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -47,74 +46,46 @@ int main(int argc, char *argv[])
|
||||
TagLib::Tag *tag = f.tag();
|
||||
|
||||
cout << "-- TAG (basic) --" << endl;
|
||||
cout << "title - \"" << tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << tag->album() << "\"" << endl;
|
||||
cout << "year - \"" << tag->year() << "\"" << endl;
|
||||
cout << "comment - \"" << tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
cout << "title - \"" << tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << tag->album() << "\"" << endl;
|
||||
cout << "year - \"" << tag->year() << "\"" << endl;
|
||||
cout << "comment - \"" << tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
if(!tag->pictures().isEmpty())
|
||||
cout << "pictures -" << tag->pictures() << endl;
|
||||
|
||||
TagLib::PropertyMap tags = f.properties();
|
||||
if(!tags.isEmpty()) {
|
||||
unsigned int longest = 0;
|
||||
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
|
||||
if (j->first.size() > longest) {
|
||||
longest = j->first.size();
|
||||
}
|
||||
}
|
||||
TagLib::PropertyMap tags = f.file()->properties();
|
||||
|
||||
cout << "-- TAG (properties) --" << endl;
|
||||
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
|
||||
for(auto k = j->second.begin(); k != j->second.end(); ++k) {
|
||||
cout << left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << endl;
|
||||
}
|
||||
unsigned int longest = 0;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
if (i->first.size() > longest) {
|
||||
longest = i->first.size();
|
||||
}
|
||||
}
|
||||
|
||||
TagLib::StringList names = f.complexPropertyKeys();
|
||||
for(const auto &name : names) {
|
||||
const auto& properties = f.complexProperties(name);
|
||||
for(const auto &property : properties) {
|
||||
cout << name << ":" << endl;
|
||||
for(const auto &[key, value] : property) {
|
||||
cout << " " << left << std::setfill(' ') << std::setw(11) << key << " - ";
|
||||
if(value.type() == TagLib::Variant::ByteVector) {
|
||||
cout << "(" << value.value<TagLib::ByteVector>().size() << " bytes)" << endl;
|
||||
/* The picture could be extracted using:
|
||||
ofstream picture;
|
||||
TagLib::String fn(argv[i]);
|
||||
int slashPos = fn.rfind('/');
|
||||
int dotPos = fn.rfind('.');
|
||||
if(slashPos >= 0 && dotPos > slashPos) {
|
||||
fn = fn.substr(slashPos + 1, dotPos - slashPos - 1);
|
||||
}
|
||||
fn += ".jpg";
|
||||
picture.open(fn.toCString(), ios_base::out | ios_base::binary);
|
||||
picture << value.value<TagLib::ByteVector>();
|
||||
picture.close();
|
||||
*/
|
||||
}
|
||||
else {
|
||||
cout << value << endl;
|
||||
}
|
||||
}
|
||||
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()) {
|
||||
|
||||
TagLib::AudioProperties *properties = f.audioProperties();
|
||||
|
||||
int seconds = properties->lengthInSeconds() % 60;
|
||||
int minutes = (properties->lengthInSeconds() - seconds) / 60;
|
||||
int seconds = properties->length() % 60;
|
||||
int minutes = (properties->length() - seconds) / 60;
|
||||
|
||||
cout << "-- AUDIO --" << endl;
|
||||
cout << "bitrate - " << properties->bitrate() << endl;
|
||||
cout << "sample rate - " << properties->sampleRate() << endl;
|
||||
cout << "channels - " << properties->channels() << endl;
|
||||
cout << "length - " << minutes << ":" << setfill('0') << setw(2) << right << seconds << endl;
|
||||
cout << "length - " << minutes << ":" << setfill('0') << setw(2) << seconds << endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tag_c.h"
|
||||
#include <tag_c.h>
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
@@ -34,16 +32,15 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
int seconds;
|
||||
int minutes;
|
||||
TagLib_File *file;
|
||||
TagLib_Tag *tag;
|
||||
const TagLib_AudioProperties *properties;
|
||||
|
||||
taglib_set_strings_unicode(1);
|
||||
taglib_set_strings_unicode(FALSE);
|
||||
|
||||
for(i = 1; i < argc; i++) {
|
||||
TagLib_File *file;
|
||||
TagLib_Tag *tag;
|
||||
const TagLib_AudioProperties *properties;
|
||||
char **propertiesMap;
|
||||
char **complexKeys;
|
||||
|
||||
printf("******************** \"%s\" ********************\n", argv[i]);
|
||||
|
||||
file = taglib_file_new(argv[i]);
|
||||
@@ -53,115 +50,21 @@ int main(int argc, char *argv[])
|
||||
|
||||
tag = taglib_file_tag(file);
|
||||
properties = taglib_file_audioproperties(file);
|
||||
propertiesMap = taglib_property_keys(file);
|
||||
complexKeys = taglib_complex_property_keys(file);
|
||||
|
||||
if(tag != NULL) {
|
||||
printf("-- TAG (basic) --\n");
|
||||
printf("-- TAG --\n");
|
||||
printf("title - \"%s\"\n", taglib_tag_title(tag));
|
||||
printf("artist - \"%s\"\n", taglib_tag_artist(tag));
|
||||
printf("album - \"%s\"\n", taglib_tag_album(tag));
|
||||
printf("year - \"%u\"\n", taglib_tag_year(tag));
|
||||
printf("year - \"%i\"\n", taglib_tag_year(tag));
|
||||
printf("comment - \"%s\"\n", taglib_tag_comment(tag));
|
||||
printf("track - \"%u\"\n", taglib_tag_track(tag));
|
||||
printf("track - \"%i\"\n", taglib_tag_track(tag));
|
||||
printf("genre - \"%s\"\n", taglib_tag_genre(tag));
|
||||
}
|
||||
|
||||
|
||||
if(propertiesMap != NULL) {
|
||||
char **keyPtr = propertiesMap;
|
||||
int longest = 0;
|
||||
while(*keyPtr) {
|
||||
int len = (int)strlen(*keyPtr++);
|
||||
if(len > longest) {
|
||||
longest = len;
|
||||
}
|
||||
}
|
||||
keyPtr = propertiesMap;
|
||||
|
||||
printf("-- TAG (properties) --\n");
|
||||
while(*keyPtr) {
|
||||
char **valPtr;
|
||||
char **propertyValues = valPtr = taglib_property_get(file, *keyPtr);
|
||||
while(valPtr && *valPtr)
|
||||
{
|
||||
printf("%-*s - \"%s\"\n", longest, *keyPtr, *valPtr++);
|
||||
}
|
||||
taglib_property_free(propertyValues);
|
||||
++keyPtr;
|
||||
}
|
||||
}
|
||||
|
||||
if(complexKeys != NULL) {
|
||||
char **keyPtr = complexKeys;
|
||||
while(*keyPtr) {
|
||||
TagLib_Complex_Property_Attribute*** props =
|
||||
taglib_complex_property_get(file, *keyPtr);
|
||||
if(props != NULL) {
|
||||
TagLib_Complex_Property_Attribute*** propPtr = props;
|
||||
while(*propPtr) {
|
||||
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
|
||||
printf("%s:\n", *keyPtr);
|
||||
while(*attrPtr) {
|
||||
TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
TagLib_Variant_Type type = attr->value.type;
|
||||
printf(" %-11s - ", attr->key);
|
||||
switch(type) {
|
||||
case TagLib_Variant_Void:
|
||||
printf("null\n");
|
||||
break;
|
||||
case TagLib_Variant_Bool:
|
||||
printf("%s\n", attr->value.value.boolValue ? "true" : "false");
|
||||
break;
|
||||
case TagLib_Variant_Int:
|
||||
printf("%d\n", attr->value.value.intValue);
|
||||
break;
|
||||
case TagLib_Variant_UInt:
|
||||
printf("%u\n", attr->value.value.uIntValue);
|
||||
break;
|
||||
case TagLib_Variant_LongLong:
|
||||
printf("%lld\n", attr->value.value.longLongValue);
|
||||
break;
|
||||
case TagLib_Variant_ULongLong:
|
||||
printf("%llu\n", attr->value.value.uLongLongValue);
|
||||
break;
|
||||
case TagLib_Variant_Double:
|
||||
printf("%f\n", attr->value.value.doubleValue);
|
||||
break;
|
||||
case TagLib_Variant_String:
|
||||
printf("\"%s\"\n", attr->value.value.stringValue);
|
||||
break;
|
||||
case TagLib_Variant_StringList:
|
||||
if(attr->value.value.stringListValue) {
|
||||
char **strs = attr->value.value.stringListValue;
|
||||
char **s = strs;
|
||||
while(*s) {
|
||||
if(s != strs) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%s", *s++);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
case TagLib_Variant_ByteVector:
|
||||
printf("(%u bytes)\n", attr->value.size);
|
||||
break;
|
||||
}
|
||||
++attrPtr;
|
||||
}
|
||||
++propPtr;
|
||||
}
|
||||
taglib_complex_property_free(props);
|
||||
}
|
||||
++keyPtr;
|
||||
}
|
||||
taglib_complex_property_free_keys(complexKeys);
|
||||
}
|
||||
|
||||
if(properties != NULL) {
|
||||
int seconds = taglib_audioproperties_length(properties) % 60;
|
||||
int minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
|
||||
seconds = taglib_audioproperties_length(properties) % 60;
|
||||
minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
|
||||
|
||||
printf("-- AUDIO --\n");
|
||||
printf("bitrate - %i\n", taglib_audioproperties_bitrate(properties));
|
||||
@@ -170,7 +73,6 @@ int main(int argc, char *argv[])
|
||||
printf("length - %i:%02i\n", minutes, seconds);
|
||||
}
|
||||
|
||||
taglib_property_free(propertiesMap);
|
||||
taglib_tag_free_strings();
|
||||
taglib_file_free(file);
|
||||
}
|
||||
|
||||
@@ -22,22 +22,24 @@
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tlist.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tvariant.h"
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
#include <tlist.h>
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
#include <tpicturemap.h>
|
||||
|
||||
#include <tag.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -72,7 +74,7 @@ void usage()
|
||||
cout << " -R <tagname> <tagvalue>" << endl;
|
||||
cout << " -I <tagname> <tagvalue>" << endl;
|
||||
cout << " -D <tagname>" << endl;
|
||||
cout << " -p <picturefile> <description> (\"\" \"\" to remove)" << endl;
|
||||
cout << " -p <picture(jpg only, file between double quotes)>" << endl;
|
||||
cout << endl;
|
||||
|
||||
exit(1);
|
||||
@@ -82,14 +84,14 @@ void checkForRejectedProperties(const TagLib::PropertyMap &tags)
|
||||
{ // stolen from tagreader.cpp
|
||||
if(tags.size() > 0) {
|
||||
unsigned int longest = 0;
|
||||
for(auto i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
if(i->first.size() > longest) {
|
||||
longest = i->first.size();
|
||||
}
|
||||
}
|
||||
cout << "-- rejected TAGs (properties) --" << endl;
|
||||
for(auto i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(auto j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -113,18 +115,19 @@ int main(int argc, char *argv[])
|
||||
if(fileList.isEmpty())
|
||||
usage();
|
||||
|
||||
int i = 1;
|
||||
while(i < argc - 1) {
|
||||
if(argv[argc-1][1] == 'p')
|
||||
argc++;
|
||||
|
||||
for(int i = 1; i < argc - 1; i += 2) {
|
||||
|
||||
if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
|
||||
|
||||
char field = argv[i][1];
|
||||
TagLib::String value = argv[i + 1];
|
||||
int numArgsConsumed = 2;
|
||||
TagLib::List<TagLib::FileRef>::Iterator it;
|
||||
for(it = fileList.begin(); it != fileList.end(); ++it) {
|
||||
|
||||
for(auto &f : fileList) {
|
||||
|
||||
TagLib::Tag *t = f.tag();
|
||||
TagLib::Tag *t = (*it).tag();
|
||||
|
||||
switch (field) {
|
||||
case 't':
|
||||
@@ -151,76 +154,64 @@ int main(int argc, char *argv[])
|
||||
case 'R':
|
||||
case 'I':
|
||||
if(i + 2 < argc) {
|
||||
TagLib::PropertyMap map = f.properties();
|
||||
TagLib::PropertyMap map = (*it).file()->properties ();
|
||||
if(field == 'R') {
|
||||
map.replace(value, TagLib::String(argv[i + 2]));
|
||||
}
|
||||
else {
|
||||
map.insert(value, TagLib::String(argv[i + 2]));
|
||||
}
|
||||
numArgsConsumed = 3;
|
||||
checkForRejectedProperties(f.setProperties(map));
|
||||
++i;
|
||||
checkForRejectedProperties((*it).file()->setProperties(map));
|
||||
}
|
||||
else {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case 'D': {
|
||||
TagLib::PropertyMap map = f.properties();
|
||||
TagLib::PropertyMap map = (*it).file()->properties();
|
||||
map.erase(value);
|
||||
checkForRejectedProperties(f.setProperties(map));
|
||||
checkForRejectedProperties((*it).file()->setProperties(map));
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
if(i + 2 < argc) {
|
||||
numArgsConsumed = 3;
|
||||
if(!value.isEmpty()) {
|
||||
if(!isFile(value.toCString())) {
|
||||
cout << value.toCString() << " not found." << endl;
|
||||
return 1;
|
||||
}
|
||||
ifstream picture;
|
||||
picture.open(value.toCString());
|
||||
stringstream buffer;
|
||||
buffer << picture.rdbuf();
|
||||
picture.close();
|
||||
TagLib::String buf(buffer.str());
|
||||
TagLib::ByteVector data(buf.data(TagLib::String::Latin1));
|
||||
TagLib::String mimeType = data.startsWith("\x89PNG\x0d\x0a\x1a\x0a")
|
||||
? "image/png" : "image/jpeg";
|
||||
TagLib::String description(argv[i + 2]);
|
||||
f.setComplexProperties("PICTURE", {
|
||||
{
|
||||
{"data", data},
|
||||
{"pictureType", "Front Cover"},
|
||||
{"mimeType", mimeType},
|
||||
{"description", description}
|
||||
}
|
||||
});
|
||||
case 'p':
|
||||
{
|
||||
if(!isFile(value.toCString())) {
|
||||
cout << value.toCString() << " not found." << endl;
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
// empty value, remove pictures
|
||||
f.setComplexProperties("PICTURE", {});
|
||||
ifstream picture;
|
||||
picture.open(value.toCString());
|
||||
stringstream buffer;
|
||||
buffer << picture.rdbuf();
|
||||
picture.close();
|
||||
TagLib::String buf(buffer.str());
|
||||
TagLib::ByteVector data(buf.data(TagLib::String::Latin1));
|
||||
if(!data.find("JFIF")) {
|
||||
cout << value.toCString() << " is not a JPEG." << endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
usage();
|
||||
TagLib::Picture pic(data,
|
||||
TagLib::Picture::FrontCover,
|
||||
"image/jpeg",
|
||||
"Added with taglib");
|
||||
TagLib::PictureMap picMap(pic);
|
||||
t->setPictures(picMap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
i += numArgsConsumed;
|
||||
}
|
||||
else
|
||||
usage();
|
||||
}
|
||||
|
||||
for(auto &f : fileList)
|
||||
f.save();
|
||||
TagLib::List<TagLib::FileRef>::ConstIterator it;
|
||||
for(it = fileList.begin(); it != fileList.end(); ++it)
|
||||
(*it).file()->save();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,32 +2,22 @@
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "usage: $0 [OPTIONS]"
|
||||
echo "usage: $0 [OPTIONS]"
|
||||
cat << EOH
|
||||
|
||||
options:
|
||||
[--libs]
|
||||
[--cflags]
|
||||
[--version]
|
||||
[--prefix]
|
||||
[--libs]
|
||||
[--cflags]
|
||||
[--version]
|
||||
[--prefix]
|
||||
EOH
|
||||
exit 1
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Looks useless as it is, but could be replaced with a "pcfiledir" by Buildroot.
|
||||
prefix=
|
||||
exec_prefix=
|
||||
|
||||
if test -z "$prefix"; then
|
||||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
else
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
fi
|
||||
if test -z "$exec_prefix"; then
|
||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||
else
|
||||
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
fi
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=${CMAKE_INSTALL_PREFIX}
|
||||
libdir=${LIB_INSTALL_DIR}
|
||||
includedir=${INCLUDE_INSTALL_DIR}
|
||||
|
||||
flags=""
|
||||
|
||||
@@ -39,22 +29,22 @@ while test $# -gt 0
|
||||
do
|
||||
case $1 in
|
||||
--libs)
|
||||
flags="$flags -L$libdir -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@"
|
||||
;;
|
||||
flags="$flags -L$libdir -ltag"
|
||||
;;
|
||||
--cflags)
|
||||
flags="$flags -I$includedir -I$includedir/taglib@TAGLIB_INSTALL_SUFFIX@"
|
||||
;;
|
||||
flags="$flags -I$includedir/taglib"
|
||||
;;
|
||||
--version)
|
||||
echo @TAGLIB_LIB_VERSION_STRING@
|
||||
;;
|
||||
echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
;;
|
||||
--prefix)
|
||||
echo ${prefix:-@CMAKE_INSTALL_PREFIX@}
|
||||
;;
|
||||
*)
|
||||
echo "$0: unknown option $1"
|
||||
echo
|
||||
usage
|
||||
;;
|
||||
echo $prefix
|
||||
;;
|
||||
*)
|
||||
echo "$0: unknown option $1"
|
||||
echo
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/taglib-targets.cmake")
|
||||
|
||||
set(TAGLIB_FOUND ${TagLib_FOUND})
|
||||
set(TAGLIB_VERSION ${TagLib_VERSION})
|
||||
|
||||
check_required_components("TagLib")
|
||||
|
||||
if(NOT TARGET TagLib::TagLib)
|
||||
add_library(TagLib::TagLib ALIAS TagLib::tag)
|
||||
endif()
|
||||
@@ -1,35 +1,35 @@
|
||||
@echo off
|
||||
goto beginning
|
||||
*
|
||||
* It is what it is, you can do with it as you please.
|
||||
*
|
||||
* Just don't blame me if it teaches your computer to smoke!
|
||||
*
|
||||
* -Enjoy
|
||||
* fh :)_~
|
||||
*
|
||||
:beginning
|
||||
if /i "%1#" == "--libs#" goto doit
|
||||
if /i "%1#" == "--cflags#" goto doit
|
||||
if /i "%1#" == "--version#" goto doit
|
||||
if /i "%1#" == "--prefix#" goto doit
|
||||
|
||||
echo usage: %0 [OPTIONS]
|
||||
echo [--libs]
|
||||
echo [--cflags]
|
||||
echo [--version]
|
||||
echo [--prefix]
|
||||
goto theend
|
||||
|
||||
*
|
||||
* NOTE: Windows does not assume libraries are prefixed with 'lib'.
|
||||
* NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process
|
||||
* to allow for static, shared or debug builds.
|
||||
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
|
||||
:doit
|
||||
if /i "%1#" == "--libs#" echo -L${CMAKE_INSTALL_FULL_LIBDIR} -llibtag${TAGLIB_INSTALL_SUFFIX}
|
||||
if /i "%1#" == "--cflags#" echo -I${CMAKE_INSTALL_FULL_INCLUDEDIR} -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
|
||||
|
||||
:theend
|
||||
@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_VERSION_STRING}
|
||||
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
|
||||
|
||||
:theend
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=@CMAKE_PC_LIBDIR@
|
||||
includedir=@CMAKE_PC_INCLUDEDIR@
|
||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||
libdir=@LIB_INSTALL_DIR@
|
||||
includedir=@INCLUDE_INSTALL_DIR@
|
||||
|
||||
Name: TagLib
|
||||
Description: Audio meta-data library
|
||||
Requires:
|
||||
Requires:
|
||||
Version: @TAGLIB_LIB_VERSION_STRING@
|
||||
Libs: -L${libdir} -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@
|
||||
Cflags: -I${includedir} -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@
|
||||
Libs: -L${libdir} -ltag
|
||||
Cflags: -I${includedir}/taglib
|
||||
|
||||
@@ -24,41 +24,49 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ebml
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ebml/matroska
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
|
||||
${CMAKE_SOURCE_DIR}/3rdparty
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
elseif(HAVE_ZLIB_SOURCE)
|
||||
include_directories(${ZLIB_SOURCE})
|
||||
endif()
|
||||
|
||||
set(tag_HDRS
|
||||
tag.h
|
||||
fileref.h
|
||||
audioproperties.h
|
||||
taglib_export.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
|
||||
toolkit/taglib.h
|
||||
toolkit/tstring.h
|
||||
toolkit/tlist.h
|
||||
toolkit/tlist.tcc
|
||||
toolkit/tstringlist.h
|
||||
toolkit/tstringhandler.h
|
||||
toolkit/tbytevector.h
|
||||
toolkit/tbytevectorlist.h
|
||||
toolkit/tvariant.h
|
||||
toolkit/tbytevectorstream.h
|
||||
toolkit/tiostream.h
|
||||
toolkit/tfile.h
|
||||
toolkit/tfilestream.h
|
||||
toolkit/tmap.h
|
||||
toolkit/tmap.tcc
|
||||
toolkit/tpicturetype.h
|
||||
toolkit/tpicture.h
|
||||
toolkit/tpicturemap.h
|
||||
toolkit/tpropertymap.h
|
||||
toolkit/trefcounter.h
|
||||
toolkit/tdebuglistener.h
|
||||
toolkit/tversionnumber.h
|
||||
mpeg/mpegfile.h
|
||||
mpeg/mpegproperties.h
|
||||
mpeg/mpegheader.h
|
||||
mpeg/xingheader.h
|
||||
mpeg/id3v1/id3v1tag.h
|
||||
mpeg/id3v1/id3v1genres.h
|
||||
mpeg/id3v2/id3v2.h
|
||||
mpeg/id3v2/id3v2extendedheader.h
|
||||
mpeg/id3v2/id3v2frame.h
|
||||
mpeg/id3v2/id3v2header.h
|
||||
@@ -126,7 +134,6 @@ set(tag_HDRS
|
||||
mp4/mp4item.h
|
||||
mp4/mp4properties.h
|
||||
mp4/mp4coverart.h
|
||||
mp4/mp4itemfactory.h
|
||||
mod/modfilebase.h
|
||||
mod/modfile.h
|
||||
mod/modtag.h
|
||||
@@ -137,6 +144,12 @@ set(tag_HDRS
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
ebml/ebmlfile.h
|
||||
ebml/ebmlelement.h
|
||||
ebml/ebmlconstants.h
|
||||
ebml/matroska/ebmlmatroskafile.h
|
||||
ebml/matroska/ebmlmatroskaconstants.h
|
||||
ebml/matroska/ebmlmatroskaaudio.h
|
||||
dsf/dsffile.h
|
||||
dsf/dsfproperties.h
|
||||
dsdiff/dsdifffile.h
|
||||
@@ -222,7 +235,6 @@ set(mp4_SRCS
|
||||
mp4/mp4item.cpp
|
||||
mp4/mp4properties.cpp
|
||||
mp4/mp4coverart.cpp
|
||||
mp4/mp4itemfactory.cpp
|
||||
)
|
||||
|
||||
set(ape_SRCS
|
||||
@@ -303,6 +315,16 @@ set(dsf_SRCS
|
||||
dsf/dsfproperties.cpp
|
||||
)
|
||||
|
||||
set(ebml_SRCS
|
||||
ebml/ebmlfile.cpp
|
||||
ebml/ebmlelement.cpp
|
||||
)
|
||||
|
||||
set(matroska_SRCS
|
||||
ebml/matroska/ebmlmatroskafile.cpp
|
||||
ebml/matroska/ebmlmatroskaaudio.cpp
|
||||
)
|
||||
|
||||
set(dsdiff_SRCS
|
||||
dsdiff/dsdifffile.cpp
|
||||
dsdiff/dsdiffproperties.cpp
|
||||
@@ -310,29 +332,43 @@ set(dsdiff_SRCS
|
||||
)
|
||||
|
||||
set(toolkit_SRCS
|
||||
toolkit/taglib.cpp
|
||||
toolkit/tstring.cpp
|
||||
toolkit/tstringlist.cpp
|
||||
toolkit/tstringhandler.cpp
|
||||
toolkit/tbytevector.cpp
|
||||
toolkit/tbytevectorlist.cpp
|
||||
toolkit/tvariant.cpp
|
||||
toolkit/tbytevectorstream.cpp
|
||||
toolkit/tiostream.cpp
|
||||
toolkit/tfile.cpp
|
||||
toolkit/tfilestream.cpp
|
||||
toolkit/tdebug.cpp
|
||||
toolkit/tpicturetype.cpp
|
||||
toolkit/tpicture.cpp
|
||||
toolkit/tpicturemap.cpp
|
||||
toolkit/tpropertymap.cpp
|
||||
toolkit/trefcounter.cpp
|
||||
toolkit/tdebuglistener.cpp
|
||||
toolkit/tzlib.cpp
|
||||
toolkit/tversionnumber.cpp
|
||||
)
|
||||
|
||||
if(HAVE_ZLIB_SOURCE)
|
||||
set(zlib_SRCS
|
||||
${ZLIB_SOURCE}/adler32.c
|
||||
${ZLIB_SOURCE}/crc32.c
|
||||
${ZLIB_SOURCE}/inffast.c
|
||||
${ZLIB_SOURCE}/inflate.c
|
||||
${ZLIB_SOURCE}/inftrees.c
|
||||
${ZLIB_SOURCE}/zutil.c
|
||||
)
|
||||
endif()
|
||||
|
||||
set(tag_LIB_SRCS
|
||||
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
|
||||
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
|
||||
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
|
||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
|
||||
${dsf_SRCS} ${dsdiff_SRCS}
|
||||
${ebml_SRCS} ${matroska_SRCS} ${dsf_SRCS} ${dsdiff_SRCS}
|
||||
${zlib_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
@@ -342,32 +378,26 @@ set(tag_LIB_SRCS
|
||||
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
|
||||
|
||||
target_include_directories(tag INTERFACE
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}>
|
||||
)
|
||||
|
||||
target_link_libraries(tag
|
||||
PRIVATE $<$<TARGET_EXISTS:utf8::cpp>:utf8::cpp>
|
||||
$<$<TARGET_EXISTS:ZLIB::ZLIB>:ZLIB::ZLIB>
|
||||
)
|
||||
if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
|
||||
target_link_libraries(tag ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
set_target_properties(tag PROPERTIES
|
||||
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
|
||||
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
|
||||
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
||||
INTERFACE_LINK_LIBRARIES "${ZLIB_INTERFACE_LINK_LIBRARIES}"
|
||||
LINK_INTERFACE_LIBRARIES ""
|
||||
PUBLIC_HEADER "${tag_HDRS}"
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(tag PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden
|
||||
)
|
||||
endif()
|
||||
|
||||
if(BUILD_FRAMEWORK)
|
||||
unset(INSTALL_NAME_DIR)
|
||||
set_target_properties(tag PROPERTIES
|
||||
set_target_properties(tag PROPERTIES
|
||||
FRAMEWORK TRUE
|
||||
MACOSX_RPATH 1
|
||||
VERSION "A"
|
||||
@@ -375,43 +405,10 @@ if(BUILD_FRAMEWORK)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(TAGLIB_INSTALL_SUFFIX)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
else()
|
||||
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
endif()
|
||||
set_target_properties(tag PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
|
||||
install(TARGETS tag
|
||||
EXPORT taglibTargets
|
||||
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
"${PROJECT_SOURCE_DIR}/taglib-config.cmake.in"
|
||||
"${PROJECT_BINARY_DIR}/taglib-config.cmake"
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${PROJECT_BINARY_DIR}/taglib-config-version.cmake"
|
||||
VERSION "${TAGLIB_LIB_VERSION_STRING}"
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
install(EXPORT taglibTargets
|
||||
FILE taglib-targets.cmake
|
||||
NAMESPACE TagLib::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
)
|
||||
|
||||
install(FILES "${PROJECT_BINARY_DIR}/taglib-config.cmake"
|
||||
"${PROJECT_BINARY_DIR}/taglib-config-version.cmake"
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
|
||||
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
|
||||
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
|
||||
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
|
||||
)
|
||||
|
||||
@@ -49,8 +49,8 @@ APE Tag Version 2.000 (with header, recommended):
|
||||
|
||||
APE tag items should be sorted ascending by size. When streaming, parts of the
|
||||
APE tag may be dropped to reduce the danger of drop outs between tracks. This
|
||||
is not required, but is strongly recommended. It would be desirable for the
|
||||
items to be sorted by importance / size, but this is not feasible. This
|
||||
is not required, but is strongly recommended. It would be desirable for the i
|
||||
tems to be sorted by importance / size, but this is not feasible. This
|
||||
convention should only be broken when adding less important small items and it
|
||||
is not desirable to rewrite the entire tag. An APE tag at the end of a file
|
||||
(the recommended location) must have at least a footer; an APE tag at the
|
||||
|
||||
@@ -31,14 +31,17 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "apefile.h"
|
||||
#include <tbytevector.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tagunion.h>
|
||||
#include <id3v1tag.h>
|
||||
#include <id3v2header.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "id3v1tag.h"
|
||||
#include "id3v2header.h"
|
||||
#include "tagunion.h"
|
||||
#include "tagutils.h"
|
||||
#include "apefile.h"
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
|
||||
@@ -47,23 +50,30 @@ using namespace TagLib;
|
||||
namespace
|
||||
{
|
||||
enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
|
||||
} // namespace
|
||||
}
|
||||
|
||||
class APE::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
offset_t APELocation { -1 };
|
||||
long APESize { 0 };
|
||||
FilePrivate() :
|
||||
APELocation(-1),
|
||||
APESize(0),
|
||||
ID3v1Location(-1),
|
||||
ID3v2Location(-1),
|
||||
ID3v2Size(0) {}
|
||||
|
||||
offset_t ID3v1Location { -1 };
|
||||
long long APELocation;
|
||||
long long APESize;
|
||||
|
||||
std::unique_ptr<ID3v2::Header> ID3v2Header;
|
||||
offset_t ID3v2Location { -1 };
|
||||
long ID3v2Size { 0 };
|
||||
long long ID3v1Location;
|
||||
|
||||
TagUnion tag;
|
||||
SCOPED_PTR<ID3v2::Header> ID3v2Header;
|
||||
long long ID3v2Location;
|
||||
long long ID3v2Size;
|
||||
|
||||
std::unique_ptr<Properties> properties;
|
||||
DoubleTagUnion tag;
|
||||
|
||||
SCOPED_PTR<AudioProperties> properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -75,46 +85,39 @@ bool APE::File::isSupported(IOStream *stream)
|
||||
// An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
|
||||
return (buffer.find("MAC ") >= 0);
|
||||
return (buffer.find("MAC ") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
APE::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
|
||||
APE::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
APE::File::~File() = default;
|
||||
APE::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
TagLib::Tag *APE::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
PropertyMap APE::File::properties() const
|
||||
{
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void APE::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag.removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(ID3v1Tag())
|
||||
@@ -123,7 +126,7 @@ PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
||||
return APETag(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
APE::Properties *APE::File::audioProperties() const
|
||||
APE::AudioProperties *APE::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
@@ -175,7 +178,7 @@ bool APE::File::save()
|
||||
}
|
||||
|
||||
const ByteVector data = APETag()->render();
|
||||
insert(data, d->APELocation, d->APESize);
|
||||
insert(data, d->APELocation, static_cast<size_t>(d->APESize));
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
|
||||
@@ -187,7 +190,7 @@ bool APE::File::save()
|
||||
// APE tag is empty. Remove the old one.
|
||||
|
||||
if(d->APELocation >= 0) {
|
||||
removeBlock(d->APELocation, d->APESize);
|
||||
removeBlock(d->APELocation, static_cast<size_t>(d->APESize));
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location -= d->APESize;
|
||||
@@ -213,10 +216,10 @@ APE::Tag *APE::File::APETag(bool create)
|
||||
void APE::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v1)
|
||||
d->tag.set(ApeID3v1Index, nullptr);
|
||||
d->tag.set(ApeID3v1Index, 0);
|
||||
|
||||
if(tags & APE)
|
||||
d->tag.set(ApeAPEIndex, nullptr);
|
||||
d->tag.set(ApeAPEIndex, 0);
|
||||
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
@@ -244,7 +247,7 @@ void APE::File::read(bool readProperties)
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
seek(d->ID3v2Location);
|
||||
d->ID3v2Header = std::make_unique<ID3v2::Header>(readBlock(ID3v2::Header::size()));
|
||||
d->ID3v2Header.reset(new ID3v2::Header(readBlock(ID3v2::Header::size())));
|
||||
d->ID3v2Size = d->ID3v2Header->completeTagSize();
|
||||
}
|
||||
|
||||
@@ -272,7 +275,7 @@ void APE::File::read(bool readProperties)
|
||||
|
||||
if(readProperties) {
|
||||
|
||||
offset_t streamLength;
|
||||
long long streamLength;
|
||||
|
||||
if(d->APELocation >= 0)
|
||||
streamLength = d->APELocation;
|
||||
@@ -289,6 +292,6 @@ void APE::File::read(bool readProperties)
|
||||
seek(0);
|
||||
}
|
||||
|
||||
d->properties = std::make_unique<Properties>(this, streamLength);
|
||||
d->properties.reset(new AudioProperties(this, streamLength));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace TagLib {
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs an APE file from \a stream. If \a readProperties is true the
|
||||
@@ -102,47 +102,31 @@ namespace TagLib {
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
|
||||
* or a combination of the two.
|
||||
*/
|
||||
TagLib::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* If the file contains both an APE and an ID3v1 tag, only APE
|
||||
* will be converted to the PropertyMap.
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
virtual TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
|
||||
* tag will be updated as well.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
virtual PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the APE::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Saves the file.
|
||||
@@ -150,7 +134,7 @@ namespace TagLib {
|
||||
* \note According to the official Monkey's Audio SDK, an APE file
|
||||
* can only have either ID3V1 or APE tags, so a parameter is used here.
|
||||
*/
|
||||
bool save() override;
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
@@ -224,13 +208,15 @@ namespace TagLib {
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties);
|
||||
|
||||
class FilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
FilePrivate *d;
|
||||
};
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,25 +24,37 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "apefooter.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "apefooter.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace APE;
|
||||
|
||||
class APE::Footer::FooterPrivate
|
||||
{
|
||||
public:
|
||||
unsigned int version { 0 };
|
||||
FooterPrivate() :
|
||||
version(0),
|
||||
footerPresent(true),
|
||||
headerPresent(false),
|
||||
isHeader(false),
|
||||
itemCount(0),
|
||||
tagSize(0) {}
|
||||
|
||||
bool footerPresent { true };
|
||||
bool headerPresent { false };
|
||||
unsigned int version;
|
||||
|
||||
bool isHeader { false };
|
||||
bool footerPresent;
|
||||
bool headerPresent;
|
||||
|
||||
unsigned int itemCount { 0 };
|
||||
unsigned int tagSize { 0 };
|
||||
bool isHeader;
|
||||
|
||||
unsigned int itemCount;
|
||||
unsigned int tagSize;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -64,17 +76,20 @@ ByteVector APE::Footer::fileIdentifier()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Footer::Footer() :
|
||||
d(std::make_unique<FooterPrivate>())
|
||||
d(new FooterPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
APE::Footer::Footer(const ByteVector &data) :
|
||||
d(std::make_unique<FooterPrivate>())
|
||||
d(new FooterPrivate())
|
||||
{
|
||||
parse(data);
|
||||
}
|
||||
|
||||
APE::Footer::~Footer() = default;
|
||||
APE::Footer::~Footer()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
unsigned int APE::Footer::version() const
|
||||
{
|
||||
@@ -120,7 +135,8 @@ unsigned int APE::Footer::completeTagSize() const
|
||||
{
|
||||
if(d->headerPresent)
|
||||
return d->tagSize + size();
|
||||
return d->tagSize;
|
||||
else
|
||||
return d->tagSize;
|
||||
}
|
||||
|
||||
void APE::Footer::setTagSize(unsigned int s)
|
||||
@@ -142,7 +158,8 @@ ByteVector APE::Footer::renderHeader() const
|
||||
{
|
||||
if(!d->headerPresent)
|
||||
return ByteVector();
|
||||
return render(true);
|
||||
else
|
||||
return render(true);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -158,19 +175,19 @@ void APE::Footer::parse(const ByteVector &data)
|
||||
|
||||
// Read the version number
|
||||
|
||||
d->version = data.toUInt(8, false);
|
||||
d->version = data.toUInt32LE(8);
|
||||
|
||||
// Read the tag size
|
||||
|
||||
d->tagSize = data.toUInt(12, false);
|
||||
d->tagSize = data.toUInt32LE(12);
|
||||
|
||||
// Read the item count
|
||||
|
||||
d->itemCount = data.toUInt(16, false);
|
||||
d->itemCount = data.toUInt32LE(16);
|
||||
|
||||
// Read the flags
|
||||
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt32LE(20)));
|
||||
|
||||
d->headerPresent = flags[31];
|
||||
d->footerPresent = !flags[30];
|
||||
@@ -189,15 +206,15 @@ ByteVector APE::Footer::render(bool isHeader) const
|
||||
// add the version number -- we always render a 2.000 tag regardless of what
|
||||
// the tag originally was.
|
||||
|
||||
v.append(ByteVector::fromUInt(2000, false));
|
||||
v.append(ByteVector::fromUInt32LE(2000));
|
||||
|
||||
// add the tag size
|
||||
|
||||
v.append(ByteVector::fromUInt(d->tagSize, false));
|
||||
v.append(ByteVector::fromUInt32LE(d->tagSize));
|
||||
|
||||
// add the item count
|
||||
|
||||
v.append(ByteVector::fromUInt(d->itemCount, false));
|
||||
v.append(ByteVector::fromUInt32LE(d->itemCount));
|
||||
|
||||
// render and add the flags
|
||||
|
||||
@@ -207,11 +224,11 @@ ByteVector APE::Footer::render(bool isHeader) const
|
||||
flags[30] = false; // footer is always present
|
||||
flags[29] = isHeader;
|
||||
|
||||
v.append(ByteVector::fromUInt(static_cast<unsigned int>(flags.to_ulong()), false));
|
||||
v.append(ByteVector::fromUInt32LE(flags.to_ulong()));
|
||||
|
||||
// add the reserved 64bit
|
||||
|
||||
v.append(ByteVector::fromLongLong(0));
|
||||
v.append(ByteVector::fromUInt64BE(0));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -54,16 +54,13 @@ namespace TagLib {
|
||||
* Constructs an APE footer based on \a data. parse() is called
|
||||
* immediately.
|
||||
*/
|
||||
Footer(const ByteVector &data);
|
||||
explicit Footer(const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Destroys the footer.
|
||||
*/
|
||||
virtual ~Footer();
|
||||
|
||||
Footer(const Footer &) = delete;
|
||||
Footer &operator=(const Footer &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the version number. (Note: This is the 1000 or 2000.)
|
||||
*/
|
||||
@@ -163,12 +160,14 @@ namespace TagLib {
|
||||
ByteVector render(bool isHeader) const;
|
||||
|
||||
private:
|
||||
Footer(const Footer &);
|
||||
Footer &operator=(const Footer &);
|
||||
|
||||
class FooterPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FooterPrivate> d;
|
||||
FooterPrivate *d;
|
||||
};
|
||||
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,24 +23,35 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tdebug.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "apeitem.h"
|
||||
|
||||
#include <utility>
|
||||
#include <numeric>
|
||||
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace APE;
|
||||
|
||||
class APE::Item::ItemPrivate
|
||||
struct ItemData
|
||||
{
|
||||
public:
|
||||
Item::ItemTypes type { Text };
|
||||
ItemData() :
|
||||
type(Item::Text),
|
||||
readOnly(false) {}
|
||||
|
||||
Item::ItemTypes type;
|
||||
String key;
|
||||
ByteVector value;
|
||||
StringList text;
|
||||
bool readOnly { false };
|
||||
bool readOnly;
|
||||
};
|
||||
|
||||
class APE::Item::ItemPrivate
|
||||
{
|
||||
public:
|
||||
ItemPrivate() :
|
||||
data(new ItemData()) {}
|
||||
|
||||
SHARED_PTR<ItemData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -48,36 +59,46 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Item::Item() :
|
||||
d(std::make_unique<ItemPrivate>())
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const String &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->data->key = key;
|
||||
d->data->text.append(value);
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const StringList &values) :
|
||||
d(std::make_unique<ItemPrivate>())
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->key = key;
|
||||
d->text = values;
|
||||
d->data->key = key;
|
||||
d->data->text = values;
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
|
||||
d(std::make_unique<ItemPrivate>())
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->key = key;
|
||||
d->data->key = key;
|
||||
if(binary) {
|
||||
d->type = Binary;
|
||||
d->value = value;
|
||||
d->data->type = Binary;
|
||||
d->data->value = value;
|
||||
}
|
||||
else {
|
||||
d->text.append(value);
|
||||
d->data->text.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
APE::Item::Item(const Item &item) :
|
||||
d(std::make_unique<ItemPrivate>(*item.d))
|
||||
d(new ItemPrivate(*item.d))
|
||||
{
|
||||
}
|
||||
|
||||
APE::Item::~Item() = default;
|
||||
APE::Item::~Item()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
Item &APE::Item::operator=(const Item &item)
|
||||
{
|
||||
@@ -85,7 +106,7 @@ Item &APE::Item::operator=(const Item &item)
|
||||
return *this;
|
||||
}
|
||||
|
||||
void APE::Item::swap(Item &item) noexcept
|
||||
void APE::Item::swap(Item &item)
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
@@ -94,117 +115,122 @@ void APE::Item::swap(Item &item) noexcept
|
||||
|
||||
void APE::Item::setReadOnly(bool readOnly)
|
||||
{
|
||||
d->readOnly = readOnly;
|
||||
d->data->readOnly = readOnly;
|
||||
}
|
||||
|
||||
bool APE::Item::isReadOnly() const
|
||||
{
|
||||
return d->readOnly;
|
||||
return d->data->readOnly;
|
||||
}
|
||||
|
||||
void APE::Item::setType(APE::Item::ItemTypes val)
|
||||
{
|
||||
d->type = val;
|
||||
d->data->type = val;
|
||||
}
|
||||
|
||||
APE::Item::ItemTypes APE::Item::type() const
|
||||
{
|
||||
return d->type;
|
||||
return d->data->type;
|
||||
}
|
||||
|
||||
String APE::Item::key() const
|
||||
{
|
||||
return d->key;
|
||||
return d->data->key;
|
||||
}
|
||||
|
||||
ByteVector APE::Item::binaryData() const
|
||||
{
|
||||
return d->value;
|
||||
return d->data->value;
|
||||
}
|
||||
|
||||
void APE::Item::setBinaryData(const ByteVector &value)
|
||||
{
|
||||
d->type = Binary;
|
||||
d->value = value;
|
||||
d->text.clear();
|
||||
d->data->type = Binary;
|
||||
d->data->value = value;
|
||||
d->data->text.clear();
|
||||
}
|
||||
|
||||
void APE::Item::setKey(const String &key)
|
||||
{
|
||||
d->key = key;
|
||||
d->data->key = key;
|
||||
}
|
||||
|
||||
void APE::Item::setValue(const String &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->value.clear();
|
||||
d->data->type = Text;
|
||||
d->data->text = value;
|
||||
d->data->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::setValues(const StringList &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->value.clear();
|
||||
d->data->type = Text;
|
||||
d->data->text = value;
|
||||
d->data->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::appendValue(const String &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text.append(value);
|
||||
d->value.clear();
|
||||
d->data->type = Text;
|
||||
d->data->text.append(value);
|
||||
d->data->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::appendValues(const StringList &values)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text.append(values);
|
||||
d->value.clear();
|
||||
d->data->type = Text;
|
||||
d->data->text.append(values);
|
||||
d->data->value.clear();
|
||||
}
|
||||
|
||||
int APE::Item::size() const
|
||||
{
|
||||
int result = 8 + d->key.size() + 1;
|
||||
switch(d->type) {
|
||||
size_t result = 8 + d->data->key.size() + 1;
|
||||
switch(d->data->type) {
|
||||
case Text:
|
||||
if(!d->text.isEmpty()) {
|
||||
result = std::accumulate(d->text.cbegin(), d->text.cend(), result,
|
||||
[](int sz, const String &t) {
|
||||
return sz + 1 + t.data(String::UTF8).size();
|
||||
}) - 1;
|
||||
if(!d->data->text.isEmpty()) {
|
||||
StringList::ConstIterator it = d->data->text.begin();
|
||||
|
||||
result += it->data(String::UTF8).size();
|
||||
it++;
|
||||
for(; it != d->data->text.end(); ++it)
|
||||
result += 1 + it->data(String::UTF8).size();
|
||||
}
|
||||
break;
|
||||
|
||||
case Binary:
|
||||
case Locator:
|
||||
result += d->value.size();
|
||||
result += d->data->value.size();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
StringList APE::Item::values() const
|
||||
{
|
||||
return d->text;
|
||||
return d->data->text;
|
||||
}
|
||||
|
||||
String APE::Item::toString() const
|
||||
{
|
||||
if(d->type == Text && !isEmpty())
|
||||
return d->text.front();
|
||||
return String();
|
||||
if(d->data->type == Text && !isEmpty())
|
||||
return d->data->text.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
bool APE::Item::isEmpty() const
|
||||
{
|
||||
switch(d->type) {
|
||||
switch(d->data->type) {
|
||||
case Text:
|
||||
if(d->text.isEmpty())
|
||||
if(d->data->text.isEmpty())
|
||||
return true;
|
||||
return d->text.size() == 1 && d->text.front().isEmpty();
|
||||
if(d->data->text.size() == 1 && d->data->text.front().isEmpty())
|
||||
return true;
|
||||
return false;
|
||||
case Binary:
|
||||
case Locator:
|
||||
return d->value.isEmpty();
|
||||
return d->data->value.isEmpty();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -219,52 +245,53 @@ void APE::Item::parse(const ByteVector &data)
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int valueLength = data.toUInt(0, false);
|
||||
const unsigned int flags = data.toUInt(4, false);
|
||||
const unsigned int valueLength = data.toUInt32LE(0);
|
||||
const unsigned int flags = data.toUInt32LE(4);
|
||||
|
||||
// An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8.
|
||||
// We assume that the validity of the given key has been checked.
|
||||
|
||||
d->key = String(&data[8], String::Latin1);
|
||||
d->data->key = String(&data[8], String::Latin1);
|
||||
|
||||
const ByteVector val = data.mid(8 + d->key.size() + 1, valueLength);
|
||||
const ByteVector value = data.mid(8 + d->data->key.size() + 1, valueLength);
|
||||
|
||||
setReadOnly(flags & 1);
|
||||
setType(static_cast<ItemTypes>((flags >> 1) & 3));
|
||||
setType(ItemTypes((flags >> 1) & 3));
|
||||
|
||||
if(Text == d->type)
|
||||
d->text = StringList(ByteVectorList::split(val, '\0'), String::UTF8);
|
||||
if(Text == d->data->type)
|
||||
d->data->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
|
||||
else
|
||||
d->value = val;
|
||||
d->data->value = value;
|
||||
}
|
||||
|
||||
ByteVector APE::Item::render() const
|
||||
{
|
||||
ByteVector data;
|
||||
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
|
||||
ByteVector val;
|
||||
unsigned int flags = ((d->data->readOnly) ? 1 : 0) | (d->data->type << 1);
|
||||
ByteVector value;
|
||||
|
||||
if(isEmpty())
|
||||
return data;
|
||||
|
||||
if(d->type == Text) {
|
||||
auto it = d->text.cbegin();
|
||||
if(d->data->type == Text) {
|
||||
StringList::ConstIterator it = d->data->text.begin();
|
||||
|
||||
val.append(it->data(String::UTF8));
|
||||
for(it = std::next(it); it != d->text.cend(); ++it) {
|
||||
val.append('\0');
|
||||
val.append(it->data(String::UTF8));
|
||||
value.append(it->data(String::UTF8));
|
||||
it++;
|
||||
for(; it != d->data->text.end(); ++it) {
|
||||
value.append('\0');
|
||||
value.append(it->data(String::UTF8));
|
||||
}
|
||||
d->value = val;
|
||||
d->data->value = value;
|
||||
}
|
||||
else
|
||||
val.append(d->value);
|
||||
value.append(d->data->value);
|
||||
|
||||
data.append(ByteVector::fromUInt(val.size(), false));
|
||||
data.append(ByteVector::fromUInt(flags, false));
|
||||
data.append(d->key.data(String::Latin1));
|
||||
data.append(ByteVector::fromUInt32LE(value.size()));
|
||||
data.append(ByteVector::fromUInt32LE(flags));
|
||||
data.append(d->data->key.data(String::Latin1));
|
||||
data.append(ByteVector('\0'));
|
||||
data.append(val);
|
||||
data.append(value);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
#include "tstringlist.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace APE {
|
||||
|
||||
//! An implementation of APE-items
|
||||
|
||||
/*!
|
||||
@@ -56,6 +58,11 @@ namespace TagLib {
|
||||
*/
|
||||
Item();
|
||||
|
||||
/*!
|
||||
* Constructs a text item with \a key and \a value.
|
||||
*/
|
||||
Item(const String &key, const String &value);
|
||||
|
||||
/*!
|
||||
* Constructs a text item with \a key and \a values.
|
||||
*/
|
||||
@@ -85,7 +92,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Exchanges the content of this item by the content of \a item.
|
||||
*/
|
||||
void swap(Item &item) noexcept;
|
||||
void swap(Item &item);
|
||||
|
||||
/*!
|
||||
* Returns the key.
|
||||
@@ -120,7 +127,7 @@ namespace TagLib {
|
||||
* Sets the text value of the item to the list of values in \a value and clears
|
||||
* any previous contents.
|
||||
*
|
||||
* \see values()
|
||||
* \see toStringList()
|
||||
*/
|
||||
void setValues(const StringList &values);
|
||||
|
||||
@@ -134,7 +141,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Appends \a values to extend the current list of text values.
|
||||
*
|
||||
* \see values()
|
||||
* \see toStringList()
|
||||
*/
|
||||
void appendValues(const StringList &values);
|
||||
|
||||
@@ -177,11 +184,11 @@ namespace TagLib {
|
||||
bool isReadOnly() const;
|
||||
|
||||
/*!
|
||||
* Sets the type of the item to \a val.
|
||||
* Sets the type of the item to \a type.
|
||||
*
|
||||
* \see ItemTypes
|
||||
*/
|
||||
void setType(ItemTypes val);
|
||||
void setType(ItemTypes type);
|
||||
|
||||
/*!
|
||||
* Returns the type of the item.
|
||||
@@ -195,10 +202,12 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class ItemPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ItemPrivate> d;
|
||||
ItemPrivate *d;
|
||||
};
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -27,71 +27,95 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "apeproperties.h"
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "apeproperties.h"
|
||||
#include "apefile.h"
|
||||
#include "apefooter.h"
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class APE::Properties::PropertiesPrivate
|
||||
class APE::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int version { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
unsigned int sampleFrames { 0 };
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
version(0),
|
||||
bitsPerSample(0),
|
||||
sampleFrames(0) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int version;
|
||||
int bitsPerSample;
|
||||
unsigned int sampleFrames;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Properties::Properties(File *file, offset_t streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
APE::AudioProperties::AudioProperties(File *file, long long streamLength, ReadStyle) :
|
||||
TagLib::AudioProperties(),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
read(file, streamLength);
|
||||
}
|
||||
|
||||
APE::Properties::~Properties() = default;
|
||||
APE::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int APE::Properties::lengthInMilliseconds() const
|
||||
int APE::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int APE::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int APE::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int APE::Properties::bitrate() const
|
||||
int APE::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int APE::Properties::sampleRate() const
|
||||
int APE::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int APE::Properties::channels() const
|
||||
int APE::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int APE::Properties::version() const
|
||||
int APE::AudioProperties::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
int APE::Properties::bitsPerSample() const
|
||||
int APE::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
unsigned int APE::Properties::sampleFrames() const
|
||||
unsigned int APE::AudioProperties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
@@ -107,29 +131,29 @@ namespace
|
||||
if(header.size() < 6 || !header.startsWith("MAC "))
|
||||
return -1;
|
||||
|
||||
return header.toUShort(4, false);
|
||||
return header.toUInt16LE(4);
|
||||
}
|
||||
} // namespace
|
||||
}
|
||||
|
||||
void APE::Properties::read(File *file, offset_t streamLength)
|
||||
void APE::AudioProperties::read(File *file, long long streamLength)
|
||||
{
|
||||
// First, we assume that the file pointer is set at the first descriptor.
|
||||
offset_t offset = file->tell();
|
||||
int vers = headerVersion(file->readBlock(6));
|
||||
long long offset = file->tell();
|
||||
int version = headerVersion(file->readBlock(6));
|
||||
|
||||
// Next, we look for the descriptor.
|
||||
if(vers < 0) {
|
||||
if(version < 0) {
|
||||
offset = file->find("MAC ", offset);
|
||||
file->seek(offset);
|
||||
vers = headerVersion(file->readBlock(6));
|
||||
version = headerVersion(file->readBlock(6));
|
||||
}
|
||||
|
||||
if(vers < 0) {
|
||||
debug("APE::Properties::read() -- APE descriptor not found");
|
||||
if(version < 0) {
|
||||
debug("APE::AudioProperties::read() -- APE descriptor not found");
|
||||
return;
|
||||
}
|
||||
|
||||
d->version = vers;
|
||||
d->version = version;
|
||||
|
||||
if(d->version >= 3980)
|
||||
analyzeCurrent(file);
|
||||
@@ -143,17 +167,17 @@ void APE::Properties::read(File *file, offset_t streamLength)
|
||||
}
|
||||
}
|
||||
|
||||
void APE::Properties::analyzeCurrent(File *file)
|
||||
void APE::AudioProperties::analyzeCurrent(File *file)
|
||||
{
|
||||
// Read the descriptor
|
||||
file->seek(2, File::Current);
|
||||
const ByteVector descriptor = file->readBlock(44);
|
||||
if(descriptor.size() < 44) {
|
||||
debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
|
||||
debug("APE::AudioProperties::analyzeCurrent() -- descriptor is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int descriptorBytes = descriptor.toUInt(0, false);
|
||||
const unsigned int descriptorBytes = descriptor.toUInt32LE(0);
|
||||
|
||||
if((descriptorBytes - 52) > 0)
|
||||
file->seek(descriptorBytes - 52, File::Current);
|
||||
@@ -161,39 +185,39 @@ void APE::Properties::analyzeCurrent(File *file)
|
||||
// Read the header
|
||||
const ByteVector header = file->readBlock(24);
|
||||
if(header.size() < 24) {
|
||||
debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
|
||||
debug("APE::AudioProperties::analyzeCurrent() -- MAC header is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the APE info
|
||||
d->channels = header.toShort(18, false);
|
||||
d->sampleRate = header.toUInt(20, false);
|
||||
d->bitsPerSample = header.toShort(16, false);
|
||||
d->channels = header.toUInt16LE(18);
|
||||
d->sampleRate = header.toUInt32LE(20);
|
||||
d->bitsPerSample = header.toUInt16LE(16);
|
||||
|
||||
const unsigned int totalFrames = header.toUInt(12, false);
|
||||
const unsigned int totalFrames = header.toUInt32LE(12);
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
const unsigned int blocksPerFrame = header.toUInt(4, false);
|
||||
const unsigned int finalFrameBlocks = header.toUInt(8, false);
|
||||
const unsigned int blocksPerFrame = header.toUInt32LE(4);
|
||||
const unsigned int finalFrameBlocks = header.toUInt32LE(8);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
}
|
||||
|
||||
void APE::Properties::analyzeOld(File *file)
|
||||
void APE::AudioProperties::analyzeOld(File *file)
|
||||
{
|
||||
const ByteVector header = file->readBlock(26);
|
||||
if(header.size() < 26) {
|
||||
debug("APE::Properties::analyzeOld() -- MAC header is too short.");
|
||||
debug("APE::AudioProperties::analyzeOld() -- MAC header is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int totalFrames = header.toUInt(18, false);
|
||||
const unsigned int totalFrames = header.toUInt32LE(18);
|
||||
|
||||
// Fail on 0 length APE files (catches non-finalized APE files)
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
const short compressionLevel = header.toShort(0, false);
|
||||
const short compressionLevel = header.toUInt32LE(0);
|
||||
unsigned int blocksPerFrame;
|
||||
if(d->version >= 3950)
|
||||
blocksPerFrame = 73728 * 4;
|
||||
@@ -203,19 +227,19 @@ void APE::Properties::analyzeOld(File *file)
|
||||
blocksPerFrame = 9216;
|
||||
|
||||
// Get the APE info
|
||||
d->channels = header.toShort(4, false);
|
||||
d->sampleRate = header.toUInt(6, false);
|
||||
d->channels = header.toUInt16LE(4);
|
||||
d->sampleRate = header.toUInt32LE(6);
|
||||
|
||||
const unsigned int finalFrameBlocks = header.toUInt(22, false);
|
||||
const unsigned int finalFrameBlocks = header.toUInt32LE(22);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
|
||||
// Get the bit depth from the RIFF-fmt chunk.
|
||||
file->seek(16, File::Current);
|
||||
const ByteVector fmt = file->readBlock(28);
|
||||
if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
|
||||
debug("APE::Properties::analyzeOld() -- fmt header is too short.");
|
||||
debug("APE::AudioProperties::analyzeOld() -- fmt header is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
d->bitsPerSample = fmt.toShort(26, false);
|
||||
d->bitsPerSample = fmt.toUInt16LE(26);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#ifndef TAGLIB_APEPROPERTIES_H
|
||||
#define TAGLIB_APEPROPERTIES_H
|
||||
|
||||
#include "taglib.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
@@ -47,44 +46,59 @@ namespace TagLib {
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
* APE::File \a file.
|
||||
*/
|
||||
Properties(File *file, offset_t streamLength, ReadStyle style = Average);
|
||||
AudioProperties(File *file, long long streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this APE::Properties instance.
|
||||
* Destroys this APE::AudioProperties instance.
|
||||
*/
|
||||
~Properties() override;
|
||||
virtual ~AudioProperties();
|
||||
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
int lengthInMilliseconds() const override;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
int bitrate() const override;
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
int sampleRate() const override;
|
||||
virtual int sampleRate() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
int channels() const override;
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
@@ -102,16 +116,14 @@ namespace TagLib {
|
||||
int version() const;
|
||||
|
||||
private:
|
||||
void read(File *file, offset_t streamLength);
|
||||
|
||||
void read(File *file, long long streamLength);
|
||||
void analyzeCurrent(File *file);
|
||||
void analyzeOld(File *file);
|
||||
|
||||
class PropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -31,14 +31,15 @@
|
||||
#define WANT_CLASS_INSTANTIATION_OF_MAP (1)
|
||||
#endif
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tstring.h>
|
||||
#include <tmap.h>
|
||||
#include <tpicturemap.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tdebug.h>
|
||||
#include <tutils.h>
|
||||
|
||||
#include "apetag.h"
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "apefooter.h"
|
||||
#include "apeitem.h"
|
||||
|
||||
@@ -50,26 +51,37 @@ namespace
|
||||
const unsigned int MinKeyLength = 2;
|
||||
const unsigned int MaxKeyLength = 255;
|
||||
|
||||
const String FRONT_COVER("COVER ART (FRONT)");
|
||||
const String BACK_COVER("COVER ART (BACK)");
|
||||
|
||||
bool isKeyValid(const ByteVector &key)
|
||||
{
|
||||
static constexpr std::array invalidKeys { "ID3", "TAG", "OGGS", "MP+" };
|
||||
const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 };
|
||||
|
||||
// only allow printable ASCII including space (32..126)
|
||||
return std::none_of(key.begin(), key.end(),
|
||||
[](unsigned char c) { return c < 32 || c > 126; })
|
||||
&& std::none_of(invalidKeys.begin(), invalidKeys.end(),
|
||||
[upperKey = String(key).upper()](auto k) { return upperKey == k; });
|
||||
|
||||
for(ByteVector::ConstIterator it = key.begin(); it != key.end(); ++it) {
|
||||
const int c = static_cast<unsigned char>(*it);
|
||||
if(c < 32 || c > 126)
|
||||
return false;
|
||||
}
|
||||
|
||||
const String upperKey = String(key).upper();
|
||||
for(size_t i = 0; invalidKeys[i] != 0; ++i) {
|
||||
if(upperKey == invalidKeys[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
}
|
||||
|
||||
class APE::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
File *file { nullptr };
|
||||
offset_t footerLocation { 0 };
|
||||
TagPrivate() :
|
||||
file(0),
|
||||
footerLocation(0) {}
|
||||
|
||||
File *file;
|
||||
long long footerLocation;
|
||||
|
||||
Footer footer;
|
||||
ItemListMap itemListMap;
|
||||
@@ -80,12 +92,14 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Tag::Tag() :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
APE::Tag::Tag(TagLib::File *file, offset_t footerLocation) :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
APE::Tag::Tag(TagLib::File *file, long long footerLocation) :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d->file = file;
|
||||
d->footerLocation = footerLocation;
|
||||
@@ -93,7 +107,10 @@ APE::Tag::Tag(TagLib::File *file, offset_t footerLocation) :
|
||||
read();
|
||||
}
|
||||
|
||||
APE::Tag::~Tag() = default;
|
||||
APE::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
ByteVector APE::Tag::fileIdentifier()
|
||||
{
|
||||
@@ -102,44 +119,87 @@ ByteVector APE::Tag::fileIdentifier()
|
||||
|
||||
String APE::Tag::title() const
|
||||
{
|
||||
Item val = d->itemListMap.value("TITLE");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
if(d->itemListMap["TITLE"].isEmpty())
|
||||
return String();
|
||||
return d->itemListMap["TITLE"].values().toString();
|
||||
}
|
||||
|
||||
String APE::Tag::artist() const
|
||||
{
|
||||
Item val = d->itemListMap.value("ARTIST");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
if(d->itemListMap["ARTIST"].isEmpty())
|
||||
return String();
|
||||
return d->itemListMap["ARTIST"].values().toString();
|
||||
}
|
||||
|
||||
String APE::Tag::album() const
|
||||
{
|
||||
Item val = d->itemListMap.value("ALBUM");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
if(d->itemListMap["ALBUM"].isEmpty())
|
||||
return String();
|
||||
return d->itemListMap["ALBUM"].values().toString();
|
||||
}
|
||||
|
||||
String APE::Tag::comment() const
|
||||
{
|
||||
Item val = d->itemListMap.value("COMMENT");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
if(d->itemListMap["COMMENT"].isEmpty())
|
||||
return String();
|
||||
return d->itemListMap["COMMENT"].values().toString();
|
||||
}
|
||||
|
||||
String APE::Tag::genre() const
|
||||
{
|
||||
Item val = d->itemListMap.value("GENRE");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
if(d->itemListMap["GENRE"].isEmpty())
|
||||
return String();
|
||||
return d->itemListMap["GENRE"].values().toString();
|
||||
}
|
||||
|
||||
unsigned int APE::Tag::year() const
|
||||
{
|
||||
Item val = d->itemListMap.value("YEAR");
|
||||
return val.isEmpty() ? 0 : val.toString().toInt();
|
||||
if(d->itemListMap["YEAR"].isEmpty())
|
||||
return 0;
|
||||
return d->itemListMap["YEAR"].toString().toInt();
|
||||
}
|
||||
|
||||
unsigned int APE::Tag::track() const
|
||||
{
|
||||
Item val = d->itemListMap.value("TRACK");
|
||||
return val.isEmpty() ? 0 : val.toString().toInt();
|
||||
if(d->itemListMap["TRACK"].isEmpty())
|
||||
return 0;
|
||||
return d->itemListMap["TRACK"].toString().toInt();
|
||||
}
|
||||
|
||||
TagLib::PictureMap APE::Tag::pictures() const
|
||||
{
|
||||
PictureMap map;
|
||||
if(d->itemListMap.contains(FRONT_COVER)) {
|
||||
Item front = d->itemListMap[FRONT_COVER];
|
||||
if(Item::Binary == front.type()) {
|
||||
ByteVector picture = front.binaryData();
|
||||
const size_t index = picture.find('\0');
|
||||
if(index < picture.size()) {
|
||||
ByteVector desc = picture.mid(0, index + 1);
|
||||
String mime = "image/jpeg";
|
||||
ByteVector data = picture.mid(index + 1);
|
||||
Picture p(data, Picture::FrontCover, mime, desc);
|
||||
map.insert(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(d->itemListMap.contains(BACK_COVER)) {
|
||||
Item back = d->itemListMap[BACK_COVER];
|
||||
if(Item::Binary == back.type()) {
|
||||
ByteVector picture = back.binaryData();
|
||||
const size_t index = picture.find('\0');
|
||||
if(index < picture.size()) {
|
||||
ByteVector desc = picture.mid(0, index + 1);
|
||||
String mime = "image/jpeg";
|
||||
ByteVector data = picture.mid(index + 1);
|
||||
Picture p(data, Picture::BackCover, mime, desc);
|
||||
map.insert(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PictureMap(map);
|
||||
}
|
||||
|
||||
void APE::Tag::setTitle(const String &s)
|
||||
@@ -183,39 +243,94 @@ void APE::Tag::setTrack(unsigned int i)
|
||||
addValue("TRACK", String::number(i), true);
|
||||
}
|
||||
|
||||
void APE::Tag::setPictures(const PictureMap &l)
|
||||
{
|
||||
removeItem(FRONT_COVER);
|
||||
removeItem(BACK_COVER);
|
||||
for(PictureMap::ConstIterator pictureMapIt = l.begin();
|
||||
pictureMapIt != l.end();
|
||||
++pictureMapIt) {
|
||||
Picture::Type type = pictureMapIt->first;
|
||||
if(Picture::FrontCover != type && Picture::BackCover != type) {
|
||||
std::cout << "APE: Trying to add a picture with wrong type"
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *id;
|
||||
switch(type) {
|
||||
case Picture::FrontCover:
|
||||
id = FRONT_COVER;
|
||||
break;
|
||||
case Picture::BackCover:
|
||||
id = BACK_COVER;
|
||||
break;
|
||||
default:
|
||||
id = FRONT_COVER;
|
||||
break;
|
||||
}
|
||||
|
||||
PictureList list = pictureMapIt->second;
|
||||
for(PictureList::ConstIterator pictureListIt = list.begin();
|
||||
pictureListIt != list.end();
|
||||
++pictureListIt) {
|
||||
Picture picture = *pictureListIt;
|
||||
if(d->itemListMap.contains(id)) {
|
||||
std::cout << "APE: Already added a picture of type "
|
||||
<< id
|
||||
<< " '"
|
||||
<< picture.description()
|
||||
<< "' "
|
||||
<< "and next are being ignored"
|
||||
<< std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
ByteVector data = picture.description().data(String::Latin1)
|
||||
.append('\0')
|
||||
.append(picture.data());
|
||||
|
||||
Item item;
|
||||
item.setKey(id);
|
||||
item.setType(Item::Binary);
|
||||
item.setBinaryData(data);
|
||||
setItem(item.key(), item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// conversions of tag keys between what we use in PropertyMap and what's usual
|
||||
// for APE tags
|
||||
// usual, APE
|
||||
constexpr std::array keyConversions {
|
||||
std::pair("TRACKNUMBER", "TRACK"),
|
||||
std::pair("DATE", "YEAR"),
|
||||
std::pair("ALBUMARTIST", "ALBUM ARTIST"),
|
||||
std::pair("DISCNUMBER", "DISC"),
|
||||
std::pair("REMIXER", "MIXARTIST"),
|
||||
std::pair("RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS"),
|
||||
std::pair("RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE"),
|
||||
};
|
||||
} // namespace
|
||||
// usual, APE
|
||||
const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
|
||||
{"DATE", "YEAR" },
|
||||
{"ALBUMARTIST", "ALBUM ARTIST"},
|
||||
{"DISCNUMBER", "DISC" },
|
||||
{"REMIXER", "MIXARTIST" }};
|
||||
const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]);
|
||||
}
|
||||
|
||||
PropertyMap APE::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
for(const auto &[tag, item] : std::as_const(itemListMap())) {
|
||||
String tagName = tag.upper();
|
||||
ItemListMap::ConstIterator it = itemListMap().begin();
|
||||
for(; it != itemListMap().end(); ++it) {
|
||||
String tagName = it->first.upper();
|
||||
// if the item is Binary or Locator, or if the key is an invalid string,
|
||||
// add to unsupportedData
|
||||
if(item.type() != Item::Text || tagName.isEmpty()) {
|
||||
properties.addUnsupportedData(tag);
|
||||
if(it->second.type() != Item::Text || tagName.isEmpty()) {
|
||||
properties.unsupportedData().append(it->first);
|
||||
}
|
||||
else {
|
||||
// Some tags need to be handled specially
|
||||
for(const auto &[k, t] : keyConversions) {
|
||||
if(tagName == t)
|
||||
tagName = k;
|
||||
for(size_t i = 0; i < keyConversionsSize; ++i) {
|
||||
if(tagName == keyConversions[i][1])
|
||||
tagName = keyConversions[i][0];
|
||||
}
|
||||
properties[tagName].append(item.values());
|
||||
properties[tagName].append(it->second.values());
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
@@ -223,141 +338,57 @@ PropertyMap APE::Tag::properties() const
|
||||
|
||||
void APE::Tag::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
for(const auto &property : properties)
|
||||
removeItem(property);
|
||||
StringList::ConstIterator it = properties.begin();
|
||||
for(; it != properties.end(); ++it)
|
||||
removeItem(*it);
|
||||
}
|
||||
|
||||
PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap props(origProps); // make a local copy that can be modified
|
||||
PropertyMap properties(origProps); // make a local copy that can be modified
|
||||
|
||||
// see comment in properties()
|
||||
for(const auto &[k, t] : keyConversions)
|
||||
if(props.contains(k)) {
|
||||
props.insert(t, props[k]);
|
||||
props.erase(k);
|
||||
for(size_t i = 0; i < keyConversionsSize; ++i)
|
||||
if(properties.contains(keyConversions[i][0])) {
|
||||
properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]);
|
||||
properties.erase(keyConversions[i][0]);
|
||||
}
|
||||
|
||||
// first check if tags need to be removed completely
|
||||
StringList toRemove;
|
||||
for(const auto &[k, t] : std::as_const(itemListMap())) {
|
||||
String key = k.upper();
|
||||
ItemListMap::ConstIterator remIt = itemListMap().begin();
|
||||
for(; remIt != itemListMap().end(); ++remIt) {
|
||||
String key = remIt->first.upper();
|
||||
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
|
||||
if(!key.isEmpty() && t.type() == APE::Item::Text && !props.contains(key))
|
||||
toRemove.append(k);
|
||||
if(!key.isEmpty() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
|
||||
toRemove.append(remIt->first);
|
||||
}
|
||||
|
||||
for(const auto &item : std::as_const(toRemove))
|
||||
removeItem(item);
|
||||
for(StringList::ConstIterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
|
||||
removeItem(*removeIt);
|
||||
|
||||
// now sync in the "forward direction"
|
||||
PropertyMap::ConstIterator it = properties.begin();
|
||||
PropertyMap invalid;
|
||||
for(const auto &[tagName, val] : std::as_const(props)) {
|
||||
for(; it != properties.end(); ++it) {
|
||||
const String &tagName = it->first;
|
||||
if(!checkKey(tagName))
|
||||
invalid.insert(tagName, val);
|
||||
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == val)) {
|
||||
if(val.isEmpty())
|
||||
invalid.insert(it->first, it->second);
|
||||
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
||||
if(it->second.isEmpty())
|
||||
removeItem(tagName);
|
||||
else {
|
||||
addValue(tagName, *val.begin(), true);
|
||||
for(auto it = std::next(val.begin()); it != val.end(); ++it)
|
||||
addValue(tagName, *it, false);
|
||||
StringList::ConstIterator valueIt = it->second.begin();
|
||||
addValue(tagName, *valueIt, true);
|
||||
++valueIt;
|
||||
for(; valueIt != it->second.end(); ++valueIt)
|
||||
addValue(tagName, *valueIt, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
|
||||
StringList APE::Tag::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys;
|
||||
if(d->itemListMap.contains(FRONT_COVER) ||
|
||||
d->itemListMap.contains(BACK_COVER)) {
|
||||
keys.append("PICTURE");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> APE::Tag::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props;
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
const StringList itemNames = StringList(FRONT_COVER).append(BACK_COVER);
|
||||
for(const auto &itemName: itemNames) {
|
||||
if(d->itemListMap.contains(itemName)) {
|
||||
Item picture = d->itemListMap.value(itemName);
|
||||
if(picture.type() == Item::Binary) {
|
||||
ByteVector data = picture.binaryData();
|
||||
// Do not search for a description if the first byte could start JPG or PNG
|
||||
// data.
|
||||
int index = data.isEmpty() || data.at(0) == '\xff' || data.at(0) == '\x89'
|
||||
? -1 : data.find('\0');
|
||||
String description;
|
||||
if(index >= 0) {
|
||||
description = String(data.mid(0, index), String::UTF8);
|
||||
data = data.mid(index + 1);
|
||||
}
|
||||
|
||||
VariantMap property;
|
||||
property.insert("data", data);
|
||||
if(!description.isEmpty()) {
|
||||
property.insert("description", description);
|
||||
}
|
||||
property.insert("pictureType",
|
||||
itemName == BACK_COVER ? "Back Cover" : "Front Cover");
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
bool APE::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
removeItem(FRONT_COVER);
|
||||
removeItem(BACK_COVER);
|
||||
|
||||
auto frontItems = List<Item>();
|
||||
auto backItems = List<Item>();
|
||||
for(const auto &property : value) {
|
||||
auto data = property.value("description").value<String>().data(String::UTF8)
|
||||
.append('\0')
|
||||
.append(property.value("data").value<ByteVector>());
|
||||
auto pictureType = property.value("pictureType").value<String>();
|
||||
Item item;
|
||||
item.setType(Item::Binary);
|
||||
item.setBinaryData(data);
|
||||
if(pictureType == "Back Cover") {
|
||||
item.setKey(BACK_COVER);
|
||||
backItems.append(item);
|
||||
}
|
||||
else if(pictureType == "Front Cover") {
|
||||
item.setKey(FRONT_COVER);
|
||||
// prioritize pictures with correct type
|
||||
frontItems.prepend(item);
|
||||
}
|
||||
else {
|
||||
item.setKey(FRONT_COVER);
|
||||
frontItems.append(item);
|
||||
}
|
||||
}
|
||||
if(!frontItems.isEmpty()) {
|
||||
setItem(FRONT_COVER, frontItems.front());
|
||||
}
|
||||
if(!backItems.isEmpty()) {
|
||||
setItem(BACK_COVER, backItems.front());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APE::Tag::checkKey(const String &key)
|
||||
{
|
||||
if(key.size() < MinKeyLength || key.size() > MaxKeyLength)
|
||||
@@ -392,7 +423,7 @@ void APE::Tag::addValue(const String &key, const String &value, bool replace)
|
||||
// Text items may contain more than one value.
|
||||
// Binary or locator items may have only one value, hence always replaced.
|
||||
|
||||
auto it = d->itemListMap.find(key.upper());
|
||||
ItemListMap::Iterator it = d->itemListMap.find(key.upper());
|
||||
|
||||
if(it != d->itemListMap.end() && it->second.type() == Item::Text)
|
||||
it->second.appendValue(value);
|
||||
@@ -450,13 +481,13 @@ ByteVector APE::Tag::render() const
|
||||
ByteVector data;
|
||||
unsigned int itemCount = 0;
|
||||
|
||||
for(const auto &[_, list] : std::as_const(d->itemListMap)) {
|
||||
data.append(list.render());
|
||||
for(ItemListMap::ConstIterator it = d->itemListMap.begin(); it != d->itemListMap.end(); ++it) {
|
||||
data.append(it->second.render());
|
||||
itemCount++;
|
||||
}
|
||||
|
||||
d->footer.setItemCount(itemCount);
|
||||
d->footer.setTagSize(data.size() + Footer::size());
|
||||
d->footer.setTagSize(static_cast<unsigned int>(data.size() + Footer::size()));
|
||||
d->footer.setHeaderPresent(true);
|
||||
|
||||
return d->footer.renderHeader() + data + d->footer.renderFooter();
|
||||
@@ -469,23 +500,18 @@ void APE::Tag::parse(const ByteVector &data)
|
||||
if(data.size() < 11)
|
||||
return;
|
||||
|
||||
unsigned int pos = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
|
||||
|
||||
const int nullPos = data.find('\0', pos + 8);
|
||||
if(nullPos < 0) {
|
||||
const size_t nullPos = data.find('\0', pos + 8);
|
||||
if(nullPos == ByteVector::npos()) {
|
||||
debug("APE::Tag::parse() - Couldn't find a key/value separator. Stopped parsing.");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int keyLength = nullPos - pos - 8;
|
||||
const unsigned int valLength = data.toUInt(pos, false);
|
||||
|
||||
if(valLength >= data.size() || pos > data.size() - valLength) {
|
||||
debug("APE::Tag::parse() - Invalid val length. Stopped parsing.");
|
||||
return;
|
||||
}
|
||||
const size_t keyLength = nullPos - pos - 8;
|
||||
const size_t valLegnth = data.toUInt32LE(pos);
|
||||
|
||||
if(keyLength >= MinKeyLength
|
||||
&& keyLength <= MaxKeyLength
|
||||
@@ -500,6 +526,6 @@ void APE::Tag::parse(const ByteVector &data)
|
||||
debug("APE::Tag::parse() - Skipped an item due to an invalid key.");
|
||||
}
|
||||
|
||||
pos += keyLength + valLength + 9;
|
||||
pos += keyLength + valLegnth + 9;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +26,17 @@
|
||||
#ifndef TAGLIB_APETAG_H
|
||||
#define TAGLIB_APETAG_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tmap.h"
|
||||
#include "tstring.h"
|
||||
#include "taglib_export.h"
|
||||
#include "tag.h"
|
||||
|
||||
#include "apeitem.h"
|
||||
|
||||
#define FRONT_COVER "COVER ART (FRONT)"
|
||||
#define BACK_COVER "COVER ART (BACK)"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
class File;
|
||||
@@ -48,7 +52,8 @@ namespace TagLib {
|
||||
*
|
||||
* \see APE::Tag::itemListMap()
|
||||
*/
|
||||
using ItemListMap = Map<const String, Item>;
|
||||
typedef Map<String, Item> ItemListMap;
|
||||
|
||||
|
||||
//! An APE tag implementation
|
||||
|
||||
@@ -64,15 +69,12 @@ namespace TagLib {
|
||||
* Create an APE tag and parse the data in \a file with APE footer at
|
||||
* \a tagOffset.
|
||||
*/
|
||||
Tag(TagLib::File *file, offset_t footerLocation);
|
||||
Tag(TagLib::File *file, long long footerLocation);
|
||||
|
||||
/*!
|
||||
* Destroys this Tag instance.
|
||||
*/
|
||||
~Tag() override;
|
||||
|
||||
Tag(const Tag &) = delete;
|
||||
Tag &operator=(const Tag &) = delete;
|
||||
virtual ~Tag();
|
||||
|
||||
/*!
|
||||
* Renders the in memory values to a ByteVector suitable for writing to
|
||||
@@ -88,21 +90,31 @@ namespace TagLib {
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
String title() const override;
|
||||
String artist() const override;
|
||||
String album() const override;
|
||||
String comment() const override;
|
||||
String genre() const override;
|
||||
unsigned int year() const override;
|
||||
unsigned int track() const override;
|
||||
virtual String title() const;
|
||||
virtual String artist() const;
|
||||
virtual String album() const;
|
||||
virtual String comment() const;
|
||||
virtual String genre() const;
|
||||
virtual unsigned int year() const;
|
||||
virtual unsigned int track() const;
|
||||
|
||||
void setTitle(const String &s) override;
|
||||
void setArtist(const String &s) override;
|
||||
void setAlbum(const String &s) override;
|
||||
void setComment(const String &s) override;
|
||||
void setGenre(const String &s) override;
|
||||
void setYear(unsigned int i) override;
|
||||
void setTrack(unsigned int i) override;
|
||||
/**
|
||||
* @brief pictures
|
||||
* According to :
|
||||
* http://www.hydrogenaud.io/forums/index.php?showtopic=40603&st=50&p=504669&#entry504669
|
||||
* http://git.videolan.org/?p=vlc.git;a=blob;f=modules/meta_engine/taglib.cpp
|
||||
* @return
|
||||
*/
|
||||
virtual PictureMap pictures() 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(unsigned int i);
|
||||
virtual void setTrack(unsigned int i);
|
||||
virtual void setPictures(const PictureMap &l);
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- export function.
|
||||
@@ -118,9 +130,9 @@ namespace TagLib {
|
||||
* TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively,
|
||||
* in order to be compliant with the names used in other formats.
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
PropertyMap properties() const;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function. The same
|
||||
@@ -128,11 +140,7 @@ namespace TagLib {
|
||||
* specification requires keys to have between 2 and 16 printable ASCII characters
|
||||
* with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+".
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
StringList complexPropertyKeys() const override;
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Check if the given String is a valid APE tag key.
|
||||
@@ -186,7 +194,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns true if the tag does not contain any data.
|
||||
*/
|
||||
bool isEmpty() const override;
|
||||
bool isEmpty() const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -201,11 +209,13 @@ namespace TagLib {
|
||||
void parse(const ByteVector &data);
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
TagPrivate *d;
|
||||
};
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**************************************************************************
|
||||
/**************************************************************************
|
||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
**************************************************************************/
|
||||
@@ -23,29 +23,45 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct AttributeData
|
||||
{
|
||||
AttributeData() :
|
||||
numericValue(0),
|
||||
stream(0),
|
||||
language(0) {}
|
||||
|
||||
ASF::Attribute::AttributeTypes type;
|
||||
String stringValue;
|
||||
ByteVector byteVectorValue;
|
||||
ASF::Picture pictureValue;
|
||||
unsigned long long numericValue;
|
||||
int stream;
|
||||
int language;
|
||||
};
|
||||
}
|
||||
|
||||
class ASF::Attribute::AttributePrivate
|
||||
{
|
||||
public:
|
||||
AttributePrivate() :
|
||||
pictureValue(ASF::Picture::fromInvalid())
|
||||
data(new AttributeData())
|
||||
{
|
||||
data->pictureValue = ASF::Picture::fromInvalid();
|
||||
}
|
||||
AttributeTypes type;
|
||||
String stringValue;
|
||||
ByteVector byteVectorValue;
|
||||
ASF::Picture pictureValue;
|
||||
unsigned long long numericValue { 0 };
|
||||
int stream { 0 };
|
||||
int language { 0 };
|
||||
|
||||
SHARED_PTR<AttributeData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -53,125 +69,136 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Attribute::Attribute() :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = UnicodeType;
|
||||
d->data->type = UnicodeType;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ASF::Attribute &) = default;
|
||||
ASF::Attribute::Attribute(const ASF::Attribute &other) :
|
||||
d(new AttributePrivate(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const String &value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = UnicodeType;
|
||||
d->stringValue = value;
|
||||
d->data->type = UnicodeType;
|
||||
d->data->stringValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ByteVector &value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = BytesType;
|
||||
d->byteVectorValue = value;
|
||||
d->data->type = BytesType;
|
||||
d->data->byteVectorValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ASF::Picture &value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = BytesType;
|
||||
d->pictureValue = value;
|
||||
d->data->type = BytesType;
|
||||
d->data->pictureValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned int value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = DWordType;
|
||||
d->numericValue = value;
|
||||
d->data->type = DWordType;
|
||||
d->data->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned long long value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = QWordType;
|
||||
d->numericValue = value;
|
||||
d->data->type = QWordType;
|
||||
d->data->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned short value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = WordType;
|
||||
d->numericValue = value;
|
||||
d->data->type = WordType;
|
||||
d->data->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(bool value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = BoolType;
|
||||
d->numericValue = value;
|
||||
d->data->type = BoolType;
|
||||
d->data->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &) = default;
|
||||
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
|
||||
{
|
||||
Attribute(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ASF::Attribute::swap(Attribute &other) noexcept
|
||||
void ASF::Attribute::swap(Attribute &other)
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
ASF::Attribute::~Attribute() = default;
|
||||
ASF::Attribute::~Attribute()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
|
||||
{
|
||||
return d->type;
|
||||
return d->data->type;
|
||||
}
|
||||
|
||||
String ASF::Attribute::toString() const
|
||||
{
|
||||
return d->stringValue;
|
||||
return d->data->stringValue;
|
||||
}
|
||||
|
||||
ByteVector ASF::Attribute::toByteVector() const
|
||||
{
|
||||
if(d->pictureValue.isValid())
|
||||
return d->pictureValue.render();
|
||||
return d->byteVectorValue;
|
||||
if(d->data->pictureValue.isValid())
|
||||
return d->data->pictureValue.render();
|
||||
|
||||
return d->data->byteVectorValue;
|
||||
}
|
||||
|
||||
unsigned short ASF::Attribute::toBool() const
|
||||
{
|
||||
return d->numericValue ? 1 : 0;
|
||||
return d->data->numericValue ? 1 : 0;
|
||||
}
|
||||
|
||||
unsigned short ASF::Attribute::toUShort() const
|
||||
{
|
||||
return static_cast<unsigned short>(d->numericValue);
|
||||
return static_cast<unsigned short>(d->data->numericValue);
|
||||
}
|
||||
|
||||
unsigned int ASF::Attribute::toUInt() const
|
||||
{
|
||||
return static_cast<unsigned int>(d->numericValue);
|
||||
return static_cast<unsigned int>(d->data->numericValue);
|
||||
}
|
||||
|
||||
unsigned long long ASF::Attribute::toULongLong() const
|
||||
{
|
||||
return static_cast<unsigned long long>(d->numericValue);
|
||||
return static_cast<unsigned long long>(d->data->numericValue);
|
||||
}
|
||||
|
||||
ASF::Picture ASF::Attribute::toPicture() const
|
||||
{
|
||||
return d->pictureValue;
|
||||
return d->data->pictureValue;
|
||||
}
|
||||
|
||||
String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
{
|
||||
unsigned int size, nameLength;
|
||||
String name;
|
||||
d->pictureValue = Picture::fromInvalid();
|
||||
d->data->pictureValue = Picture::fromInvalid();
|
||||
// extended content descriptor
|
||||
if(kind == 0) {
|
||||
nameLength = readWORD(&f);
|
||||
name = readString(&f, nameLength);
|
||||
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&f));
|
||||
d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
size = readWORD(&f);
|
||||
}
|
||||
// metadata & metadata library
|
||||
@@ -179,11 +206,11 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
int temp = readWORD(&f);
|
||||
// metadata library
|
||||
if(kind == 2) {
|
||||
d->language = temp;
|
||||
d->data->language = temp;
|
||||
}
|
||||
d->stream = readWORD(&f);
|
||||
d->data->stream = readWORD(&f);
|
||||
nameLength = readWORD(&f);
|
||||
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&f));
|
||||
d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
size = readDWORD(&f);
|
||||
name = readString(&f, nameLength);
|
||||
}
|
||||
@@ -192,42 +219,42 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
debug("ASF::Attribute::parse() -- Value larger than 64kB");
|
||||
}
|
||||
|
||||
switch(d->type) {
|
||||
switch(d->data->type) {
|
||||
case WordType:
|
||||
d->numericValue = readWORD(&f);
|
||||
d->data->numericValue = readWORD(&f);
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
d->numericValue = (readDWORD(&f) != 0);
|
||||
d->data->numericValue = (readDWORD(&f) != 0);
|
||||
}
|
||||
else {
|
||||
d->numericValue = (readWORD(&f) != 0);
|
||||
d->data->numericValue = (readWORD(&f) != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
d->numericValue = readDWORD(&f);
|
||||
d->data->numericValue = readDWORD(&f);
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
d->numericValue = readQWORD(&f);
|
||||
d->data->numericValue = readQWORD(&f);
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
d->stringValue = readString(&f, size);
|
||||
d->data->stringValue = readString(&f, size);
|
||||
break;
|
||||
|
||||
case BytesType:
|
||||
case GuidType:
|
||||
d->byteVectorValue = f.readBlock(size);
|
||||
d->data->byteVectorValue = f.readBlock(size);
|
||||
break;
|
||||
}
|
||||
|
||||
if(d->type == BytesType && name == "WM/Picture") {
|
||||
d->pictureValue.parse(d->byteVectorValue);
|
||||
if(d->pictureValue.isValid()) {
|
||||
d->byteVectorValue.clear();
|
||||
if(d->data->type == BytesType && name == "WM/Picture") {
|
||||
d->data->pictureValue.parse(d->data->byteVectorValue);
|
||||
if(d->data->pictureValue.isValid()) {
|
||||
d->data->byteVectorValue.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +263,7 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
|
||||
int ASF::Attribute::dataSize() const
|
||||
{
|
||||
switch (d->type) {
|
||||
switch (d->data->type) {
|
||||
case WordType:
|
||||
return 2;
|
||||
case BoolType:
|
||||
@@ -246,14 +273,12 @@ int ASF::Attribute::dataSize() const
|
||||
case QWordType:
|
||||
return 5;
|
||||
case UnicodeType:
|
||||
return d->stringValue.size() * 2 + 2;
|
||||
return static_cast<int>(d->data->stringValue.size() * 2 + 2);
|
||||
case BytesType:
|
||||
if(d->pictureValue.isValid()) {
|
||||
return d->pictureValue.dataSize();
|
||||
}
|
||||
return d->byteVectorValue.size();
|
||||
if(d->data->pictureValue.isValid())
|
||||
return d->data->pictureValue.dataSize();
|
||||
case GuidType:
|
||||
return d->byteVectorValue.size();
|
||||
return static_cast<int>(d->data->byteVectorValue.size());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -262,58 +287,55 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
{
|
||||
ByteVector data;
|
||||
|
||||
switch (d->type) {
|
||||
switch (d->data->type) {
|
||||
case WordType:
|
||||
data.append(ByteVector::fromShort(toUShort(), false));
|
||||
data.append(ByteVector::fromUInt16LE(toUShort()));
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
data.append(ByteVector::fromUInt(toBool(), false));
|
||||
data.append(ByteVector::fromUInt32LE(toBool()));
|
||||
}
|
||||
else {
|
||||
data.append(ByteVector::fromShort(toBool(), false));
|
||||
data.append(ByteVector::fromUInt16LE(toBool()));
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
data.append(ByteVector::fromUInt(toUInt(), false));
|
||||
data.append(ByteVector::fromUInt32LE(toUInt()));
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
data.append(ByteVector::fromLongLong(toULongLong(), false));
|
||||
data.append(ByteVector::fromUInt64LE(toULongLong()));
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
data.append(renderString(d->stringValue));
|
||||
data.append(renderString(d->data->stringValue));
|
||||
break;
|
||||
|
||||
case BytesType:
|
||||
if(d->pictureValue.isValid()) {
|
||||
data.append(d->pictureValue.render());
|
||||
if(d->data->pictureValue.isValid()) {
|
||||
data.append(d->data->pictureValue.render());
|
||||
break;
|
||||
}
|
||||
else {
|
||||
data.append(d->byteVectorValue);
|
||||
}
|
||||
break;
|
||||
case GuidType:
|
||||
data.append(d->byteVectorValue);
|
||||
data.append(d->data->byteVectorValue);
|
||||
break;
|
||||
}
|
||||
|
||||
if(kind == 0) {
|
||||
data = renderString(name, true) +
|
||||
ByteVector::fromShort(static_cast<int>(d->type), false) +
|
||||
ByteVector::fromShort(data.size(), false) +
|
||||
ByteVector::fromUInt16LE((int)d->data->type) +
|
||||
ByteVector::fromUInt16LE(data.size()) +
|
||||
data;
|
||||
}
|
||||
else {
|
||||
ByteVector nameData = renderString(name);
|
||||
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
|
||||
ByteVector::fromShort(d->stream, false) +
|
||||
ByteVector::fromShort(nameData.size(), false) +
|
||||
ByteVector::fromShort(static_cast<int>(d->type), false) +
|
||||
ByteVector::fromUInt(data.size(), false) +
|
||||
data = ByteVector::fromUInt16LE(kind == 2 ? d->data->language : 0) +
|
||||
ByteVector::fromUInt16LE(d->data->stream) +
|
||||
ByteVector::fromUInt16LE(nameData.size()) +
|
||||
ByteVector::fromUInt16LE((int)d->data->type) +
|
||||
ByteVector::fromUInt32LE(data.size()) +
|
||||
nameData +
|
||||
data;
|
||||
}
|
||||
@@ -323,20 +345,20 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
|
||||
int ASF::Attribute::language() const
|
||||
{
|
||||
return d->language;
|
||||
return d->data->language;
|
||||
}
|
||||
|
||||
void ASF::Attribute::setLanguage(int value)
|
||||
{
|
||||
d->language = value;
|
||||
d->data->language = value;
|
||||
}
|
||||
|
||||
int ASF::Attribute::stream() const
|
||||
{
|
||||
return d->stream;
|
||||
return d->data->stream;
|
||||
}
|
||||
|
||||
void ASF::Attribute::setStream(int value)
|
||||
{
|
||||
d->stream = value;
|
||||
d->data->stream = value;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
|
||||
namespace ASF
|
||||
{
|
||||
class File;
|
||||
@@ -106,7 +107,7 @@ namespace TagLib
|
||||
/*!
|
||||
* Construct an attribute as a copy of \a other.
|
||||
*/
|
||||
Attribute(const Attribute &other);
|
||||
Attribute(const Attribute &item);
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this item.
|
||||
@@ -116,7 +117,7 @@ namespace TagLib
|
||||
/*!
|
||||
* Exchanges the content of the Attribute by the content of \a other.
|
||||
*/
|
||||
void swap(Attribute &other) noexcept;
|
||||
void swap(Attribute &other);
|
||||
|
||||
/*!
|
||||
* Destroys the attribute.
|
||||
@@ -183,24 +184,20 @@ namespace TagLib
|
||||
*/
|
||||
void setStream(int value);
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* THIS IS PRIVATE, DON'T TOUCH IT! */
|
||||
String parse(ASF::File &file, int kind = 0);
|
||||
#endif
|
||||
|
||||
//! Returns the size of the stored data
|
||||
int dataSize() const;
|
||||
|
||||
private:
|
||||
friend class File;
|
||||
|
||||
String parse(ASF::File &file, int kind = 0);
|
||||
ByteVector render(const String &name, int kind = 0) const;
|
||||
|
||||
class AttributePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<AttributePrivate> d;
|
||||
AttributePrivate *d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,14 +23,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
#include <tagutils.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "asffile.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tbytevectorlist.h"
|
||||
#include "tagutils.h"
|
||||
#include "asftag.h"
|
||||
#include "asfproperties.h"
|
||||
#include "asfutils.h"
|
||||
@@ -51,28 +51,24 @@ public:
|
||||
class MetadataObject;
|
||||
class MetadataLibraryObject;
|
||||
|
||||
FilePrivate()
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
typedef List<SHARED_PTR<BaseObject> > ObjectList;
|
||||
typedef ObjectList::ConstIterator ObjectConstIterator;
|
||||
|
||||
~FilePrivate() = default;
|
||||
FilePrivate():
|
||||
headerSize(0) {}
|
||||
|
||||
FilePrivate(const FilePrivate &) = delete;
|
||||
FilePrivate &operator=(const FilePrivate &) = delete;
|
||||
unsigned long long headerSize;
|
||||
|
||||
unsigned long long headerSize { 0 };
|
||||
SCOPED_PTR<ASF::Tag> tag;
|
||||
SCOPED_PTR<ASF::AudioProperties> properties;
|
||||
|
||||
std::unique_ptr<ASF::Tag> tag;
|
||||
std::unique_ptr<ASF::Properties> properties;
|
||||
ObjectList objects;
|
||||
|
||||
List<BaseObject *> objects;
|
||||
|
||||
ContentDescriptionObject *contentDescriptionObject { nullptr };
|
||||
ExtendedContentDescriptionObject *extendedContentDescriptionObject { nullptr };
|
||||
HeaderExtensionObject *headerExtensionObject { nullptr };
|
||||
MetadataObject *metadataObject { nullptr };
|
||||
MetadataLibraryObject *metadataLibraryObject { nullptr };
|
||||
SHARED_PTR<ContentDescriptionObject> contentDescriptionObject;
|
||||
SHARED_PTR<ExtendedContentDescriptionObject> extendedContentDescriptionObject;
|
||||
SHARED_PTR<HeaderExtensionObject> headerExtensionObject;
|
||||
SHARED_PTR<MetadataObject> metadataObject;
|
||||
SHARED_PTR<MetadataLibraryObject> metadataLibraryObject;
|
||||
};
|
||||
|
||||
namespace
|
||||
@@ -89,15 +85,15 @@ namespace
|
||||
const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
|
||||
const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
|
||||
const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
|
||||
} // namespace
|
||||
}
|
||||
|
||||
class ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector data;
|
||||
virtual ~BaseObject() = default;
|
||||
virtual ~BaseObject() {}
|
||||
virtual ByteVector guid() const = 0;
|
||||
virtual void parse(ASF::File *file, long long size);
|
||||
virtual void parse(ASF::File *file, unsigned int size);
|
||||
virtual ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
@@ -105,74 +101,74 @@ class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::Bas
|
||||
{
|
||||
ByteVector myGuid;
|
||||
public:
|
||||
UnknownObject(const ByteVector &guid);
|
||||
ByteVector guid() const override;
|
||||
explicit UnknownObject(const ByteVector &guid);
|
||||
ByteVector guid() const;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
List<ASF::File::FilePrivate::BaseObject *> objects;
|
||||
ObjectList objects;
|
||||
HeaderExtensionObject();
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
|
||||
private:
|
||||
enum CodecType
|
||||
@@ -183,10 +179,10 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, long long size)
|
||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
|
||||
{
|
||||
data.clear();
|
||||
if(size > 24 && size <= file->length())
|
||||
if(size > 24 && static_cast<long long>(size) <= file->length())
|
||||
data = file->readBlock(size - 24);
|
||||
else
|
||||
data = ByteVector();
|
||||
@@ -194,7 +190,7 @@ void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, long long size)
|
||||
|
||||
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
|
||||
{
|
||||
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
|
||||
return guid() + ByteVector::fromUInt64LE(data.size() + 24) + data;
|
||||
}
|
||||
|
||||
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
|
||||
@@ -211,7 +207,7 @@ ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
|
||||
return filePropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long long size)
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() < 64) {
|
||||
@@ -219,8 +215,8 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long l
|
||||
return;
|
||||
}
|
||||
|
||||
const long long duration = data.toLongLong(40, false);
|
||||
const long long preroll = data.toLongLong(56, false);
|
||||
const long long duration = data.toInt64LE(40);
|
||||
const long long preroll = data.toInt64LE(56);
|
||||
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
|
||||
}
|
||||
|
||||
@@ -229,7 +225,7 @@ ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
|
||||
return streamPropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, long long size)
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() < 70) {
|
||||
@@ -237,11 +233,11 @@ void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, long
|
||||
return;
|
||||
}
|
||||
|
||||
file->d->properties->setCodec(data.toUShort(54, false));
|
||||
file->d->properties->setChannels(data.toUShort(56, false));
|
||||
file->d->properties->setSampleRate(data.toUInt(58, false));
|
||||
file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
|
||||
file->d->properties->setBitsPerSample(data.toUShort(68, false));
|
||||
file->d->properties->setCodec(data.toUInt16LE(54));
|
||||
file->d->properties->setChannels(data.toUInt16LE(56));
|
||||
file->d->properties->setSampleRate(data.toUInt32LE(58));
|
||||
file->d->properties->setBitrate(static_cast<int>(data.toUInt32LE(62) * 8.0 / 1000.0 + 0.5));
|
||||
file->d->properties->setBitsPerSample(data.toUInt16LE(68));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
||||
@@ -249,7 +245,7 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
||||
return contentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
const int titleLength = readWORD(file);
|
||||
const int artistLength = readWORD(file);
|
||||
@@ -271,11 +267,11 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *f
|
||||
const ByteVector v4 = renderString(file->d->tag->comment());
|
||||
const ByteVector v5 = renderString(file->d->tag->rating());
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(v1.size(), false));
|
||||
data.append(ByteVector::fromShort(v2.size(), false));
|
||||
data.append(ByteVector::fromShort(v3.size(), false));
|
||||
data.append(ByteVector::fromShort(v4.size(), false));
|
||||
data.append(ByteVector::fromShort(v5.size(), false));
|
||||
data.append(ByteVector::fromUInt16LE(v1.size()));
|
||||
data.append(ByteVector::fromUInt16LE(v2.size()));
|
||||
data.append(ByteVector::fromUInt16LE(v3.size()));
|
||||
data.append(ByteVector::fromUInt16LE(v4.size()));
|
||||
data.append(ByteVector::fromUInt16LE(v5.size()));
|
||||
data.append(v1);
|
||||
data.append(v2);
|
||||
data.append(v3);
|
||||
@@ -289,7 +285,7 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() cons
|
||||
return extendedContentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
@@ -302,7 +298,7 @@ void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *
|
||||
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(ByteVector::fromUInt16LE(attributeData.size()));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
@@ -312,7 +308,7 @@ ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
|
||||
return metadataGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
@@ -325,7 +321,7 @@ void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*
|
||||
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(ByteVector::fromUInt16LE(attributeData.size()));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
@@ -335,7 +331,7 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
|
||||
return metadataLibraryGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long long /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
@@ -348,14 +344,13 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long
|
||||
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(ByteVector::fromUInt16LE(attributeData.size()));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
||||
@@ -363,36 +358,36 @@ ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
||||
return headerExtensionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long long /*size*/)
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->seek(18, File::Current);
|
||||
long long dataSize = readDWORD(file);
|
||||
long long dataPos = 0;
|
||||
while(dataPos < dataSize) {
|
||||
ByteVector uid = file->readBlock(16);
|
||||
if(uid.size() != 16) {
|
||||
ByteVector guid = file->readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
file->setValid(false);
|
||||
break;
|
||||
}
|
||||
bool ok;
|
||||
long long size = readQWORD(file, &ok);
|
||||
if(!ok || size < 0 || size > dataSize - dataPos) {
|
||||
if(!ok) {
|
||||
file->setValid(false);
|
||||
break;
|
||||
}
|
||||
BaseObject *obj;
|
||||
if(uid == metadataGuid) {
|
||||
file->d->metadataObject = new MetadataObject();
|
||||
SHARED_PTR<BaseObject> obj;
|
||||
if(guid == metadataGuid) {
|
||||
file->d->metadataObject.reset(new MetadataObject());
|
||||
obj = file->d->metadataObject;
|
||||
}
|
||||
else if(uid == metadataLibraryGuid) {
|
||||
file->d->metadataLibraryObject = new MetadataLibraryObject();
|
||||
else if(guid == metadataLibraryGuid) {
|
||||
file->d->metadataLibraryObject.reset(new MetadataLibraryObject());
|
||||
obj = file->d->metadataLibraryObject;
|
||||
}
|
||||
else {
|
||||
obj = new UnknownObject(uid);
|
||||
obj.reset(new UnknownObject(guid));
|
||||
}
|
||||
obj->parse(file, size);
|
||||
obj->parse(file, (unsigned int)size);
|
||||
objects.append(obj);
|
||||
dataPos += size;
|
||||
}
|
||||
@@ -401,10 +396,10 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
for(const auto &object : std::as_const(objects)) {
|
||||
data.append(object->render(file));
|
||||
for(ObjectConstIterator it = objects.begin(); it != objects.end(); ++it) {
|
||||
data.append((*it)->render(file));
|
||||
}
|
||||
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
|
||||
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt32LE(data.size()) + data;
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
@@ -413,7 +408,7 @@ ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
|
||||
return codecListGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long size)
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() <= 20) {
|
||||
@@ -423,7 +418,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long s
|
||||
|
||||
unsigned int pos = 16;
|
||||
|
||||
const int count = data.toUInt(pos, false);
|
||||
const int count = data.toUInt32LE(pos);
|
||||
pos += 4;
|
||||
|
||||
for(int i = 0; i < count; ++i) {
|
||||
@@ -431,22 +426,22 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long s
|
||||
if(pos >= data.size())
|
||||
break;
|
||||
|
||||
const auto type = static_cast<CodecType>(data.toUShort(pos, false));
|
||||
const CodecType type = static_cast<CodecType>(data.toUInt16LE(pos));
|
||||
pos += 2;
|
||||
|
||||
int nameLength = data.toUShort(pos, false);
|
||||
int nameLength = data.toUInt16LE(pos);
|
||||
pos += 2;
|
||||
|
||||
const unsigned int namePos = pos;
|
||||
pos += nameLength * 2;
|
||||
|
||||
const int descLength = data.toUShort(pos, false);
|
||||
const int descLength = data.toUInt16LE(pos);
|
||||
pos += 2;
|
||||
|
||||
const unsigned int descPos = pos;
|
||||
pos += descLength * 2;
|
||||
|
||||
const int infoLength = data.toUShort(pos, false);
|
||||
const int infoLength = data.toUInt16LE(pos);
|
||||
pos += 2 + infoLength * 2;
|
||||
|
||||
if(type == CodecListObject::Audio) {
|
||||
@@ -479,45 +474,33 @@ bool ASF::File::isSupported(IOStream *stream)
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
ASF::File::File(FileName file, bool, AudioProperties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read();
|
||||
}
|
||||
|
||||
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
|
||||
ASF::File::File(IOStream *stream, bool, AudioProperties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read();
|
||||
}
|
||||
|
||||
ASF::File::~File() = default;
|
||||
ASF::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
ASF::Tag *ASF::File::tag() const
|
||||
{
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
void ASF::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
ASF::Properties *ASF::File::audioProperties() const
|
||||
ASF::AudioProperties *ASF::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
@@ -535,23 +518,23 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
if(!d->contentDescriptionObject) {
|
||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
||||
d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject());
|
||||
d->objects.append(d->contentDescriptionObject);
|
||||
}
|
||||
if(!d->extendedContentDescriptionObject) {
|
||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject());
|
||||
d->objects.append(d->extendedContentDescriptionObject);
|
||||
}
|
||||
if(!d->headerExtensionObject) {
|
||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
||||
d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject());
|
||||
d->objects.append(d->headerExtensionObject);
|
||||
}
|
||||
if(!d->metadataObject) {
|
||||
d->metadataObject = new FilePrivate::MetadataObject();
|
||||
d->metadataObject.reset(new FilePrivate::MetadataObject());
|
||||
d->headerExtensionObject->objects.append(d->metadataObject);
|
||||
}
|
||||
if(!d->metadataLibraryObject) {
|
||||
d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
|
||||
d->metadataLibraryObject.reset(new FilePrivate::MetadataLibraryObject());
|
||||
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
|
||||
}
|
||||
|
||||
@@ -559,11 +542,19 @@ bool ASF::File::save()
|
||||
d->metadataObject->attributeData.clear();
|
||||
d->metadataLibraryObject->attributeData.clear();
|
||||
|
||||
for(const auto &[name, attributes] : std::as_const(d->tag->attributeListMap())) {
|
||||
const AttributeListMap allAttributes = d->tag->attributeListMap();
|
||||
|
||||
for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) {
|
||||
|
||||
const String &name = it->first;
|
||||
const AttributeList &attributes = it->second;
|
||||
|
||||
bool inExtendedContentDescriptionObject = false;
|
||||
bool inMetadataObject = false;
|
||||
|
||||
for(const auto &attribute : attributes) {
|
||||
for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) {
|
||||
|
||||
const Attribute &attribute = *jt;
|
||||
const bool largeValue = (attribute.dataSize() > 65535);
|
||||
const bool guid = (attribute.type() == Attribute::GuidType);
|
||||
|
||||
@@ -582,16 +573,16 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
ByteVector data;
|
||||
for(const auto &object : std::as_const(d->objects)) {
|
||||
data.append(object->render(this));
|
||||
for(FilePrivate::ObjectConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
|
||||
data.append((*it)->render(this));
|
||||
}
|
||||
|
||||
seek(16);
|
||||
writeBlock(ByteVector::fromLongLong(data.size() + 30, false));
|
||||
writeBlock(ByteVector::fromUInt(d->objects.size(), false));
|
||||
writeBlock(ByteVector::fromUInt64LE(data.size() + 30));
|
||||
writeBlock(ByteVector::fromUInt32LE(d->objects.size()));
|
||||
writeBlock(ByteVector("\x01\x02", 2));
|
||||
|
||||
insert(data, 30, static_cast<unsigned long>(d->headerSize - 30));
|
||||
insert(data, 30, static_cast<size_t>(d->headerSize - 30));
|
||||
|
||||
d->headerSize = data.size() + 30;
|
||||
|
||||
@@ -613,8 +604,8 @@ void ASF::File::read()
|
||||
return;
|
||||
}
|
||||
|
||||
d->tag = std::make_unique<ASF::Tag>();
|
||||
d->properties = std::make_unique<ASF::Properties>();
|
||||
d->tag.reset(new ASF::Tag());
|
||||
d->properties.reset(new ASF::AudioProperties());
|
||||
|
||||
bool ok;
|
||||
d->headerSize = readQWORD(this, &ok);
|
||||
@@ -629,42 +620,42 @@ void ASF::File::read()
|
||||
}
|
||||
seek(2, Current);
|
||||
|
||||
FilePrivate::FilePropertiesObject *filePropertiesObject = nullptr;
|
||||
FilePrivate::StreamPropertiesObject *streamPropertiesObject = nullptr;
|
||||
SHARED_PTR<FilePrivate::FilePropertiesObject> filePropertiesObject;
|
||||
SHARED_PTR<FilePrivate::StreamPropertiesObject> streamPropertiesObject;
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
const ByteVector guid = readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
auto size = readQWORD(this, &ok);
|
||||
long size = (long)readQWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
FilePrivate::BaseObject *obj;
|
||||
SHARED_PTR<FilePrivate::BaseObject> obj;
|
||||
if(guid == filePropertiesGuid) {
|
||||
filePropertiesObject = new FilePrivate::FilePropertiesObject();
|
||||
filePropertiesObject.reset(new FilePrivate::FilePropertiesObject());
|
||||
obj = filePropertiesObject;
|
||||
}
|
||||
else if(guid == streamPropertiesGuid) {
|
||||
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
|
||||
streamPropertiesObject.reset(new FilePrivate::StreamPropertiesObject());
|
||||
obj = streamPropertiesObject;
|
||||
}
|
||||
else if(guid == contentDescriptionGuid) {
|
||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
||||
d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject());
|
||||
obj = d->contentDescriptionObject;
|
||||
}
|
||||
else if(guid == extendedContentDescriptionGuid) {
|
||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject());
|
||||
obj = d->extendedContentDescriptionObject;
|
||||
}
|
||||
else if(guid == headerExtensionGuid) {
|
||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
||||
d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject());
|
||||
obj = d->headerExtensionObject;
|
||||
}
|
||||
else if(guid == codecListGuid) {
|
||||
obj = new FilePrivate::CodecListObject();
|
||||
obj.reset(new FilePrivate::CodecListObject());
|
||||
}
|
||||
else {
|
||||
if(guid == contentEncryptionGuid ||
|
||||
@@ -672,7 +663,7 @@ void ASF::File::read()
|
||||
guid == advancedContentEncryptionGuid) {
|
||||
d->properties->setEncrypted(true);
|
||||
}
|
||||
obj = new FilePrivate::UnknownObject(guid);
|
||||
obj.reset(new FilePrivate::UnknownObject(guid));
|
||||
}
|
||||
obj->parse(this, size);
|
||||
d->objects.append(obj);
|
||||
|
||||
@@ -26,15 +26,17 @@
|
||||
#ifndef TAGLIB_ASFFILE_H
|
||||
#define TAGLIB_ASFFILE_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tfile.h"
|
||||
#include "taglib_export.h"
|
||||
#include "tag.h"
|
||||
#include "asfproperties.h"
|
||||
#include "asftag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of ASF (WMA) metadata
|
||||
namespace ASF {
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for ASF files to the
|
||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
||||
@@ -46,17 +48,21 @@ namespace TagLib {
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Constructs an ASF file from \a file.
|
||||
* Contructs an ASF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs an ASF file from \a stream.
|
||||
* Contructs an ASF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
@@ -66,15 +72,12 @@ namespace TagLib {
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ASF tag of the file.
|
||||
@@ -86,35 +89,19 @@ namespace TagLib {
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*/
|
||||
Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
virtual Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the ASF audio properties for this file.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*/
|
||||
bool save() override;
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an ASF
|
||||
@@ -129,10 +116,11 @@ namespace TagLib {
|
||||
void read();
|
||||
|
||||
class FilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
FilePrivate *d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,21 +23,36 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "asfpicture.h"
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
#include "asffile.h"
|
||||
#include "asfpicture.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct PictureData
|
||||
{
|
||||
bool valid;
|
||||
ASF::Picture::Type type;
|
||||
String mimeType;
|
||||
String description;
|
||||
ByteVector picture;
|
||||
};
|
||||
}
|
||||
|
||||
class ASF::Picture::PicturePrivate
|
||||
{
|
||||
public:
|
||||
bool valid;
|
||||
Type type;
|
||||
String mimeType;
|
||||
String description;
|
||||
ByteVector picture;
|
||||
PicturePrivate() :
|
||||
data(new PictureData()) {}
|
||||
|
||||
SHARED_PTR<PictureData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -45,69 +60,80 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Picture::Picture() :
|
||||
d(std::make_shared<PicturePrivate>())
|
||||
d(new PicturePrivate())
|
||||
{
|
||||
d->valid = true;
|
||||
d->data->valid = true;
|
||||
}
|
||||
|
||||
ASF::Picture::Picture(const Picture &) = default;
|
||||
ASF::Picture::~Picture() = default;
|
||||
ASF::Picture::Picture(const Picture& other) :
|
||||
d(new PicturePrivate(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
ASF::Picture::~Picture()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool ASF::Picture::isValid() const
|
||||
{
|
||||
return d->valid;
|
||||
return d->data->valid;
|
||||
}
|
||||
|
||||
String ASF::Picture::mimeType() const
|
||||
{
|
||||
return d->mimeType;
|
||||
return d->data->mimeType;
|
||||
}
|
||||
|
||||
void ASF::Picture::setMimeType(const String &value)
|
||||
{
|
||||
d->mimeType = value;
|
||||
d->data->mimeType = value;
|
||||
}
|
||||
|
||||
ASF::Picture::Type ASF::Picture::type() const
|
||||
{
|
||||
return d->type;
|
||||
return d->data->type;
|
||||
}
|
||||
|
||||
void ASF::Picture::setType(const ASF::Picture::Type& t)
|
||||
{
|
||||
d->type = t;
|
||||
d->data->type = t;
|
||||
}
|
||||
|
||||
String ASF::Picture::description() const
|
||||
{
|
||||
return d->description;
|
||||
return d->data->description;
|
||||
}
|
||||
|
||||
void ASF::Picture::setDescription(const String &desc)
|
||||
{
|
||||
d->description = desc;
|
||||
d->data->description = desc;
|
||||
}
|
||||
|
||||
ByteVector ASF::Picture::picture() const
|
||||
{
|
||||
return d->picture;
|
||||
return d->data->picture;
|
||||
}
|
||||
|
||||
void ASF::Picture::setPicture(const ByteVector &p)
|
||||
{
|
||||
d->picture = p;
|
||||
d->data->picture = p;
|
||||
}
|
||||
|
||||
int ASF::Picture::dataSize() const
|
||||
{
|
||||
return
|
||||
9 + (d->mimeType.length() + d->description.length()) * 2 +
|
||||
d->picture.size();
|
||||
return static_cast<int>(
|
||||
9 + (d->data->mimeType.length() + d->data->description.length()) * 2 +
|
||||
d->data->picture.size());
|
||||
}
|
||||
|
||||
ASF::Picture &ASF::Picture::operator=(const ASF::Picture &) = default;
|
||||
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
|
||||
{
|
||||
Picture(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ASF::Picture::swap(Picture &other) noexcept
|
||||
void ASF::Picture::swap(Picture &other)
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
@@ -120,46 +146,47 @@ ByteVector ASF::Picture::render() const
|
||||
return ByteVector();
|
||||
|
||||
return
|
||||
ByteVector(static_cast<char>(d->type)) +
|
||||
ByteVector::fromUInt(d->picture.size(), false) +
|
||||
renderString(d->mimeType) +
|
||||
renderString(d->description) +
|
||||
d->picture;
|
||||
ByteVector((char)d->data->type) +
|
||||
ByteVector::fromUInt32LE(d->data->picture.size()) +
|
||||
renderString(d->data->mimeType) +
|
||||
renderString(d->data->description) +
|
||||
d->data->picture;
|
||||
}
|
||||
|
||||
void ASF::Picture::parse(const ByteVector& bytes)
|
||||
{
|
||||
d->valid = false;
|
||||
d->data->valid = false;
|
||||
if(bytes.size() < 9)
|
||||
return;
|
||||
int pos = 0;
|
||||
d->type = static_cast<Type>(bytes[0]); ++pos;
|
||||
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
|
||||
size_t pos = 0;
|
||||
d->data->type = (Type)bytes[0]; ++pos;
|
||||
const unsigned int dataLen = bytes.toUInt32LE(pos); pos+=4;
|
||||
|
||||
const ByteVector nullStringTerminator(2, 0);
|
||||
|
||||
int endPos = bytes.find(nullStringTerminator, pos, 2);
|
||||
if(endPos < 0)
|
||||
size_t endPos = bytes.find(nullStringTerminator, pos, 2);
|
||||
if(endPos == ByteVector::npos())
|
||||
return;
|
||||
d->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
||||
d->data->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
||||
pos = endPos+2;
|
||||
|
||||
endPos = bytes.find(nullStringTerminator, pos, 2);
|
||||
if(endPos < 0)
|
||||
if(endPos == ByteVector::npos())
|
||||
return;
|
||||
d->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
||||
d->data->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
||||
pos = endPos+2;
|
||||
|
||||
if(dataLen + pos != bytes.size())
|
||||
return;
|
||||
|
||||
d->picture = bytes.mid(pos, dataLen);
|
||||
d->valid = true;
|
||||
d->data->picture = bytes.mid(pos, dataLen);
|
||||
d->data->valid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
ASF::Picture ASF::Picture::fromInvalid()
|
||||
{
|
||||
Picture ret;
|
||||
ret.d->valid = false;
|
||||
ret.d->data->valid = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tpicturetype.h"
|
||||
#include "taglib_export.h"
|
||||
#include "attachedpictureframe.h"
|
||||
|
||||
@@ -36,6 +35,7 @@ namespace TagLib
|
||||
{
|
||||
namespace ASF
|
||||
{
|
||||
class Attribute;
|
||||
|
||||
//! An ASF attached picture interface implementation
|
||||
|
||||
@@ -47,13 +47,57 @@ namespace TagLib
|
||||
* \see Attribute::toPicture()
|
||||
* \see Attribute::Attribute(const Picture& picture)
|
||||
*/
|
||||
class TAGLIB_EXPORT Picture {
|
||||
class TAGLIB_EXPORT Picture
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
* This describes the function or content of the picture.
|
||||
*/
|
||||
DECLARE_PICTURE_TYPE_ENUM(Type)
|
||||
enum Type {
|
||||
//! A type not enumerated below
|
||||
Other = 0x00,
|
||||
//! 32x32 PNG image that should be used as the file icon
|
||||
FileIcon = 0x01,
|
||||
//! File icon of a different size or format
|
||||
OtherFileIcon = 0x02,
|
||||
//! Front cover image of the album
|
||||
FrontCover = 0x03,
|
||||
//! Back cover image of the album
|
||||
BackCover = 0x04,
|
||||
//! Inside leaflet page of the album
|
||||
LeafletPage = 0x05,
|
||||
//! Image from the album itself
|
||||
Media = 0x06,
|
||||
//! Picture of the lead artist or soloist
|
||||
LeadArtist = 0x07,
|
||||
//! Picture of the artist or performer
|
||||
Artist = 0x08,
|
||||
//! Picture of the conductor
|
||||
Conductor = 0x09,
|
||||
//! Picture of the band or orchestra
|
||||
Band = 0x0A,
|
||||
//! Picture of the composer
|
||||
Composer = 0x0B,
|
||||
//! Picture of the lyricist or text writer
|
||||
Lyricist = 0x0C,
|
||||
//! Picture of the recording location or studio
|
||||
RecordingLocation = 0x0D,
|
||||
//! Picture of the artists during recording
|
||||
DuringRecording = 0x0E,
|
||||
//! Picture of the artists during performance
|
||||
DuringPerformance = 0x0F,
|
||||
//! Picture from a movie or video related to the track
|
||||
MovieScreenCapture = 0x10,
|
||||
//! Picture of a large, coloured fish
|
||||
ColouredFish = 0x11,
|
||||
//! Illustration related to the track
|
||||
Illustration = 0x12,
|
||||
//! Logo of the band or performer
|
||||
BandLogo = 0x13,
|
||||
//! Logo of the publisher (record company)
|
||||
PublisherLogo = 0x14
|
||||
};
|
||||
|
||||
/*!
|
||||
* Constructs an empty picture.
|
||||
@@ -61,7 +105,7 @@ namespace TagLib
|
||||
Picture();
|
||||
|
||||
/*!
|
||||
* Construct an picture as a copy of \a other.
|
||||
* Constructs an picture as a copy of \a other.
|
||||
*/
|
||||
Picture(const Picture& other);
|
||||
|
||||
@@ -78,7 +122,7 @@ namespace TagLib
|
||||
/*!
|
||||
* Exchanges the content of the Picture by the content of \a other.
|
||||
*/
|
||||
void swap(Picture &other) noexcept;
|
||||
void swap(Picture &other);
|
||||
|
||||
/*!
|
||||
* Returns true if Picture stores valid picture
|
||||
@@ -164,18 +208,16 @@ namespace TagLib
|
||||
*/
|
||||
int dataSize() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* THIS IS PRIVATE, DON'T TOUCH IT! */
|
||||
void parse(const ByteVector& );
|
||||
static Picture fromInvalid();
|
||||
#endif
|
||||
private:
|
||||
friend class Attribute;
|
||||
|
||||
private:
|
||||
class PicturePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<PicturePrivate> d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
void parse(const ByteVector &);
|
||||
static Picture fromInvalid();
|
||||
|
||||
class PicturePrivate;
|
||||
PicturePrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ASFPICTURE_H
|
||||
|
||||
@@ -23,77 +23,101 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "asfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::Properties::PropertiesPrivate
|
||||
class ASF::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
ASF::Properties::Codec codec { ASF::Properties::Unknown };
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
bitsPerSample(0),
|
||||
codec(ASF::AudioProperties::Unknown),
|
||||
encrypted(false) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int bitsPerSample;
|
||||
ASF::AudioProperties::Codec codec;
|
||||
String codecName;
|
||||
String codecDescription;
|
||||
bool encrypted { false };
|
||||
bool encrypted;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Properties::Properties() :
|
||||
AudioProperties(AudioProperties::Average),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
ASF::AudioProperties::AudioProperties() :
|
||||
TagLib::AudioProperties(),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
ASF::Properties::~Properties() = default;
|
||||
ASF::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int ASF::Properties::lengthInMilliseconds() const
|
||||
int ASF::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int ASF::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int ASF::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int ASF::Properties::bitrate() const
|
||||
int ASF::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int ASF::Properties::sampleRate() const
|
||||
int ASF::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int ASF::Properties::channels() const
|
||||
int ASF::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int ASF::Properties::bitsPerSample() const
|
||||
int ASF::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
ASF::Properties::Codec ASF::Properties::codec() const
|
||||
ASF::AudioProperties::Codec ASF::AudioProperties::codec() const
|
||||
{
|
||||
return d->codec;
|
||||
}
|
||||
|
||||
String ASF::Properties::codecName() const
|
||||
String ASF::AudioProperties::codecName() const
|
||||
{
|
||||
return d->codecName;
|
||||
}
|
||||
|
||||
String ASF::Properties::codecDescription() const
|
||||
String ASF::AudioProperties::codecDescription() const
|
||||
{
|
||||
return d->codecDescription;
|
||||
}
|
||||
|
||||
bool ASF::Properties::isEncrypted() const
|
||||
bool ASF::AudioProperties::isEncrypted() const
|
||||
{
|
||||
return d->encrypted;
|
||||
}
|
||||
@@ -102,32 +126,32 @@ bool ASF::Properties::isEncrypted() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ASF::Properties::setLengthInMilliseconds(int value)
|
||||
void ASF::AudioProperties::setLengthInMilliseconds(int value)
|
||||
{
|
||||
d->length = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setBitrate(int value)
|
||||
void ASF::AudioProperties::setBitrate(int value)
|
||||
{
|
||||
d->bitrate = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setSampleRate(int value)
|
||||
void ASF::AudioProperties::setSampleRate(int value)
|
||||
{
|
||||
d->sampleRate = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setChannels(int value)
|
||||
void ASF::AudioProperties::setChannels(int value)
|
||||
{
|
||||
d->channels = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setBitsPerSample(int value)
|
||||
void ASF::AudioProperties::setBitsPerSample(int value)
|
||||
{
|
||||
d->bitsPerSample = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodec(int value)
|
||||
void ASF::AudioProperties::setCodec(int value)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
@@ -149,17 +173,17 @@ void ASF::Properties::setCodec(int value)
|
||||
}
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodecName(const String &value)
|
||||
void ASF::AudioProperties::setCodecName(const String &value)
|
||||
{
|
||||
d->codecName = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodecDescription(const String &value)
|
||||
void ASF::AudioProperties::setCodecDescription(const String &value)
|
||||
{
|
||||
d->codecDescription = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setEncrypted(bool value)
|
||||
void ASF::AudioProperties::setEncrypted(bool value)
|
||||
{
|
||||
d->encrypted = value;
|
||||
}
|
||||
|
||||
@@ -26,17 +26,22 @@
|
||||
#ifndef TAGLIB_ASFPROPERTIES_H
|
||||
#define TAGLIB_ASFPROPERTIES_H
|
||||
|
||||
#include "audioproperties.h"
|
||||
#include "tstring.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace ASF {
|
||||
//! An implementation of ASF audio properties
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
{
|
||||
public:
|
||||
|
||||
namespace ASF {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of ASF audio properties
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
friend class File;
|
||||
|
||||
public:
|
||||
/*!
|
||||
* Audio codec types can be used in ASF file.
|
||||
*/
|
||||
@@ -69,39 +74,54 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
/*!
|
||||
* Creates an instance of ASF::Properties.
|
||||
* Creates an instance of ASF::AudioProperties.
|
||||
*/
|
||||
Properties();
|
||||
AudioProperties();
|
||||
|
||||
/*!
|
||||
* Destroys this ASF::Properties instance.
|
||||
* Destroys this ASF::AudioProperties instance.
|
||||
*/
|
||||
~Properties() override;
|
||||
virtual ~AudioProperties();
|
||||
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
int lengthInMilliseconds() const override;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
int bitrate() const override;
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
int sampleRate() const override;
|
||||
virtual int sampleRate() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
int channels() const override;
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
@@ -140,7 +160,7 @@ namespace TagLib {
|
||||
*/
|
||||
bool isEncrypted() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
private:
|
||||
void setLengthInMilliseconds(int value);
|
||||
void setBitrate(int value);
|
||||
void setSampleRate(int value);
|
||||
@@ -150,13 +170,13 @@ namespace TagLib {
|
||||
void setCodecName(const String &value);
|
||||
void setCodecDescription(const String &value);
|
||||
void setEncrypted(bool value);
|
||||
#endif
|
||||
|
||||
private:
|
||||
class PropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,29 +23,12 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tpicturemap.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "asftag.h"
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
#include "tpropertymap.h"
|
||||
#include "asfattribute.h"
|
||||
#include "asfpicture.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
StringList attributeListToStringList(const ASF::AttributeList &attributes)
|
||||
{
|
||||
StringList strs;
|
||||
for(const auto &attribute : attributes) {
|
||||
strs.append(attribute.toString());
|
||||
}
|
||||
return strs;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class ASF::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
@@ -58,11 +41,15 @@ public:
|
||||
};
|
||||
|
||||
ASF::Tag::Tag() :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
ASF::Tag::~Tag() = default;
|
||||
ASF::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
String ASF::Tag::title() const
|
||||
{
|
||||
@@ -77,8 +64,7 @@ String ASF::Tag::artist() const
|
||||
String ASF::Tag::album() const
|
||||
{
|
||||
if(d->attributeListMap.contains("WM/AlbumTitle"))
|
||||
return joinTagValues(
|
||||
attributeListToStringList(d->attributeListMap.value("WM/AlbumTitle")));
|
||||
return d->attributeListMap["WM/AlbumTitle"][0].toString();
|
||||
return String();
|
||||
}
|
||||
|
||||
@@ -110,7 +96,8 @@ unsigned int ASF::Tag::track() const
|
||||
const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0];
|
||||
if(attr.type() == ASF::Attribute::DWordType)
|
||||
return attr.toUInt();
|
||||
return attr.toString().toInt();
|
||||
else
|
||||
return attr.toString().toInt();
|
||||
}
|
||||
if(d->attributeListMap.contains("WM/Track"))
|
||||
return d->attributeListMap["WM/Track"][0].toUInt();
|
||||
@@ -120,11 +107,95 @@ unsigned int ASF::Tag::track() const
|
||||
String ASF::Tag::genre() const
|
||||
{
|
||||
if(d->attributeListMap.contains("WM/Genre"))
|
||||
return joinTagValues(
|
||||
attributeListToStringList(d->attributeListMap.value("WM/Genre")));
|
||||
return d->attributeListMap["WM/Genre"][0].toString();
|
||||
return String();
|
||||
}
|
||||
|
||||
PictureMap ASF::Tag::pictures() const
|
||||
{
|
||||
PictureMap map;
|
||||
if(d->attributeListMap.contains("WM/Picture")) {
|
||||
AttributeList list = d->attributeListMap["WM/Picture"];
|
||||
for(AttributeList::ConstIterator it = list.begin();
|
||||
it != list.end();
|
||||
++it) {
|
||||
ASF::Picture asfPicture = (*it).toPicture();
|
||||
TagLib::Picture::Type type;
|
||||
switch(asfPicture.type()) {
|
||||
case ASF::Picture::FileIcon:
|
||||
type = TagLib::Picture::FileIcon;
|
||||
break;
|
||||
case ASF::Picture::OtherFileIcon:
|
||||
type = TagLib::Picture::OtherFileIcon;
|
||||
break;
|
||||
case ASF::Picture::FrontCover:
|
||||
type = TagLib::Picture::FrontCover;
|
||||
break;
|
||||
case ASF::Picture::BackCover:
|
||||
type = TagLib::Picture::BackCover;
|
||||
break;
|
||||
case ASF::Picture::LeafletPage:
|
||||
type = TagLib::Picture::LeafletPage;
|
||||
break;
|
||||
case ASF::Picture::Media:
|
||||
type = TagLib::Picture::Media;
|
||||
break;
|
||||
case ASF::Picture::LeadArtist:
|
||||
type = TagLib::Picture::LeadArtist;
|
||||
break;
|
||||
case ASF::Picture::Artist:
|
||||
type = TagLib::Picture::Artist;
|
||||
break;
|
||||
case ASF::Picture::Conductor:
|
||||
type = TagLib::Picture::Conductor;
|
||||
break;
|
||||
case ASF::Picture::Band:
|
||||
type = TagLib::Picture::Band;
|
||||
break;
|
||||
case ASF::Picture::Composer:
|
||||
type = TagLib::Picture::Composer;
|
||||
break;
|
||||
case ASF::Picture::Lyricist:
|
||||
type = TagLib::Picture::Lyricist;
|
||||
break;
|
||||
case ASF::Picture::RecordingLocation:
|
||||
type = TagLib::Picture::RecordingLocation;
|
||||
break;
|
||||
case ASF::Picture::DuringRecording:
|
||||
type = TagLib::Picture::DuringRecording;
|
||||
break;
|
||||
case ASF::Picture::DuringPerformance:
|
||||
type = TagLib::Picture::DuringPerformance;
|
||||
break;
|
||||
case ASF::Picture::MovieScreenCapture:
|
||||
type = TagLib::Picture::MovieScreenCapture;
|
||||
break;
|
||||
case ASF::Picture::ColouredFish:
|
||||
type = TagLib::Picture::ColouredFish;
|
||||
break;
|
||||
case ASF::Picture::Illustration:
|
||||
type = TagLib::Picture::Illustration;
|
||||
break;
|
||||
case ASF::Picture::BandLogo:
|
||||
type = TagLib::Picture::BandLogo;
|
||||
break;
|
||||
case ASF::Picture::PublisherLogo:
|
||||
type = TagLib::Picture::PublisherLogo;
|
||||
break;
|
||||
default:
|
||||
type = TagLib::Picture::Other;
|
||||
break;
|
||||
}
|
||||
TagLib::Picture picture(asfPicture.picture(),
|
||||
type,
|
||||
asfPicture.mimeType(),
|
||||
asfPicture.description());
|
||||
map.insert(picture);
|
||||
}
|
||||
}
|
||||
return PictureMap(map);
|
||||
}
|
||||
|
||||
void ASF::Tag::setTitle(const String &value)
|
||||
{
|
||||
d->title = value;
|
||||
@@ -170,6 +241,94 @@ void ASF::Tag::setTrack(unsigned int value)
|
||||
setAttribute("WM/TrackNumber", String::number(value));
|
||||
}
|
||||
|
||||
void ASF::Tag::setPictures(const PictureMap &l)
|
||||
{
|
||||
removeItem("WM/Picture");
|
||||
for(PictureMap::ConstIterator pictureMapIt = l.begin();
|
||||
pictureMapIt != l.end();
|
||||
++pictureMapIt)
|
||||
{
|
||||
PictureList list = pictureMapIt->second;
|
||||
for( PictureList::ConstIterator pictureListIt = list.begin();
|
||||
pictureListIt != list.end();
|
||||
++pictureListIt)
|
||||
{
|
||||
const TagLib::Picture picture = (*pictureListIt);
|
||||
ASF::Picture asfPicture;
|
||||
asfPicture.setPicture(picture.data());
|
||||
asfPicture.setMimeType(picture.mime());
|
||||
asfPicture.setDescription(picture.description());
|
||||
switch (picture.type()) {
|
||||
case TagLib::Picture::Other:
|
||||
asfPicture.setType(ASF::Picture::Other);
|
||||
break;
|
||||
case TagLib::Picture::FileIcon:
|
||||
asfPicture.setType(ASF::Picture::FileIcon);
|
||||
break;
|
||||
case TagLib::Picture::OtherFileIcon:
|
||||
asfPicture.setType(ASF::Picture::OtherFileIcon);
|
||||
break;
|
||||
case TagLib::Picture::FrontCover:
|
||||
asfPicture.setType(ASF::Picture::FrontCover);
|
||||
break;
|
||||
case TagLib::Picture::BackCover:
|
||||
asfPicture.setType(ASF::Picture::BackCover);
|
||||
break;
|
||||
case TagLib::Picture::LeafletPage:
|
||||
asfPicture.setType(ASF::Picture::LeafletPage);
|
||||
break;
|
||||
case TagLib::Picture::Media:
|
||||
asfPicture.setType(ASF::Picture::Media);
|
||||
break;
|
||||
case TagLib::Picture::LeadArtist:
|
||||
asfPicture.setType(ASF::Picture::LeadArtist);
|
||||
break;
|
||||
case TagLib::Picture::Artist:
|
||||
asfPicture.setType(ASF::Picture::Artist);
|
||||
break;
|
||||
case TagLib::Picture::Conductor:
|
||||
asfPicture.setType(ASF::Picture::Conductor);
|
||||
break;
|
||||
case TagLib::Picture::Band:
|
||||
asfPicture.setType(ASF::Picture::Band);
|
||||
break;
|
||||
case TagLib::Picture::Composer:
|
||||
asfPicture.setType(ASF::Picture::Composer);
|
||||
break;
|
||||
case TagLib::Picture::Lyricist:
|
||||
asfPicture.setType(ASF::Picture::Lyricist);
|
||||
break;
|
||||
case TagLib::Picture::RecordingLocation:
|
||||
asfPicture.setType(ASF::Picture::RecordingLocation);
|
||||
break;
|
||||
case TagLib::Picture::DuringRecording:
|
||||
asfPicture.setType(ASF::Picture::DuringRecording);
|
||||
break;
|
||||
case TagLib::Picture::DuringPerformance:
|
||||
asfPicture.setType(ASF::Picture::DuringPerformance);
|
||||
break;
|
||||
case TagLib::Picture::MovieScreenCapture:
|
||||
asfPicture.setType(ASF::Picture::MovieScreenCapture);
|
||||
break;
|
||||
case TagLib::Picture::ColouredFish:
|
||||
asfPicture.setType(ASF::Picture::ColouredFish);
|
||||
break;
|
||||
case TagLib::Picture::Illustration:
|
||||
asfPicture.setType(ASF::Picture::Illustration);
|
||||
break;
|
||||
case TagLib::Picture::BandLogo:
|
||||
asfPicture.setType(ASF::Picture::BandLogo);
|
||||
break;
|
||||
case TagLib::Picture::PublisherLogo:
|
||||
asfPicture.setType(ASF::Picture::PublisherLogo);
|
||||
break;
|
||||
}
|
||||
addAttribute("WM/Picture", Attribute(asfPicture));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ASF::AttributeListMap& ASF::Tag::attributeListMap()
|
||||
{
|
||||
return d->attributeListMap;
|
||||
@@ -197,9 +356,9 @@ ASF::AttributeList ASF::Tag::attribute(const String &name) const
|
||||
|
||||
void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
|
||||
{
|
||||
AttributeList val;
|
||||
val.append(attribute);
|
||||
d->attributeListMap.insert(name, val);
|
||||
AttributeList value;
|
||||
value.append(attribute);
|
||||
d->attributeListMap.insert(name, value);
|
||||
}
|
||||
|
||||
void ASF::Tag::setAttribute(const String &name, const AttributeList &values)
|
||||
@@ -227,73 +386,59 @@ bool ASF::Tag::isEmpty() const
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr std::array keyTranslation {
|
||||
std::pair("WM/AlbumTitle", "ALBUM"),
|
||||
std::pair("WM/AlbumArtist", "ALBUMARTIST"),
|
||||
std::pair("WM/AuthorURL", "ARTISTWEBPAGE"),
|
||||
std::pair("WM/Composer", "COMPOSER"),
|
||||
std::pair("WM/Writer", "LYRICIST"),
|
||||
std::pair("WM/Conductor", "CONDUCTOR"),
|
||||
std::pair("WM/ModifiedBy", "REMIXER"),
|
||||
std::pair("WM/Year", "DATE"),
|
||||
std::pair("WM/OriginalAlbumTitle", "ORIGINALALBUM"),
|
||||
std::pair("WM/OriginalArtist", "ORIGINALARTIST"),
|
||||
std::pair("WM/OriginalFilename", "ORIGINALFILENAME"),
|
||||
std::pair("WM/OriginalLyricist", "ORIGINALLYRICIST"),
|
||||
std::pair("WM/OriginalReleaseYear", "ORIGINALDATE"),
|
||||
std::pair("WM/Producer", "PRODUCER"),
|
||||
std::pair("WM/ContentGroupDescription", "WORK"),
|
||||
std::pair("WM/SubTitle", "SUBTITLE"),
|
||||
std::pair("WM/SetSubTitle", "DISCSUBTITLE"),
|
||||
std::pair("WM/TrackNumber", "TRACKNUMBER"),
|
||||
std::pair("WM/PartOfSet", "DISCNUMBER"),
|
||||
std::pair("WM/Genre", "GENRE"),
|
||||
std::pair("WM/BeatsPerMinute", "BPM"),
|
||||
std::pair("WM/Mood", "MOOD"),
|
||||
std::pair("WM/InitialKey", "INITIALKEY"),
|
||||
std::pair("WM/ISRC", "ISRC"),
|
||||
std::pair("WM/Lyrics", "LYRICS"),
|
||||
std::pair("WM/Media", "MEDIA"),
|
||||
std::pair("WM/Publisher", "LABEL"),
|
||||
std::pair("WM/CatalogNo", "CATALOGNUMBER"),
|
||||
std::pair("WM/Barcode", "BARCODE"),
|
||||
std::pair("WM/EncodedBy", "ENCODEDBY"),
|
||||
std::pair("WM/EncodingSettings", "ENCODING"),
|
||||
std::pair("WM/EncodingTime", "ENCODINGTIME"),
|
||||
std::pair("WM/AudioFileURL", "FILEWEBPAGE"),
|
||||
std::pair("WM/AlbumSortOrder", "ALBUMSORT"),
|
||||
std::pair("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT"),
|
||||
std::pair("WM/ArtistSortOrder", "ARTISTSORT"),
|
||||
std::pair("WM/TitleSortOrder", "TITLESORT"),
|
||||
std::pair("WM/Script", "SCRIPT"),
|
||||
std::pair("WM/Language", "LANGUAGE"),
|
||||
std::pair("WM/ARTISTS", "ARTISTS"),
|
||||
std::pair("ASIN", "ASIN"),
|
||||
std::pair("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID"),
|
||||
std::pair("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID"),
|
||||
std::pair("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID"),
|
||||
std::pair("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"),
|
||||
std::pair("MusicBrainz/Album Release Country", "RELEASECOUNTRY"),
|
||||
std::pair("MusicBrainz/Album Status", "RELEASESTATUS"),
|
||||
std::pair("MusicBrainz/Album Type", "RELEASETYPE"),
|
||||
std::pair("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"),
|
||||
std::pair("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID"),
|
||||
std::pair("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID"),
|
||||
std::pair("MusicIP/PUID", "MUSICIP_PUID"),
|
||||
std::pair("Acoustid/Id", "ACOUSTID_ID"),
|
||||
std::pair("Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT"),
|
||||
const char *keyTranslation[][2] = {
|
||||
{ "WM/AlbumTitle", "ALBUM" },
|
||||
{ "WM/AlbumArtist", "ALBUMARTIST" },
|
||||
{ "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" },
|
||||
};
|
||||
const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
|
||||
String translateKey(const String &key)
|
||||
{
|
||||
for(const auto &[k, t] : keyTranslation) {
|
||||
if(key == k)
|
||||
return t;
|
||||
for(size_t i = 0; i < keyTranslationSize; ++i) {
|
||||
if(key == keyTranslation[i][0])
|
||||
return keyTranslation[i][1];
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
} // namespace
|
||||
}
|
||||
|
||||
PropertyMap ASF::Tag::properties() const
|
||||
{
|
||||
@@ -312,23 +457,25 @@ PropertyMap ASF::Tag::properties() const
|
||||
props["COMMENT"] = d->comment;
|
||||
}
|
||||
|
||||
for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) {
|
||||
const String key = translateKey(k);
|
||||
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
|
||||
for(; it != d->attributeListMap.end(); ++it) {
|
||||
const String key = translateKey(it->first);
|
||||
if(!key.isEmpty()) {
|
||||
for(const auto &attr : attributes) {
|
||||
AttributeList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
if(key == "TRACKNUMBER") {
|
||||
if(attr.type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(attr.toUInt()));
|
||||
if(it2->type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(it2->toUInt()));
|
||||
else
|
||||
props.insert(key, attr.toString());
|
||||
props.insert(key, it2->toString());
|
||||
}
|
||||
else {
|
||||
props.insert(key, attr.toString());
|
||||
props.insert(key, it2->toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
props.addUnsupportedData(k);
|
||||
props.unsupportedData().append(it->first);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
@@ -336,116 +483,70 @@ PropertyMap ASF::Tag::properties() const
|
||||
|
||||
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
|
||||
{
|
||||
for(const auto &prop : props)
|
||||
d->attributeListMap.erase(prop);
|
||||
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()) {
|
||||
for(const auto &[k, t] : keyTranslation) {
|
||||
reverseKeyMap[t] = k;
|
||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
for(int i = 0; i < numKeys; i++) {
|
||||
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
|
||||
}
|
||||
}
|
||||
|
||||
const PropertyMap origProps = properties();
|
||||
for(const auto &[prop, _] : origProps) {
|
||||
if(!props.contains(prop) || props[prop].isEmpty()) {
|
||||
if(prop == "TITLE") {
|
||||
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.clear();
|
||||
}
|
||||
else if(prop == "ARTIST") {
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist.clear();
|
||||
}
|
||||
else if(prop == "COMMENT") {
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment.clear();
|
||||
}
|
||||
else if(prop == "COPYRIGHT") {
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright.clear();
|
||||
}
|
||||
else {
|
||||
d->attributeListMap.erase(reverseKeyMap[prop]);
|
||||
d->attributeListMap.erase(reverseKeyMap[it->first]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap ignoredProps;
|
||||
for(const auto &[prop, attributes] : props) {
|
||||
if(reverseKeyMap.contains(prop)) {
|
||||
String name = reverseKeyMap[prop];
|
||||
it = props.begin();
|
||||
for(; it != props.end(); ++it) {
|
||||
if(reverseKeyMap.contains(it->first)) {
|
||||
String name = reverseKeyMap[it->first];
|
||||
removeItem(name);
|
||||
for(const auto &attr : attributes) {
|
||||
addAttribute(name, attr);
|
||||
StringList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
addAttribute(name, *it2);
|
||||
}
|
||||
}
|
||||
else if(prop == "TITLE") {
|
||||
d->title = attributes.toString();
|
||||
else if(it->first == "TITLE") {
|
||||
d->title = it->second.toString();
|
||||
}
|
||||
else if(prop == "ARTIST") {
|
||||
d->artist = attributes.toString();
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist = it->second.toString();
|
||||
}
|
||||
else if(prop == "COMMENT") {
|
||||
d->comment = attributes.toString();
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment = it->second.toString();
|
||||
}
|
||||
else if(prop == "COPYRIGHT") {
|
||||
d->copyright = attributes.toString();
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright = it->second.toString();
|
||||
}
|
||||
else {
|
||||
ignoredProps.insert(prop, attributes);
|
||||
ignoredProps.insert(it->first, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
return ignoredProps;
|
||||
}
|
||||
|
||||
StringList ASF::Tag::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys;
|
||||
if(d->attributeListMap.contains("WM/Picture")) {
|
||||
keys.append("PICTURE");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> ASF::Tag::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props;
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
const AttributeList pictures = d->attributeListMap.value("WM/Picture");
|
||||
for(const Attribute &attr : pictures) {
|
||||
ASF::Picture picture = attr.toPicture();
|
||||
VariantMap property;
|
||||
property.insert("data", picture.picture());
|
||||
property.insert("mimeType", picture.mimeType());
|
||||
property.insert("description", picture.description());
|
||||
property.insert("pictureType",
|
||||
ASF::Picture::typeToString(picture.type()));
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
bool ASF::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
removeItem("WM/Picture");
|
||||
|
||||
for(const auto &property : value) {
|
||||
ASF::Picture picture;
|
||||
picture.setPicture(property.value("data").value<ByteVector>());
|
||||
picture.setMimeType(property.value("mimeType").value<String>());
|
||||
picture.setDescription(property.value("description").value<String>());
|
||||
picture.setType(ASF::Picture::typeFromString(
|
||||
property.value("pictureType").value<String>()));
|
||||
addAttribute("WM/Picture", Attribute(picture));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,18 +26,18 @@
|
||||
#ifndef TAGLIB_ASFTAG_H
|
||||
#define TAGLIB_ASFTAG_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tlist.h"
|
||||
#include "tmap.h"
|
||||
#include "taglib_export.h"
|
||||
#include "tag.h"
|
||||
#include "asfattribute.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace ASF {
|
||||
|
||||
using AttributeList = List<Attribute>;
|
||||
using AttributeListMap = Map<String, AttributeList>;
|
||||
typedef List<Attribute> AttributeList;
|
||||
typedef Map<String, AttributeList> AttributeListMap;
|
||||
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag {
|
||||
|
||||
@@ -47,37 +47,34 @@ namespace TagLib {
|
||||
|
||||
Tag();
|
||||
|
||||
~Tag() override;
|
||||
|
||||
Tag(const Tag &) = delete;
|
||||
Tag &operator=(const Tag &) = delete;
|
||||
virtual ~Tag();
|
||||
|
||||
/*!
|
||||
* Returns the track name.
|
||||
*/
|
||||
String title() const override;
|
||||
virtual String title() const;
|
||||
|
||||
/*!
|
||||
* Returns the artist name.
|
||||
*/
|
||||
String artist() const override;
|
||||
virtual String artist() const;
|
||||
|
||||
/*!
|
||||
* Returns the album name; if no album name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
String album() const override;
|
||||
virtual String album() const;
|
||||
|
||||
/*!
|
||||
* Returns the track comment.
|
||||
*/
|
||||
String comment() const override;
|
||||
virtual String comment() const;
|
||||
|
||||
/*!
|
||||
* Returns the genre name; if no genre is present in the tag String::null
|
||||
* will be returned.
|
||||
*/
|
||||
String genre() const override;
|
||||
virtual String genre() const;
|
||||
|
||||
/*!
|
||||
* Returns the rating.
|
||||
@@ -93,71 +90,73 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the year; if there is no year set, this will return 0.
|
||||
*/
|
||||
unsigned int year() const override;
|
||||
virtual unsigned int year() const;
|
||||
|
||||
/*!
|
||||
* Returns the track number; if there is no track number set, this will
|
||||
* return 0.
|
||||
*/
|
||||
unsigned int track() const override;
|
||||
virtual unsigned int track() const;
|
||||
|
||||
virtual PictureMap pictures() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a value.
|
||||
* Sets the title to \a s.
|
||||
*/
|
||||
void setTitle(const String &value) override;
|
||||
virtual void setTitle(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the artist to \a value.
|
||||
* Sets the artist to \a s.
|
||||
*/
|
||||
void setArtist(const String &value) override;
|
||||
virtual void setArtist(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the album to \a value. If \a value is String::null then this value will be
|
||||
* Sets the album to \a s. If \a s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
void setAlbum(const String &value) override;
|
||||
virtual void setAlbum(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the comment to \a value.
|
||||
* Sets the comment to \a s.
|
||||
*/
|
||||
void setComment(const String &value) override;
|
||||
virtual void setComment(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the rating to \a value.
|
||||
* Sets the rating to \a s.
|
||||
*/
|
||||
virtual void setRating(const String &value);
|
||||
virtual void setRating(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the copyright to \a value.
|
||||
* Sets the copyright to \a s.
|
||||
*/
|
||||
virtual void setCopyright(const String &value);
|
||||
virtual void setCopyright(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the genre to \a value.
|
||||
* Sets the genre to \a s.
|
||||
*/
|
||||
void setGenre(const String &value) override;
|
||||
virtual void setGenre(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the year to \a value. If \a value is 0 then this value will be cleared.
|
||||
* Sets the year to \a i. If \a s is 0 then this value will be cleared.
|
||||
*/
|
||||
void setYear(unsigned int value) override;
|
||||
virtual void setYear(unsigned int i);
|
||||
|
||||
/*!
|
||||
* Sets the track to \a value. If \a value is 0 then this value will be cleared.
|
||||
* Sets the track to \a i. If \a s is 0 then this value will be cleared.
|
||||
*/
|
||||
void setTrack(unsigned int value) override;
|
||||
virtual void setTrack(unsigned int i);
|
||||
|
||||
virtual void setPictures(const PictureMap &l);
|
||||
|
||||
/*!
|
||||
* Returns true if the tag does not contain any data. This should be
|
||||
* reimplemented in subclasses that provide more than the basic tagging
|
||||
* abilities in this class.
|
||||
*/
|
||||
bool isEmpty() const override;
|
||||
virtual bool isEmpty() const;
|
||||
|
||||
/*!
|
||||
* \warning You should not modify this data structure directly, instead
|
||||
* use attributeListMap() const, contains(), removeItem(),
|
||||
* attribute(), setAttribute(), addAttribute().
|
||||
* \deprecated
|
||||
*/
|
||||
AttributeListMap &attributeListMap();
|
||||
|
||||
@@ -168,14 +167,14 @@ namespace TagLib {
|
||||
const AttributeListMap &attributeListMap() const;
|
||||
|
||||
/*!
|
||||
* \return True if a value for \a key is currently set.
|
||||
* \return True if a value for \a attribute is currently set.
|
||||
*/
|
||||
bool contains(const String &key) const;
|
||||
bool contains(const String &name) const;
|
||||
|
||||
/*!
|
||||
* Removes the \a key attribute from the tag
|
||||
*/
|
||||
void removeItem(const String &key);
|
||||
void removeItem(const String &name);
|
||||
|
||||
/*!
|
||||
* \return The list of values for the key \a name, or an empty list if no
|
||||
@@ -184,8 +183,8 @@ namespace TagLib {
|
||||
AttributeList attribute(const String &name) const;
|
||||
|
||||
/*!
|
||||
* Sets the \a name attribute to the value of \a attribute. If an attribute
|
||||
* with the \a name is already present, it will be replaced.
|
||||
* Sets the \a key attribute to the value of \a attribute. If an attribute
|
||||
* with the \a key is already present, it will be replaced.
|
||||
*/
|
||||
void setAttribute(const String &name, const Attribute &attribute);
|
||||
|
||||
@@ -195,25 +194,20 @@ namespace TagLib {
|
||||
void setAttribute(const String &name, const AttributeList &values);
|
||||
|
||||
/*!
|
||||
* Sets the \a name attribute to the value of \a attribute. If an attribute
|
||||
* with the \a name is already present, it will be added to the list.
|
||||
* Sets the \a key attribute to the value of \a attribute. If an attribute
|
||||
* with the \a key is already present, it will be added to the list.
|
||||
*/
|
||||
void addAttribute(const String &name, const Attribute &attribute);
|
||||
|
||||
PropertyMap properties() const override;
|
||||
void removeUnsupportedProperties(const StringList &props) override;
|
||||
PropertyMap setProperties(const PropertyMap &props) override;
|
||||
|
||||
StringList complexPropertyKeys() const override;
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
PropertyMap properties() const;
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
private:
|
||||
|
||||
class TagPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
TagPrivate *d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace TagLib
|
||||
namespace
|
||||
{
|
||||
|
||||
inline unsigned short readWORD(File *file, bool *ok = nullptr)
|
||||
inline unsigned short readWORD(File *file, bool *ok = 0)
|
||||
{
|
||||
const ByteVector v = file->readBlock(2);
|
||||
if(v.size() != 2) {
|
||||
@@ -45,10 +45,10 @@ namespace TagLib
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUShort(false);
|
||||
return v.toUInt16LE(0);
|
||||
}
|
||||
|
||||
inline unsigned int readDWORD(File *file, bool *ok = nullptr)
|
||||
inline unsigned int readDWORD(File *file, bool *ok = 0)
|
||||
{
|
||||
const ByteVector v = file->readBlock(4);
|
||||
if(v.size() != 4) {
|
||||
@@ -56,10 +56,10 @@ namespace TagLib
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUInt(false);
|
||||
return v.toUInt32LE(0);
|
||||
}
|
||||
|
||||
inline long long readQWORD(File *file, bool *ok = nullptr)
|
||||
inline long long readQWORD(File *file, bool *ok = 0)
|
||||
{
|
||||
const ByteVector v = file->readBlock(8);
|
||||
if(v.size() != 8) {
|
||||
@@ -67,13 +67,13 @@ namespace TagLib
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toLongLong(false);
|
||||
return v.toInt64LE(0);
|
||||
}
|
||||
|
||||
inline String readString(File *file, int length)
|
||||
{
|
||||
ByteVector data = file->readBlock(length);
|
||||
unsigned int size = data.size();
|
||||
size_t size = data.size();
|
||||
while (size >= 2) {
|
||||
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
|
||||
break;
|
||||
@@ -88,16 +88,16 @@ namespace TagLib
|
||||
|
||||
inline ByteVector renderString(const String &str, bool includeLength = false)
|
||||
{
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromUInt16LE(0);
|
||||
if(includeLength) {
|
||||
data = ByteVector::fromShort(data.size(), false) + data;
|
||||
data = ByteVector::fromUInt16LE(data.size()) + data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -23,49 +23,34 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tstringlist.h>
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioProperties::~AudioProperties() = default;
|
||||
|
||||
int AudioProperties::length() const
|
||||
AudioProperties::~AudioProperties()
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int AudioProperties::lengthInSeconds() const
|
||||
String AudioProperties::toString() const
|
||||
{
|
||||
return lengthInMilliseconds() / 1000;
|
||||
}
|
||||
|
||||
int AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioProperties::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioProperties::sampleRate() const
|
||||
{
|
||||
return 0;
|
||||
StringList desc;
|
||||
desc.append("Audio");
|
||||
desc.append(String::number(length()) + " seconds");
|
||||
desc.append(String::number(bitrate()) + " kbps");
|
||||
return desc.toString(", ");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioProperties::AudioProperties(ReadStyle)
|
||||
AudioProperties::AudioProperties() :
|
||||
d(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -26,10 +26,8 @@
|
||||
#ifndef TAGLIB_AUDIOPROPERTIES_H
|
||||
#define TAGLIB_AUDIOPROPERTIES_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "taglib.h"
|
||||
#include "taglib_export.h"
|
||||
#include "tstring.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
@@ -67,19 +65,10 @@ namespace TagLib {
|
||||
*/
|
||||
virtual ~AudioProperties();
|
||||
|
||||
AudioProperties(const AudioProperties &) = delete;
|
||||
AudioProperties &operator=(const AudioProperties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated Use lengthInSeconds().
|
||||
*/
|
||||
TAGLIB_DEPRECATED
|
||||
virtual int length() const;
|
||||
* Returns the length of the file in seconds.
|
||||
*/
|
||||
virtual int length() const = 0;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -87,49 +76,55 @@ namespace TagLib {
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
virtual int lengthInSeconds() const;
|
||||
virtual int lengthInSeconds() const = 0;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
virtual int lengthInMilliseconds() const;
|
||||
virtual int lengthInMilliseconds() const = 0;
|
||||
|
||||
/*!
|
||||
* Returns the most appropriate bit rate for the file in kb/s. For constant
|
||||
* bitrate formats this is simply the bitrate of the file. For variable
|
||||
* bitrate formats this is either the average or nominal bitrate.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
virtual int bitrate() const = 0;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
virtual int sampleRate() const = 0;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const = 0;
|
||||
|
||||
/*!
|
||||
* Returns description of the audio file.
|
||||
*/
|
||||
virtual String toString() const;
|
||||
|
||||
protected:
|
||||
|
||||
/*!
|
||||
* Construct an audio properties instance. This is protected as this class
|
||||
* Constructs an audio properties instance. This is protected as this class
|
||||
* should not be instantiated directly, but should be instantiated via its
|
||||
* subclasses and can be fetched from the FileRef or File APIs.
|
||||
*
|
||||
* \see ReadStyle
|
||||
*/
|
||||
AudioProperties(ReadStyle style);
|
||||
AudioProperties();
|
||||
|
||||
private:
|
||||
class AudioPropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AudioPropertiesPrivate> d;
|
||||
// Noncopyable. Derived classes as well.
|
||||
AudioProperties(const AudioProperties &);
|
||||
AudioProperties &operator=(const AudioProperties &);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
|
||||
} // namespace TagLib
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -24,12 +24,9 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsdiffdiintag.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tdebug.h"
|
||||
#include "tpicturemap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace DSDIFF::DIIN;
|
||||
@@ -37,16 +34,23 @@ using namespace DSDIFF::DIIN;
|
||||
class DSDIFF::DIIN::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
String title;
|
||||
String artist;
|
||||
};
|
||||
|
||||
DSDIFF::DIIN::Tag::Tag() :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
DSDIFF::DIIN::Tag::Tag() : TagLib::Tag()
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag::~Tag() = default;
|
||||
DSDIFF::DIIN::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::title() const
|
||||
{
|
||||
@@ -83,6 +87,11 @@ unsigned int DSDIFF::DIIN::Tag::track() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
PictureMap DSDIFF::DIIN::Tag::pictures() const
|
||||
{
|
||||
return PictureMap();
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setTitle(const String &title)
|
||||
{
|
||||
d->title = title;
|
||||
@@ -95,27 +104,26 @@ void DSDIFF::DIIN::Tag::setArtist(const String &artist)
|
||||
|
||||
void DSDIFF::DIIN::Tag::setAlbum(const String &)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setAlbum() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setComment(const String &)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setComment() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setGenre(const String &)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setGenre() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setYear(unsigned int)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setYear() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setTrack() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setPictures( const PictureMap& l )
|
||||
{
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::DIIN::Tag::properties() const
|
||||
@@ -128,29 +136,31 @@ PropertyMap DSDIFF::DIIN::Tag::properties() const
|
||||
|
||||
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap props(origProps);
|
||||
props.removeEmpty();
|
||||
PropertyMap properties(origProps);
|
||||
properties.removeEmpty();
|
||||
StringList oneValueSet;
|
||||
|
||||
if(props.contains("TITLE")) {
|
||||
d->title = props["TITLE"].front();
|
||||
if(properties.contains("TITLE")) {
|
||||
d->title = properties["TITLE"].front();
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
d->title.clear();
|
||||
d->title = String();
|
||||
|
||||
if(props.contains("ARTIST")) {
|
||||
d->artist = props["ARTIST"].front();
|
||||
if(properties.contains("ARTIST")) {
|
||||
d->artist = properties["ARTIST"].front();
|
||||
oneValueSet.append("ARTIST");
|
||||
} else
|
||||
d->artist.clear();
|
||||
d->artist = String();
|
||||
|
||||
// for each tag that has been set above, remove the first entry in the corresponding
|
||||
// value list. The others will be returned as unsupported by this format.
|
||||
for(const auto &entry : std::as_const(oneValueSet)) {
|
||||
if(props[entry].size() == 1)
|
||||
props.erase(entry);
|
||||
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
|
||||
if(properties[*it].size() == 1)
|
||||
properties.erase(*it);
|
||||
else
|
||||
props[entry].erase(props[entry].begin());
|
||||
properties[*it].erase(properties[*it].begin());
|
||||
}
|
||||
return props;
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -43,87 +43,97 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
Tag();
|
||||
~Tag() override;
|
||||
virtual ~Tag();
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
String title() const override;
|
||||
virtual String title() const;
|
||||
|
||||
/*!
|
||||
* Returns the artist name; if no artist name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
String artist() const override;
|
||||
virtual String artist() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String album() const override;
|
||||
virtual String album() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String comment() const override;
|
||||
virtual String comment() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String genre() const override;
|
||||
virtual String genre() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
unsigned int year() const override;
|
||||
virtual unsigned int year() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
unsigned int track() const override;
|
||||
virtual unsigned int track() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns an empty list.
|
||||
*/
|
||||
virtual PictureMap pictures() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a title. If \a title is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
void setTitle(const String &title) override;
|
||||
virtual void setTitle(const String &title);
|
||||
|
||||
/*!
|
||||
* Sets the artist to \a artist. If \a artist is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
void setArtist(const String &artist) override;
|
||||
virtual void setArtist(const String &artist);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setAlbum(const String &album) override;
|
||||
virtual void setAlbum(const String &album);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setComment(const String &comment) override;
|
||||
virtual void setComment(const String &comment);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setGenre(const String &genre) override;
|
||||
virtual void setGenre(const String &genre);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setYear(unsigned int year) override;
|
||||
virtual void setYear(unsigned int year);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setTrack(unsigned int track) override;
|
||||
virtual void setTrack(unsigned int track);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
virtual void setPictures( const PictureMap& l );
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Since the DIIN tag is very limited, the exported map is as well.
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
@@ -133,15 +143,18 @@ namespace TagLib {
|
||||
* all but the first will be contained in the returned map of unsupported
|
||||
* properties.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
TagPrivate *d;
|
||||
};
|
||||
} // namespace DIIN
|
||||
} // namespace DSDIFF
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -42,7 +42,7 @@ namespace TagLib {
|
||||
* chunk as well as properties from the file.
|
||||
* Description of the DSDIFF format is available
|
||||
* at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
|
||||
* DSDIFF standard does not explicitly specify the ID3V2 chunk
|
||||
* DSDIFF standard does not explictly specify the ID3V2 chunk
|
||||
* It can be found at the root level, but also sometimes inside the PROP chunk
|
||||
* In addition, title and artist info are stored as part of the standard
|
||||
*/
|
||||
@@ -61,55 +61,31 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
* This set of flags is used for various operations and is suitable for
|
||||
* being OR-ed together.
|
||||
*/
|
||||
enum TagTypes {
|
||||
//! Empty set. Matches no tag types.
|
||||
NoTags = 0x0000,
|
||||
//! Matches DIIN tags.
|
||||
DIIN = 0x0001,
|
||||
//! Matches ID3v2 tags.
|
||||
ID3v2 = 0x0002,
|
||||
//! Matches all tag types.
|
||||
AllTags = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
* Constructs a DSDIFF file from \a file. If \a readProperties is true
|
||||
* Constructs an DSDIFF file from \a file. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs an DSDIFF file from \a stream. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN
|
||||
@@ -127,7 +103,7 @@ namespace TagLib {
|
||||
* \see ID3v2Tag()
|
||||
* \see DIINTag()
|
||||
*/
|
||||
Tag *tag() const override;
|
||||
virtual Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the ID3V2 Tag for this file.
|
||||
@@ -138,33 +114,33 @@ namespace TagLib {
|
||||
*
|
||||
* \see hasID3v2Tag()
|
||||
*/
|
||||
ID3v2::Tag *ID3v2Tag(bool create = false) const;
|
||||
virtual ID3v2::Tag *ID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the DSDIFF DIIN Tag for this file
|
||||
*
|
||||
*/
|
||||
DSDIFF::DIIN::Tag *DIINTag(bool create = false) const;
|
||||
DSDIFF::DIIN::Tag *DIINTag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This method forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
PropertyMap properties() const;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the AIFF::Properties for this file. If no audio properties
|
||||
* Returns the DSDIFF::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
|
||||
@@ -181,24 +157,18 @@ namespace TagLib {
|
||||
*
|
||||
* \see save(int tags)
|
||||
*/
|
||||
bool save() override;
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Save the file. If \a strip is specified, it is possible to choose if
|
||||
* tags not specified in \a tags should be stripped from the file or
|
||||
* retained. With \a version, it is possible to specify whether ID3v2.4
|
||||
* or ID3v2.3 should be used.
|
||||
*/
|
||||
bool save(int tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4);
|
||||
|
||||
/*!
|
||||
* This will strip the tags that match the OR-ed together TagTypes from the
|
||||
* file. By default it strips all tags. It returns true if the tags are
|
||||
* successfully stripped.
|
||||
* 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.
|
||||
*
|
||||
* \note This will update the file immediately.
|
||||
* 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.
|
||||
*/
|
||||
void strip(int tags = AllTags);
|
||||
bool save(int tags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
@@ -209,7 +179,7 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has the DSDIFF
|
||||
* title and artist tags.
|
||||
* Title & Artist tag.
|
||||
*
|
||||
* \see DIINTag()
|
||||
*/
|
||||
@@ -231,10 +201,8 @@ namespace TagLib {
|
||||
File(IOStream *stream, Endianness endianness);
|
||||
|
||||
private:
|
||||
void removeRootChunk(const ByteVector &id);
|
||||
void removeRootChunk(unsigned int i);
|
||||
void removeChildChunk(const ByteVector &id, unsigned int childChunkNum);
|
||||
void removeChildChunk(unsigned int i, unsigned int childChunkNum);
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
/*!
|
||||
* Sets the data for the the specified chunk at root level to \a data.
|
||||
@@ -277,16 +245,16 @@ namespace TagLib {
|
||||
|
||||
void updateRootChunksStructure(unsigned int startingChunk);
|
||||
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle);
|
||||
void writeChunk(const ByteVector &name, const ByteVector &data,
|
||||
unsigned long long offset, unsigned long replace = 0,
|
||||
unsigned int leadingPadding = 0);
|
||||
|
||||
class FilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
FilePrivate *d;
|
||||
};
|
||||
} // namespace DSDIFF
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -23,79 +23,95 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsdiffproperties.h"
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tdebug.h"
|
||||
#include "dsdiffproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSDIFF::Properties::PropertiesPrivate
|
||||
class DSDIFF::AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
public:
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int sampleWidth { 0 };
|
||||
unsigned long long sampleCount { 0 };
|
||||
AudioPropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
sampleWidth(0),
|
||||
sampleCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int sampleWidth;
|
||||
unsigned long long sampleCount;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSDIFF::Properties::Properties(const unsigned int sampleRate,
|
||||
const unsigned short channels,
|
||||
const unsigned long long samplesCount,
|
||||
const int bitrate,
|
||||
ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
DSDIFF::AudioProperties::AudioProperties(const unsigned int sampleRate,
|
||||
const unsigned short channels,
|
||||
const unsigned long long samplesCount,
|
||||
const int bitrate,
|
||||
ReadStyle style) : TagLib::AudioProperties(), d(new AudioPropertiesPrivate)
|
||||
{
|
||||
d->channels = channels;
|
||||
d->channels = channels;
|
||||
d->sampleCount = samplesCount;
|
||||
d->sampleWidth = 1;
|
||||
d->sampleRate = sampleRate;
|
||||
d->bitrate = bitrate;
|
||||
d->length = d->sampleRate > 0
|
||||
d->sampleRate = sampleRate;
|
||||
d->bitrate = bitrate;
|
||||
d->length = d->sampleRate > 0
|
||||
? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5)
|
||||
: 0;
|
||||
}
|
||||
|
||||
DSDIFF::Properties::~Properties() = default;
|
||||
DSDIFF::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::lengthInSeconds() const
|
||||
int DSDIFF::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int DSDIFF::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::lengthInMilliseconds() const
|
||||
int DSDIFF::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::bitrate() const
|
||||
int DSDIFF::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::sampleRate() const
|
||||
int DSDIFF::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::channels() const
|
||||
int DSDIFF::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::bitsPerSample() const
|
||||
int DSDIFF::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->sampleWidth;
|
||||
}
|
||||
|
||||
long long DSDIFF::Properties::sampleCount() const
|
||||
long long DSDIFF::AudioProperties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
@@ -41,39 +41,43 @@ namespace TagLib {
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of DSDIFF::Properties with the data read from the
|
||||
* Create an instance of DSDIFF::AudioProperties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
Properties(const unsigned int sampleRate, const unsigned short channels,
|
||||
const unsigned long long samplesCount, const int bitrate,
|
||||
ReadStyle style);
|
||||
AudioProperties(const unsigned int sampleRate, const unsigned short channels,
|
||||
const unsigned long long samplesCount, const int bitrate,
|
||||
ReadStyle style);
|
||||
|
||||
/*!
|
||||
* Destroys this DSDIFF::Properties instance.
|
||||
* Destroys this DSDIFF::AudioProperties instance.
|
||||
*/
|
||||
~Properties() override;
|
||||
virtual ~AudioProperties();
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
int lengthInSeconds() const override;
|
||||
int lengthInMilliseconds() const override;
|
||||
int bitrate() const override;
|
||||
int sampleRate() const override;
|
||||
int channels() const override;
|
||||
virtual int length() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
|
||||
int bitsPerSample() const;
|
||||
long long sampleCount() const;
|
||||
|
||||
private:
|
||||
class PropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
AudioProperties(const AudioProperties &);
|
||||
AudioProperties &operator=(const AudioProperties &);
|
||||
|
||||
class AudioPropertiesPrivate;
|
||||
AudioPropertiesPrivate *d;
|
||||
};
|
||||
} // namespace DSDIFF
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
copyright : (C) 2013 - 2018 by Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -23,11 +23,15 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsffile.h"
|
||||
#include <tbytevector.h>
|
||||
#include <tdebug.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tsmartptr.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tagutils.h"
|
||||
#include "dsffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -36,58 +40,65 @@ using namespace TagLib;
|
||||
class DSF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(ID3v2::FrameFactory *frameFactory)
|
||||
: ID3v2FrameFactory(frameFactory ? frameFactory
|
||||
: ID3v2::FrameFactory::instance())
|
||||
{
|
||||
}
|
||||
FilePrivate() :
|
||||
fileSize(0),
|
||||
metadataOffset(0) {}
|
||||
|
||||
~FilePrivate() = default;
|
||||
long long fileSize;
|
||||
long long metadataOffset;
|
||||
|
||||
FilePrivate(const FilePrivate &) = delete;
|
||||
FilePrivate &operator=(const FilePrivate &) = delete;
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
long long fileSize = 0;
|
||||
long long metadataOffset = 0;
|
||||
std::unique_ptr<Properties> properties;
|
||||
std::unique_ptr<ID3v2::Tag> tag;
|
||||
SCOPED_PTR<AudioProperties> properties;
|
||||
SCOPED_PTR<ID3v2::Tag> tag;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool DSF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A DSF file has to start with "DSD "
|
||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
||||
return id.startsWith("DSD ");
|
||||
// A DSF file has to start with "DSD "
|
||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
||||
return id.startsWith("DSD ");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSF::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(propertiesStyle);
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(propertiesStyle);
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::~File() = default;
|
||||
DSF::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
ID3v2::Tag *DSF::File::tag() const
|
||||
{
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
DSF::AudioProperties *DSF::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
PropertyMap DSF::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
@@ -98,20 +109,15 @@ PropertyMap DSF::File::setProperties(const PropertyMap &properties)
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
DSF::Properties *DSF::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool DSF::File::save()
|
||||
{
|
||||
return save(ID3v2::v4);
|
||||
}
|
||||
|
||||
bool DSF::File::save(ID3v2::Version version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("DSF::File::save() - Cannot save to a read only file.");
|
||||
debug("DSF::File::save() -- File is read only.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isValid()) {
|
||||
debug("DSF::File::save() -- Trying to save invalid file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -122,13 +128,13 @@ bool DSF::File::save(ID3v2::Version version)
|
||||
|
||||
// Update the file size
|
||||
if(d->fileSize != newFileSize) {
|
||||
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
|
||||
insert(ByteVector::fromUInt64LE(newFileSize), 12, 8);
|
||||
d->fileSize = newFileSize;
|
||||
}
|
||||
|
||||
// Update the metadata offset to 0 since there is no longer a tag
|
||||
if(d->metadataOffset) {
|
||||
insert(ByteVector::fromLongLong(0ULL, false), 20, 8);
|
||||
insert(ByteVector::fromUInt64LE(0ULL), 20, 8);
|
||||
d->metadataOffset = 0;
|
||||
}
|
||||
|
||||
@@ -136,7 +142,7 @@ bool DSF::File::save(ID3v2::Version version)
|
||||
truncate(newFileSize);
|
||||
}
|
||||
else {
|
||||
ByteVector tagData = d->tag->render(version);
|
||||
ByteVector tagData = d->tag->render();
|
||||
|
||||
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
|
||||
long long newFileSize = newMetadataOffset + tagData.size();
|
||||
@@ -144,13 +150,13 @@ bool DSF::File::save(ID3v2::Version version)
|
||||
|
||||
// Update the file size
|
||||
if(d->fileSize != newFileSize) {
|
||||
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
|
||||
insert(ByteVector::fromUInt64LE(newFileSize), 12, 8);
|
||||
d->fileSize = newFileSize;
|
||||
}
|
||||
|
||||
// Update the metadata offset
|
||||
if(d->metadataOffset != newMetadataOffset) {
|
||||
insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8);
|
||||
insert(ByteVector::fromUInt64LE(newMetadataOffset), 20, 8);
|
||||
d->metadataOffset = newMetadataOffset;
|
||||
}
|
||||
|
||||
@@ -161,11 +167,12 @@ bool DSF::File::save(ID3v2::Version version)
|
||||
return true;
|
||||
}
|
||||
|
||||
void DSF::File::read(AudioProperties::ReadStyle propertiesStyle)
|
||||
{
|
||||
if(!isOpen())
|
||||
return;
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesStyle)
|
||||
{
|
||||
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
|
||||
// The file format is not chunked in the sense of a RIFF File, though
|
||||
|
||||
@@ -177,25 +184,25 @@ void DSF::File::read(AudioProperties::ReadStyle propertiesStyle)
|
||||
return;
|
||||
}
|
||||
|
||||
long long dsdHeaderSize = readBlock(8).toLongLong(false);
|
||||
long long chunkSize = readBlock(8).toInt64LE(0);
|
||||
|
||||
// Integrity check
|
||||
if(dsdHeaderSize != 28) {
|
||||
debug("DSF::File::read() -- File is corrupted, wrong DSD header size");
|
||||
if(28 != chunkSize) {
|
||||
debug("DSF::File::read() -- File is corrupted.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->fileSize = readBlock(8).toLongLong(false);
|
||||
d->fileSize = readBlock(8).toInt64LE(0);
|
||||
|
||||
// File is malformed or corrupted, allow trailing garbage
|
||||
if(d->fileSize > length()) {
|
||||
debug("DSF::File::read() -- File is corrupted wrong length");
|
||||
// File is malformed or corrupted
|
||||
if(d->fileSize != length()) {
|
||||
debug("DSF::File::read() -- File is corrupted.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->metadataOffset = readBlock(8).toLongLong(false);
|
||||
d->metadataOffset = readBlock(8).toInt64LE(0);
|
||||
|
||||
// File is malformed or corrupted
|
||||
if(d->metadataOffset > d->fileSize) {
|
||||
@@ -212,21 +219,16 @@ void DSF::File::read(AudioProperties::ReadStyle propertiesStyle)
|
||||
return;
|
||||
}
|
||||
|
||||
long long fmtHeaderSize = readBlock(8).toLongLong(false);
|
||||
if(fmtHeaderSize != 52) {
|
||||
debug("DSF::File::read() -- File is corrupted, wrong FMT header size");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
chunkSize = readBlock(8).toInt64LE(0);
|
||||
|
||||
d->properties = std::make_unique<Properties>(readBlock(fmtHeaderSize), propertiesStyle);
|
||||
d->properties.reset(
|
||||
new AudioProperties(readBlock(static_cast<size_t>(chunkSize)), propertiesStyle));
|
||||
|
||||
// Skip the data chunk
|
||||
|
||||
// A metadata offset of 0 indicates the absence of an ID3v2 tag
|
||||
if(d->metadataOffset == 0)
|
||||
d->tag = std::make_unique<ID3v2::Tag>(nullptr, 0, d->ID3v2FrameFactory);
|
||||
if(0 == d->metadataOffset)
|
||||
d->tag.reset(new ID3v2::Tag());
|
||||
else
|
||||
d->tag = std::make_unique<ID3v2::Tag>(this, d->metadataOffset,
|
||||
d->ID3v2FrameFactory);
|
||||
d->tag.reset(new ID3v2::Tag(this, d->metadataOffset));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
copyright : (C) 2013 - 2018 by Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -26,111 +26,102 @@
|
||||
#ifndef TAGLIB_DSFFILE_H
|
||||
#define TAGLIB_DSFFILE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
|
||||
#include "id3v2tag.h"
|
||||
#include "dsfproperties.h"
|
||||
|
||||
#include "id3v2tag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of DSF metadata
|
||||
|
||||
/*!
|
||||
* This is implementation of DSF metadata.
|
||||
*
|
||||
* This supports an ID3v2 tag as well as properties from the file.
|
||||
*/
|
||||
|
||||
namespace DSF {
|
||||
class TAGLIB_EXPORT File : public TagLib::File {
|
||||
public:
|
||||
/*!
|
||||
* Constructs a DSD stream file from \a file.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Constructs a DSD stream file from \a stream.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
//! An implementation of TagLib::File with DSF specific methods
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
/*!
|
||||
* This implements and provides an interface for DSF files to the
|
||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
||||
* the abstract TagLib::File API as well as providing some additional
|
||||
* information specific to DSF files.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Returns the ID3v2 Tag for this file.
|
||||
*/
|
||||
ID3v2::Tag *tag() const override;
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs an DSF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
/*!
|
||||
* Contructs an DSF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns the DSF::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
/*!
|
||||
* Returns the Tag for this file.
|
||||
*/
|
||||
virtual ID3v2::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*/
|
||||
bool save() override;
|
||||
/*!
|
||||
* Returns the DSF::AudioProperties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* \a version specifies the ID3v2 version to be used for writing tags.
|
||||
*/
|
||||
bool save(ID3v2::Version version);
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This method forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
virtual PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a DSF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
virtual PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
private:
|
||||
void read(AudioProperties::ReadStyle propertiesStyle);
|
||||
/*!
|
||||
* Saves the file.
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
class FilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a DSF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
} // namespace DSF
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
copyright : (C) 2013 by Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -23,90 +23,115 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "dsfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSF::Properties::PropertiesPrivate
|
||||
class DSF::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() = default;
|
||||
~PropertiesPrivate() = default;
|
||||
|
||||
PropertiesPrivate(const PropertiesPrivate &) = delete;
|
||||
PropertiesPrivate &operator=(const PropertiesPrivate &) = delete;
|
||||
PropertiesPrivate() :
|
||||
formatVersion(0),
|
||||
formatID(0),
|
||||
channelType(0),
|
||||
channelNum(0),
|
||||
samplingFrequency(0),
|
||||
bitsPerSample(0),
|
||||
sampleCount(0),
|
||||
blockSizePerChannel(0),
|
||||
bitrate(0),
|
||||
length(0) {}
|
||||
|
||||
// Nomenclature is from DSF file format specification
|
||||
unsigned int formatVersion = 0;
|
||||
unsigned int formatID = 0;
|
||||
unsigned int channelType = 0;
|
||||
unsigned int channelNum = 0;
|
||||
unsigned int samplingFrequency = 0;
|
||||
unsigned int bitsPerSample = 0;
|
||||
long long sampleCount = 0;
|
||||
unsigned int blockSizePerChannel = 0;
|
||||
unsigned int formatVersion;
|
||||
unsigned int formatID;
|
||||
unsigned int channelType;
|
||||
unsigned int channelNum;
|
||||
unsigned int samplingFrequency;
|
||||
unsigned int bitsPerSample;
|
||||
long long sampleCount;
|
||||
unsigned int blockSizePerChannel;
|
||||
|
||||
// Computed
|
||||
unsigned int bitrate = 0;
|
||||
unsigned int length = 0;
|
||||
int bitrate;
|
||||
int length;
|
||||
};
|
||||
|
||||
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSF::AudioProperties::AudioProperties(const ByteVector &data, ReadStyle style) :
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
read(data);
|
||||
}
|
||||
|
||||
DSF::Properties::~Properties() = default;
|
||||
DSF::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int DSF::Properties::lengthInMilliseconds() const
|
||||
int DSF::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int DSF::Properties::bitrate() const
|
||||
int DSF::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int DSF::Properties::sampleRate() const
|
||||
int DSF::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->samplingFrequency;
|
||||
}
|
||||
|
||||
int DSF::Properties::channels() const
|
||||
int DSF::AudioProperties::channels() const
|
||||
{
|
||||
return d->channelNum;
|
||||
}
|
||||
|
||||
int DSF::Properties::formatVersion() const
|
||||
// DSF specific
|
||||
int DSF::AudioProperties::formatVersion() const
|
||||
{
|
||||
return d->formatVersion;
|
||||
}
|
||||
|
||||
int DSF::Properties::formatID() const
|
||||
int DSF::AudioProperties::formatID() const
|
||||
{
|
||||
return d->formatID;
|
||||
}
|
||||
|
||||
int DSF::Properties::channelType() const
|
||||
int DSF::AudioProperties::channelType() const
|
||||
{
|
||||
return d->channelType;
|
||||
}
|
||||
|
||||
int DSF::Properties::bitsPerSample() const
|
||||
int DSF::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
long long DSF::Properties::sampleCount() const
|
||||
long long DSF::AudioProperties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
int DSF::Properties::blockSizePerChannel() const
|
||||
int DSF::AudioProperties::blockSizePerChannel() const
|
||||
{
|
||||
return d->blockSizePerChannel;
|
||||
}
|
||||
@@ -115,20 +140,19 @@ int DSF::Properties::blockSizePerChannel() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSF::Properties::read(const ByteVector &data)
|
||||
void DSF::AudioProperties::read(const ByteVector &data)
|
||||
{
|
||||
d->formatVersion = data.toUInt(0U,false);
|
||||
d->formatID = data.toUInt(4U,false);
|
||||
d->channelType = data.toUInt(8U,false);
|
||||
d->channelNum = data.toUInt(12U,false);
|
||||
d->samplingFrequency = data.toUInt(16U,false);
|
||||
d->bitsPerSample = data.toUInt(20U,false);
|
||||
d->sampleCount = data.toLongLong(24U,false);
|
||||
d->blockSizePerChannel = data.toUInt(32U,false);
|
||||
d->formatVersion = data.toUInt32LE(0);
|
||||
d->formatID = data.toUInt32LE(4);
|
||||
d->channelType = data.toUInt32LE(8);
|
||||
d->channelNum = data.toUInt32LE(12);
|
||||
d->samplingFrequency = data.toUInt32LE(16);
|
||||
d->bitsPerSample = data.toUInt32LE(20);
|
||||
d->sampleCount = data.toInt64LE(24);
|
||||
d->blockSizePerChannel = data.toUInt32LE(32);
|
||||
|
||||
d->bitrate = static_cast<unsigned int>(
|
||||
(d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0 + 0.5);
|
||||
d->length = d->samplingFrequency > 0
|
||||
? static_cast<unsigned int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5)
|
||||
: 0;
|
||||
d->bitrate = static_cast<int>(d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5);
|
||||
|
||||
if(d->samplingFrequency > 0)
|
||||
d->length = static_cast<int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
copyright : (C) 2013 by Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -26,26 +26,43 @@
|
||||
#ifndef TAGLIB_DSFPROPERTIES_H
|
||||
#define TAGLIB_DSFPROPERTIES_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tbytevector.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSF {
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for DSF
|
||||
|
||||
/*!
|
||||
* This reads the data from a DSF stream found in the AudioProperties
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
Properties(const ByteVector &data, ReadStyle style);
|
||||
~Properties() override;
|
||||
/*!
|
||||
* Create an instance of DSF::AudioProperties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
AudioProperties(const ByteVector &data, ReadStyle style);
|
||||
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
/*!
|
||||
* Destroys this DSF::AudioProperties instance.
|
||||
*/
|
||||
virtual ~AudioProperties();
|
||||
|
||||
int lengthInMilliseconds() const override;
|
||||
int bitrate() const override;
|
||||
int sampleRate() const override;
|
||||
int channels() const override;
|
||||
// Reimplementations.
|
||||
|
||||
virtual int length() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
|
||||
int formatVersion() const;
|
||||
int formatID() const;
|
||||
@@ -60,13 +77,15 @@ namespace TagLib {
|
||||
int blockSizePerChannel() const;
|
||||
|
||||
private:
|
||||
AudioProperties(const AudioProperties &);
|
||||
AudioProperties &operator=(const AudioProperties &);
|
||||
|
||||
void read(const ByteVector &data);
|
||||
|
||||
class PropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
} // namespace DSF
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2023 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -23,54 +23,36 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "tpicturetype.h"
|
||||
#ifndef TAGLIB_EBML_CONSTANTS
|
||||
#define TAGLIB_EBML_CONSTANTS
|
||||
|
||||
#include "tstring.h"
|
||||
namespace TagLib {
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char *typeStrs[] = {
|
||||
"Other",
|
||||
"File Icon",
|
||||
"Other File Icon",
|
||||
"Front Cover",
|
||||
"Back Cover",
|
||||
"Leaflet Page",
|
||||
"Media",
|
||||
"Lead Artist",
|
||||
"Artist",
|
||||
"Conductor",
|
||||
"Band",
|
||||
"Composer",
|
||||
"Lyricist",
|
||||
"Recording Location",
|
||||
"During Recording",
|
||||
"During Performance",
|
||||
"Movie Screen Capture",
|
||||
"Coloured Fish",
|
||||
"Illustration",
|
||||
"Band Logo",
|
||||
"Publisher Logo",
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
String Utils::pictureTypeToString(int type)
|
||||
{
|
||||
if(type >= 0 && type < static_cast<int>(std::size(typeStrs))) {
|
||||
return typeStrs[type];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
int Utils::pictureTypeFromString(const String& str)
|
||||
{
|
||||
for(int i = 0; i < static_cast<int>(std::size(typeStrs)); ++i) {
|
||||
if(str == typeStrs[i]) {
|
||||
return i;
|
||||
namespace EBML {
|
||||
//! Shorter representation of the type.
|
||||
typedef unsigned long long int ulli;
|
||||
|
||||
//! The id of an EBML Void element that is just a placeholder.
|
||||
const ulli Void = 0xecL;
|
||||
|
||||
//! The id of an EBML CRC32 element that contains a crc32 value.
|
||||
const ulli CRC32 = 0xc3L;
|
||||
|
||||
//! A namespace containing the ids of the EBML header's elements.
|
||||
namespace Header {
|
||||
const ulli EBML = 0x1a45dfa3L;
|
||||
const ulli EBMLVersion = 0x4286L;
|
||||
const ulli EBMLReadVersion = 0x42f7L;
|
||||
const ulli EBMLMaxIDWidth = 0x42f2L;
|
||||
const ulli EBMLMaxSizeWidth = 0x42f3L;
|
||||
const ulli DocType = 0x4282L;
|
||||
const ulli DocTypeVersion = 0x4287L;
|
||||
const ulli DocTypeReadVersion = 0x4285L;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
489
taglib/ebml/ebmlelement.cpp
Normal file
489
taglib/ebml/ebmlelement.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "ebmlelement.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Element::ElementPrivate
|
||||
{
|
||||
public:
|
||||
// The id of this element.
|
||||
ulli id;
|
||||
|
||||
// The position of the element, where the header begins.
|
||||
long long position;
|
||||
|
||||
// The size of the element as read from the header. Note: Actually an ulli but
|
||||
// due to the variable integer size limited, thus long long is ok.
|
||||
long long size;
|
||||
|
||||
// The position of the element's data.
|
||||
long long data;
|
||||
|
||||
// The element's children.
|
||||
List<Element *> children;
|
||||
|
||||
// True: Treated this element as container and read children.
|
||||
bool populated;
|
||||
|
||||
// The parent element. If NULL (0) this is the document root.
|
||||
Element *parent;
|
||||
|
||||
// The file used to read and write.
|
||||
File *document;
|
||||
|
||||
// Destructor: Clean up all children.
|
||||
~ElementPrivate()
|
||||
{
|
||||
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
|
||||
delete *i;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads a variable length integer from the file at the given position
|
||||
// and saves its value to result. If cutOne is true the first one of the
|
||||
// binary representation of the result is removed (required for size). If
|
||||
// cutOne is false the one will remain in the result (required for id).
|
||||
// This method returns the position directly after the read integer.
|
||||
long long readVInt(long long position, ulli *result, bool cutOne = true)
|
||||
{
|
||||
document->seek(position);
|
||||
|
||||
// Determine the length of the integer
|
||||
char firstByte = document->readBlock(1)[0];
|
||||
size_t byteSize = 1;
|
||||
for(size_t i = 0; i < 8 && ((firstByte << i) & (1 << 7)) == 0; ++i)
|
||||
++byteSize;
|
||||
|
||||
// Load the integer
|
||||
document->seek(position);
|
||||
ByteVector vint = document->readBlock(byteSize);
|
||||
|
||||
// Cut the one if requested
|
||||
if(cutOne)
|
||||
vint[0] = (vint[0] & (~(1 << (8 - byteSize))));
|
||||
|
||||
// Store the result and return the current position
|
||||
if(result)
|
||||
*result = static_cast<ulli>(vint.toInt64BE(0));
|
||||
return position + byteSize;
|
||||
}
|
||||
|
||||
// Returns a BytVector containing the given number in the variable integer
|
||||
// format. Truncates numbers > 2^56 (^ means potency in this case).
|
||||
// If addOne is true, the ByteVector will remain the One to determine the
|
||||
// integer's length.
|
||||
// If shortest is true, the ByteVector will be as short as possible (required
|
||||
// for the id)
|
||||
ByteVector createVInt(ulli number, bool addOne = true, bool shortest = true)
|
||||
{
|
||||
ByteVector vint = ByteVector::fromUInt64BE(number);
|
||||
|
||||
// Do we actually need to calculate the length of the variable length
|
||||
// integer? If not, then prepend the 0b0000 0001 if necessary and return the
|
||||
// vint.
|
||||
if(!shortest) {
|
||||
if(addOne)
|
||||
vint[0] = 1;
|
||||
return vint;
|
||||
}
|
||||
|
||||
// Calculate the minimal length of the variable length integer
|
||||
size_t byteSize = vint.size();
|
||||
for(size_t i = 0; byteSize > 0 && vint[i] == 0; ++i)
|
||||
--byteSize;
|
||||
|
||||
if(!addOne)
|
||||
return ByteVector(vint.data() + vint.size() - byteSize, byteSize);
|
||||
|
||||
ulli firstByte = (1ULL << (vint.size() - byteSize));
|
||||
// The most significant byte loses #bytSize bits for storing information.
|
||||
// Therefore, we might need to increase byteSize.
|
||||
if(number >= (firstByte << (8 * (byteSize - 1))) && byteSize < vint.size())
|
||||
++byteSize;
|
||||
// Add the one at the correct position
|
||||
size_t firstBytePosition = vint.size() - byteSize;
|
||||
vint[firstBytePosition] |= (1 << firstBytePosition);
|
||||
return ByteVector(vint.data() + firstBytePosition, byteSize);
|
||||
}
|
||||
|
||||
// Returns a void element within this element which is at least "least" in
|
||||
// size. Uses best fit method. Returns a null pointer if no suitable element
|
||||
// was found.
|
||||
Element *searchVoid(long long least = 0L)
|
||||
{
|
||||
Element *currentBest = 0;
|
||||
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
|
||||
if((*i)->d->id == Void &&
|
||||
// We need room for the header if we don't remove the element.
|
||||
((((*i)->d->size + (*i)->d->data - (*i)->d->position) == least || ((*i)->d->size >= least)) &&
|
||||
// best fit
|
||||
(!currentBest || (*i)->d->size < currentBest->d->size))
|
||||
) {
|
||||
currentBest = *i;
|
||||
}
|
||||
}
|
||||
return currentBest;
|
||||
}
|
||||
|
||||
// Replaces this element by a Void element. Returns true on success and false
|
||||
// on error.
|
||||
bool makeVoid()
|
||||
{
|
||||
ulli realSize = size + data - position;
|
||||
ByteVector header(createVInt(Void, false));
|
||||
ulli leftSize = realSize - (header.size() + sizeof(ulli));
|
||||
// Does not make sense to create a Void element
|
||||
if (leftSize > realSize)
|
||||
return false;
|
||||
header.append(createVInt(leftSize, true, false));
|
||||
// Write to file
|
||||
document->seek(position);
|
||||
document->writeBlock(header);
|
||||
// Update data
|
||||
data = position + header.size();
|
||||
size = leftSize;
|
||||
return true;
|
||||
|
||||
// XXX: We actually should merge Voids, if possible.
|
||||
}
|
||||
|
||||
// Reading constructor: Reads all unknown information from the file.
|
||||
ElementPrivate(File *p_document, Element *p_parent = 0, long long p_position = 0) :
|
||||
id(0),
|
||||
position(p_position),
|
||||
data(0),
|
||||
populated(false),
|
||||
parent(p_parent),
|
||||
document(p_document)
|
||||
{
|
||||
if(parent) {
|
||||
ulli ssize;
|
||||
data = readVInt(readVInt(position, &id, false), &ssize);
|
||||
size = static_cast<long long>(ssize);
|
||||
}
|
||||
else {
|
||||
document->seek(0, File::End);
|
||||
size = document->tell();
|
||||
}
|
||||
}
|
||||
|
||||
// Writing constructor: Takes given information, calculates missing information
|
||||
// and writes everything to the file.
|
||||
// Tries to use void elements if available in the parent.
|
||||
ElementPrivate(ulli p_id, File *p_document, Element *p_parent,
|
||||
long long p_position, long long p_size) :
|
||||
id(p_id),
|
||||
position(p_position),
|
||||
size(p_size),
|
||||
populated(true), // It is a new element so we know, there are no children.
|
||||
parent(p_parent),
|
||||
document(p_document)
|
||||
{
|
||||
// header
|
||||
ByteVector content(createVInt(id, false).append(createVInt(size, true, false)));
|
||||
data = position + content.size();
|
||||
// space for children
|
||||
content.resize(static_cast<size_t>(data - position + size));
|
||||
|
||||
Element *freeSpace;
|
||||
if (!(freeSpace = searchVoid(content.size()))) {
|
||||
// We have to make room
|
||||
document->insert(content, position);
|
||||
// Update parents
|
||||
for(Element *current = parent; current->d->parent; current = current->d->parent) {
|
||||
current->d->size += content.size();
|
||||
// Create new header and write it.
|
||||
ByteVector parentHeader(createVInt(current->d->id, false).append(createVInt(current->d->size, true, false)));
|
||||
size_t oldHeaderSize = static_cast<size_t>(current->d->data - current->d->position);
|
||||
if(oldHeaderSize < parentHeader.size()) {
|
||||
ByteVector secondHeader(createVInt(current->d->id, false).append(createVInt(current->d->size)));
|
||||
if(oldHeaderSize == secondHeader.size()) {
|
||||
// Write the header where the old one was.
|
||||
document->seek(current->d->position);
|
||||
document->writeBlock(secondHeader);
|
||||
continue; // Very important here!
|
||||
}
|
||||
}
|
||||
// Insert the new header
|
||||
document->insert(parentHeader, current->d->position, oldHeaderSize);
|
||||
current->d->data = current->d->position + parentHeader.size();
|
||||
}
|
||||
}
|
||||
else {
|
||||
document->seek(freeSpace->d->position);
|
||||
if((freeSpace->d->size + freeSpace->d->data - freeSpace->d->position)
|
||||
== static_cast<long long>(content.size())) {
|
||||
// Write to file
|
||||
document->writeBlock(content);
|
||||
// Update parent
|
||||
for(List<Element *>::Iterator i = parent->d->children.begin();
|
||||
i != parent->d->children.end(); ++i) {
|
||||
if(freeSpace == *i)
|
||||
parent->d->children.erase(i);
|
||||
}
|
||||
delete freeSpace;
|
||||
}
|
||||
else {
|
||||
ulli newSize = freeSpace->d->size - content.size();
|
||||
ByteVector newVoid(createVInt(Void, false).append(createVInt(newSize, true, false)));
|
||||
|
||||
// Check if the original size of the size field was really 8 byte
|
||||
if (static_cast<long long>(newVoid.size()) != (freeSpace->d->data - freeSpace->d->position))
|
||||
newVoid = createVInt(Void, false).append(createVInt(newSize));
|
||||
// Update freeSpace
|
||||
freeSpace->d->size = newSize;
|
||||
freeSpace->d->data = freeSpace->d->position + newVoid.size();
|
||||
// Write to file
|
||||
document->writeBlock(
|
||||
newVoid.resize(static_cast<size_t>(newVoid.size() + newSize)).append(content));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Element::~Element()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Element::Element(EBML::File *document)
|
||||
: d(new EBML::Element::ElementPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::getChild(EBML::ulli id)
|
||||
{
|
||||
populate();
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
|
||||
++i) {
|
||||
if ((*i)->d->id == id)
|
||||
return *i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<EBML::Element *> EBML::Element::getChildren(EBML::ulli id)
|
||||
{
|
||||
populate();
|
||||
List<Element *> result;
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
|
||||
++i) {
|
||||
if ((*i)->d->id == id)
|
||||
result.append(*i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
List<EBML::Element *> EBML::Element::getChildren()
|
||||
{
|
||||
populate();
|
||||
return d->children;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::getParent()
|
||||
{
|
||||
return d->parent;
|
||||
}
|
||||
|
||||
ByteVector EBML::Element::getAsBinary()
|
||||
{
|
||||
d->document->seek(d->data);
|
||||
return d->document->readBlock(static_cast<size_t>(d->size));
|
||||
}
|
||||
|
||||
String EBML::Element::getAsString()
|
||||
{
|
||||
return String(getAsBinary(), String::UTF8);
|
||||
}
|
||||
|
||||
long long EBML::Element::getAsInt()
|
||||
{
|
||||
// The debug note about returning 0 because of empty data is irrelevant. The
|
||||
// behavior is as expected.
|
||||
return getAsBinary().toInt64BE(0);
|
||||
}
|
||||
|
||||
EBML::ulli EBML::Element::getAsUnsigned()
|
||||
{
|
||||
// The debug note about returning 0 because of empty data is irrelevant. The
|
||||
// behavior is as expected.
|
||||
return static_cast<ulli>(getAsBinary().toInt64BE(0));
|
||||
}
|
||||
|
||||
long double EBML::Element::getAsFloat()
|
||||
{
|
||||
const ByteVector bin = getAsBinary();
|
||||
switch (bin.size())
|
||||
{
|
||||
case 4:
|
||||
return bin.toFloat32BE(0);
|
||||
case 8:
|
||||
return bin.toFloat64BE(0);
|
||||
case 10:
|
||||
return bin.toFloat80BE(0);
|
||||
default:
|
||||
debug("EBML::Element::getAsFloat() - Invalid data size. Returning 0.");
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id)
|
||||
{
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(id, d->document, this, d->data + d->size, 0)
|
||||
);
|
||||
d->children.append(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, const ByteVector &binary)
|
||||
{
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(id, d->document, this, d->data + d->size, binary.size())
|
||||
);
|
||||
d->document->seek(elem->d->data);
|
||||
d->document->writeBlock(binary);
|
||||
d->children.append(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, const String &string)
|
||||
{
|
||||
return addElement(id, string.data(String::UTF8));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, signed long long number)
|
||||
{
|
||||
return addElement(id, ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, EBML::ulli number)
|
||||
{
|
||||
return addElement(id, ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, long double number)
|
||||
{
|
||||
// Probably, we will never need this method.
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChildren(EBML::ulli id, bool useVoid)
|
||||
{
|
||||
bool result = false;
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
if((*i)->d->id == id) {
|
||||
removeChild(*i, useVoid);
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChildren(bool useVoid)
|
||||
{
|
||||
// Maybe a better implementation, because we probably create a lot of voids
|
||||
// in a row where a huge Void would be more appropriate.
|
||||
if (d->children.isEmpty())
|
||||
return false;
|
||||
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
removeChild(*i, useVoid);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChild(Element *element, bool useVoid)
|
||||
{
|
||||
if (!d->children.contains(element))
|
||||
return false;
|
||||
|
||||
if(!useVoid || !element->d->makeVoid()) {
|
||||
d->document->removeBlock(element->d->position, static_cast<size_t>(element->d->size));
|
||||
// Update parents
|
||||
for(Element* current = this; current; current = current->d->parent)
|
||||
current->d->size -= element->d->size;
|
||||
// Update this element
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
if(element == *i)
|
||||
d->children.erase(i);
|
||||
delete element;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EBML::Element::setAsBinary(const ByteVector &binary)
|
||||
{
|
||||
// Maybe: Search for void element after this one
|
||||
d->document->insert(binary, d->data, static_cast<size_t>(d->size));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsString(const String &string)
|
||||
{
|
||||
setAsBinary(string.data(String::UTF8));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsInt(signed long long number)
|
||||
{
|
||||
setAsBinary(ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsUnsigned(EBML::ulli number)
|
||||
{
|
||||
setAsBinary(ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsFloat(long double)
|
||||
{
|
||||
// Probably, we will never need this method.
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EBML::Element::populate()
|
||||
{
|
||||
if(!d->populated) {
|
||||
d->populated = true;
|
||||
long long end = d->data + d->size;
|
||||
|
||||
for(long long i = d->data; i < end;) {
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(d->document, this, i)
|
||||
);
|
||||
d->children.append(elem);
|
||||
i = elem->d->data + elem->d->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Element::Element(EBML::Element::ElementPrivate *pe) : d(pe)
|
||||
{}
|
||||
276
taglib/ebml/ebmlelement.h
Normal file
276
taglib/ebml/ebmlelement.h
Normal file
@@ -0,0 +1,276 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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_EBMLELEMENT_H
|
||||
#define TAGLIB_EBMLELEMENT_H
|
||||
|
||||
#include "tlist.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tstring.h"
|
||||
|
||||
#include "ebmlfile.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
/*!
|
||||
* Represents an element of the EBML. The only instance of this child, that
|
||||
* is directly used is the root element. Every other element is accessed
|
||||
* via pointers to the elements within the root element.
|
||||
*
|
||||
* Just create one root instance per file to prevent race conditions.
|
||||
*
|
||||
* Changes of the document tree will be directly written back to the file.
|
||||
* Invalid values (exceeding the maximal value defined in the RFC) will be
|
||||
* truncated.
|
||||
*
|
||||
* This class should not be used by library users since the proper file
|
||||
* class should handle the internals.
|
||||
*
|
||||
* NOTE: Currently does not adjust CRC32 values.
|
||||
*/
|
||||
class TAGLIB_EXPORT Element
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the element.
|
||||
~Element();
|
||||
|
||||
/*!
|
||||
* Creates an root element using document.
|
||||
*/
|
||||
explicit Element(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the first found child element with the given id. Returns a null
|
||||
* pointer if the child does not exist.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *getChild(const ulli id);
|
||||
|
||||
/*!
|
||||
* Returns a list of all child elements with the given id. Returns an
|
||||
* empty list if no such element exists.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
List<Element *> getChildren(const ulli id);
|
||||
|
||||
/*!
|
||||
* Returns a list of every child elements available. Returns an empty list
|
||||
* if there are no children.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
List<Element *> getChildren();
|
||||
|
||||
/*!
|
||||
* Returns the parent element or null if no such element exists.
|
||||
*/
|
||||
Element *getParent();
|
||||
|
||||
/*!
|
||||
* Returns the raw content of the element.
|
||||
*/
|
||||
ByteVector getAsBinary();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as a string.
|
||||
*/
|
||||
String getAsString();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as an signed integer.
|
||||
*
|
||||
* Do not call this method if *this element is not an INT element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
long long getAsInt();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as an unsigned integer.
|
||||
*
|
||||
* Do not call this method if *this element is not an UINT element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
ulli getAsUnsigned();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as a floating point
|
||||
* type.
|
||||
*
|
||||
* Do not call this method if *this element is not an FLOAT element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* NOTE: There are 10 byte floats defined, therefore we might need a long
|
||||
* double to store the value.
|
||||
*/
|
||||
long double getAsFloat();
|
||||
|
||||
/*!
|
||||
* Adds an empty element with given id to this element. Returns a pointer
|
||||
* to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given binary, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, const ByteVector &binary);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given string, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, const String &string);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given integer, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, signed long long number);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given unsigned integer, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, ulli number);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given floating point value, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* This method is not implemented!
|
||||
*/
|
||||
Element *addElement(ulli id, long double number);
|
||||
|
||||
/*!
|
||||
* Removes all children with the given id. Returns false if there was no
|
||||
* such element.
|
||||
* If useVoid is true, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* Every pointer to a removed element is invalidated.
|
||||
*/
|
||||
bool removeChildren(ulli id, bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Removes all children. Returns false if this element had no children.
|
||||
* If useVoid ist rue, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* Every pointer to a removed element is invalidated.
|
||||
*/
|
||||
bool removeChildren(bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Removes the given element.
|
||||
* If useVoid is true, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* The pointer to the given element is invalidated.
|
||||
*/
|
||||
bool removeChild(Element *element, bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Writes the given binary to this element.
|
||||
*/
|
||||
void setAsBinary(const ByteVector &binary);
|
||||
|
||||
/*!
|
||||
* Writes the given string to this element.
|
||||
*/
|
||||
void setAsString(const String &string);
|
||||
|
||||
/*!
|
||||
* Writes the given integer to this element.
|
||||
*/
|
||||
void setAsInt(signed long long number);
|
||||
|
||||
/*!
|
||||
* Writes the given unsigned integer to this element.
|
||||
*/
|
||||
void setAsUnsigned(ulli number);
|
||||
|
||||
/*!
|
||||
* Writes the given floating point variable to this element.
|
||||
*
|
||||
* This method is not implemented!
|
||||
*/
|
||||
void setAsFloat(long double number);
|
||||
|
||||
private:
|
||||
//! Non-copyable
|
||||
Element(const Element &);
|
||||
//! Non-copyable
|
||||
Element &operator=(const File &);
|
||||
|
||||
//! Lazy parsing. This method will be triggered when trying to access
|
||||
//! children.
|
||||
void populate();
|
||||
|
||||
class ElementPrivate;
|
||||
ElementPrivate *d;
|
||||
|
||||
//! Creates a new Element from an ElementPrivate. (The constructor takes
|
||||
//! ownership of the pointer and will delete it when the element is
|
||||
//! destroyed.
|
||||
Element(ElementPrivate *pe);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
102
taglib/ebml/ebmlfile.cpp
Normal file
102
taglib/ebml/ebmlfile.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "ebmlelement.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
explicit FilePrivate(File *document) : root(document)
|
||||
{
|
||||
}
|
||||
|
||||
// Performs a few basic checks and creates the FilePrivate if they were
|
||||
// successful.
|
||||
static FilePrivate *checkAndCreate(File* document)
|
||||
{
|
||||
document->seek(0);
|
||||
ByteVector magical = document->readBlock(4);
|
||||
if(static_cast<ulli>(magical.toUInt32BE(0)) != Header::EBML)
|
||||
return 0;
|
||||
FilePrivate *d = new FilePrivate(document);
|
||||
Element *head = d->root.getChild(Header::EBML);
|
||||
Element *p;
|
||||
if(!head ||
|
||||
!((p = head->getChild(Header::EBMLVersion)) && p->getAsUnsigned() == 1L) ||
|
||||
!((p = head->getChild(Header::EBMLReadVersion)) && p->getAsUnsigned() == 1L) ||
|
||||
// Actually 4 is the current maximum of the EBML spec, but we support up to 8
|
||||
!((p = head->getChild(Header::EBMLMaxIDWidth)) && p->getAsUnsigned() <= 8) ||
|
||||
!((p = head->getChild(Header::EBMLMaxSizeWidth)) && p->getAsUnsigned() <= 8)
|
||||
) {
|
||||
delete d;
|
||||
return 0;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
Element root;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::File::getDocumentRoot()
|
||||
{
|
||||
if(!d && isValid())
|
||||
d = new FilePrivate(this);
|
||||
return &d->root;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::File::File(FileName file) :
|
||||
TagLib::File(file)
|
||||
{
|
||||
if(isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
}
|
||||
}
|
||||
|
||||
EBML::File::File(IOStream *stream) :
|
||||
TagLib::File(stream)
|
||||
{
|
||||
if(isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
}
|
||||
}
|
||||
86
taglib/ebml/ebmlfile.h
Normal file
86
taglib/ebml/ebmlfile.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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_EBMLFILE_H
|
||||
#define TAGLIB_EBMLFILE_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
|
||||
#include "ebmlconstants.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! A namespace for the classes used by EBML-based metadata files
|
||||
namespace EBML {
|
||||
|
||||
class Element;
|
||||
|
||||
/*!
|
||||
* Represents an EBML file. It offers access to the root element which can
|
||||
* be used to obtain the necessary information and to change the file
|
||||
* according to changes.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the file.
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the document root element of the EBML file.
|
||||
*/
|
||||
Element *getDocumentRoot();
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Constructs an instance of an EBML file from \a file.
|
||||
*
|
||||
* This constructor is protected since an object should be created
|
||||
* through a specific subclass.
|
||||
*/
|
||||
explicit File(FileName file);
|
||||
|
||||
/*!
|
||||
* Constructs an instance of an EBML file from an IOStream.
|
||||
*
|
||||
* This constructor is protected since an object should be created
|
||||
* through a specific subclass.
|
||||
*/
|
||||
explicit File(IOStream *stream);
|
||||
|
||||
private:
|
||||
//! Non-copyable
|
||||
File(const File&);
|
||||
File &operator=(const File &);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
135
taglib/ebml/matroska/ebmlmatroskaaudio.cpp
Normal file
135
taglib/ebml/matroska/ebmlmatroskaaudio.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmatroskaconstants.h"
|
||||
#include "ebmlmatroskaaudio.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Matroska::AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
AudioPropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
channels(1),
|
||||
samplerate(8000) {}
|
||||
|
||||
// The length of the file
|
||||
int length;
|
||||
|
||||
// The bitrate
|
||||
int bitrate;
|
||||
|
||||
// The amount of channels
|
||||
int channels;
|
||||
|
||||
// The sample rate
|
||||
int samplerate;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::AudioProperties::AudioProperties(File *document) :
|
||||
d(new AudioPropertiesPrivate())
|
||||
{
|
||||
read(document);
|
||||
}
|
||||
|
||||
EBML::Matroska::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->samplerate;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EBML::Matroska::AudioProperties::read(File *document)
|
||||
{
|
||||
Element *elem = document->getDocumentRoot()->getChild(Constants::Segment);
|
||||
Element *info = elem->getChild(Constants::SegmentInfo);
|
||||
Element *value;
|
||||
|
||||
if(info && (value = info->getChild(Constants::Duration))) {
|
||||
|
||||
const double length = value->getAsFloat() / 1000000.0;
|
||||
|
||||
if((value = info->getChild(Constants::TimecodeScale)))
|
||||
d->length = static_cast<int>(length * value->getAsUnsigned() + 0.5);
|
||||
else
|
||||
d->length = static_cast<int>(length * 1000000 + 0.5);
|
||||
}
|
||||
|
||||
info = elem->getChild(Constants::Tracks);
|
||||
if(!info || !(info = info->getChild(Constants::TrackEntry)) ||
|
||||
!(info = info->getChild(Constants::Audio))) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Dirty bitrate:
|
||||
if(d->length > 0)
|
||||
d->bitrate = static_cast<int>(document->length() * 8.0 / d->length + 0.5);
|
||||
|
||||
if((value = info->getChild(Constants::Channels)))
|
||||
d->channels = static_cast<int>(value->getAsUnsigned());
|
||||
|
||||
if((value = info->getChild(Constants::SamplingFrequency)))
|
||||
d->samplerate = static_cast<int>(value->getAsFloat());
|
||||
}
|
||||
92
taglib/ebml/matroska/ebmlmatroskaaudio.h
Normal file
92
taglib/ebml/matroska/ebmlmatroskaaudio.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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_EBMLMATROSKAAUDIO_H
|
||||
#define TAGLIB_EBMLMATROSKAAUDIO_H
|
||||
|
||||
#include "ebmlmatroskafile.h"
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
namespace Matroska {
|
||||
|
||||
/*!
|
||||
* This class represents the audio properties of a matroska file.
|
||||
* Currently all information are read from the container format and
|
||||
* could be inexact.
|
||||
*/
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
//! Destructor
|
||||
virtual ~AudioProperties();
|
||||
|
||||
/*!
|
||||
* Constructs an instance from a file.
|
||||
*/
|
||||
explicit AudioProperties(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the length of the file.
|
||||
*/
|
||||
virtual int length() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the bit rate of the file. Since the container format does not
|
||||
* offer a proper value, it ist currently calculated by dividing the
|
||||
* file size by the length.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the amount of channels of the file.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate of the file.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
|
||||
private:
|
||||
void read(File *document);
|
||||
|
||||
class AudioPropertiesPrivate;
|
||||
AudioPropertiesPrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
140
taglib/ebml/matroska/ebmlmatroskaconstants.h
Normal file
140
taglib/ebml/matroska/ebmlmatroskaconstants.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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_EBMLMATROSKACONSTANTS_H
|
||||
#define TAGLIB_EBMLMATROSKACONSTANTS_H
|
||||
|
||||
#include "ebmlconstants.h"
|
||||
#include "tstring.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
namespace Matroska {
|
||||
|
||||
namespace Constants {
|
||||
|
||||
//! ID of an Matroska segment.
|
||||
const ulli Segment = 0x18538067L;
|
||||
|
||||
//! ID of the tags element.
|
||||
const ulli Tags = 0x1254c367L;
|
||||
|
||||
//! ID of the tag element.
|
||||
const ulli Tag = 0x7373L;
|
||||
|
||||
//! ID of the targets element.
|
||||
const ulli Targets = 0x63c0L;
|
||||
|
||||
//! ID of the target type value element.
|
||||
const ulli TargetTypeValue = 0x68caL;
|
||||
|
||||
//! ID of the target type element.
|
||||
const ulli TargetType = 0x63caL;
|
||||
|
||||
//! ID of a simple tag element.
|
||||
const ulli SimpleTag = 0x67c8L;
|
||||
|
||||
//! ID of the tag name.
|
||||
const ulli TagName = 0x45a3L;
|
||||
|
||||
//! ID of the tag content.
|
||||
const ulli TagString = 0x4487L;
|
||||
|
||||
//! The DocType of a matroska file.
|
||||
const String DocTypeMatroska = "matroska";
|
||||
|
||||
//! The DocType of a WebM file.
|
||||
const String DocTypeWebM = "webm";
|
||||
|
||||
|
||||
|
||||
//! The TITLE entry
|
||||
const String TITLE = "TITLE";
|
||||
|
||||
//! The ARTIST entry
|
||||
const String ARTIST = "ARTIST";
|
||||
|
||||
//! The COMMENT entry
|
||||
const String COMMENT = "COMMENT";
|
||||
|
||||
//! The GENRE entry
|
||||
const String GENRE = "GENRE";
|
||||
|
||||
//! The DATE_RELEASE entry
|
||||
const String DATE_RELEASE = "DATE_RELEASE";
|
||||
|
||||
//! The PART_NUMBER entry
|
||||
const String PART_NUMBER = "PART_NUMBER";
|
||||
|
||||
//! The TargetTypeValue of the most common grouping level (e.g. album)
|
||||
const ulli MostCommonGroupingValue = 50;
|
||||
|
||||
//! The TargetTypeValue of the most common parts of a group (e.g. track)
|
||||
const ulli MostCommonPartValue = 30;
|
||||
|
||||
//! Name of the TargetType of an album.
|
||||
const String ALBUM = "ALBUM";
|
||||
|
||||
//! Name of the TargetType of a track.
|
||||
const String TRACK = "TRACK";
|
||||
|
||||
|
||||
|
||||
// For AudioProperties
|
||||
|
||||
//! ID of the Info block within the Segment.
|
||||
const ulli SegmentInfo = 0x1549a966L;
|
||||
|
||||
//! ID of the duration element.
|
||||
const ulli Duration = 0x4489L;
|
||||
|
||||
//! ID of TimecodeScale element.
|
||||
const ulli TimecodeScale = 0x2ad7b1L;
|
||||
|
||||
//! ID of the Tracks container
|
||||
const ulli Tracks = 0x1654ae6bL;
|
||||
|
||||
//! ID of a TrackEntry element.
|
||||
const ulli TrackEntry = 0xaeL;
|
||||
|
||||
//! ID of the Audio container.
|
||||
const ulli Audio = 0xe1L;
|
||||
|
||||
//! ID of the SamplingFrequency element.
|
||||
const ulli SamplingFrequency = 0xb5L;
|
||||
|
||||
//! ID of the Channels element.
|
||||
const ulli Channels = 0x9fL;
|
||||
|
||||
//! ID of the BitDepth element.
|
||||
const ulli BitDepth = 0x6264L;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
563
taglib/ebml/matroska/ebmlmatroskafile.cpp
Normal file
563
taglib/ebml/matroska/ebmlmatroskafile.cpp
Normal file
@@ -0,0 +1,563 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmatroskaconstants.h"
|
||||
#include "ebmlmatroskaaudio.h"
|
||||
|
||||
#include "tpicturemap.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Matroska::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
// Returns true if simpleTag has a TagName and TagString child and writes
|
||||
// their contents into name and value.
|
||||
bool extractContent(Element *simpleTag, String &name, String &value)
|
||||
{
|
||||
Element *n = simpleTag->getChild(Constants::TagName);
|
||||
Element *v = simpleTag->getChild(Constants::TagString);
|
||||
if(!n || !v)
|
||||
return false;
|
||||
|
||||
name = n->getAsString();
|
||||
value = v->getAsString();
|
||||
return true;
|
||||
}
|
||||
|
||||
explicit FilePrivate(File *p_document) : tag(0), document(p_document)
|
||||
{
|
||||
// Just get the first segment, because "Typically a Matroska file is
|
||||
// composed of 1 segment."
|
||||
Element* elem = document->getDocumentRoot()->getChild(Constants::Segment);
|
||||
|
||||
// We take the first tags element (there shouldn't be more), if there is
|
||||
// non such element, consider the file as not compatible.
|
||||
if(!elem || !(elem = elem->getChild(Constants::Tags))) {
|
||||
document->setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load all Tag entries
|
||||
List<Element *> entries = elem->getChildren(Constants::Tag);
|
||||
for(List<Element *>::Iterator i = entries.begin(); i != entries.end(); ++i) {
|
||||
Element *target = (*i)->getChild(Constants::Targets);
|
||||
ulli ttvalue = 50; // 50 is default (see spec.)
|
||||
if(target && (target = target->getChild(Constants::TargetTypeValue)))
|
||||
ttvalue = target->getAsUnsigned();
|
||||
|
||||
// Load all SimpleTags
|
||||
PropertyMap tagEntries;
|
||||
List<Element *> simpleTags = (*i)->getChildren(Constants::SimpleTag);
|
||||
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end();
|
||||
++j) {
|
||||
String name, value;
|
||||
if(!extractContent(*j, name, value))
|
||||
continue;
|
||||
tagEntries.insert(name, StringList(value));
|
||||
}
|
||||
|
||||
tags.append(std::pair<PropertyMap, std::pair<Element *, ulli> >(tagEntries, std::pair<Element *, ulli>(*i, ttvalue)));
|
||||
}
|
||||
}
|
||||
|
||||
// Creates Tag and AudioProperties. Late creation because both require a fully
|
||||
// functional FilePrivate (well AudioProperties doesn't...)
|
||||
void lateCreate()
|
||||
{
|
||||
tag = new Tag(document);
|
||||
audio = new AudioProperties(document);
|
||||
}
|
||||
|
||||
// Checks the EBML header and creates the FilePrivate.
|
||||
static FilePrivate *checkAndCreate(File *document)
|
||||
{
|
||||
Element *elem = document->getDocumentRoot()->getChild(Header::EBML);
|
||||
Element *child = elem->getChild(Header::DocType);
|
||||
if(child) {
|
||||
String dt = child->getAsString();
|
||||
if (dt == Constants::DocTypeMatroska || dt == Constants::DocTypeWebM) {
|
||||
FilePrivate *fp = new FilePrivate(document);
|
||||
return fp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The tags with their Element and TargetTypeValue
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > > tags;
|
||||
|
||||
// The tag
|
||||
Tag *tag;
|
||||
|
||||
// The audio properties
|
||||
AudioProperties *audio;
|
||||
|
||||
// The corresponding file.
|
||||
File *document;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::File::~File()
|
||||
{
|
||||
if (d) {
|
||||
delete d->tag;
|
||||
delete d->audio;
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Matroska::File::File(FileName file, bool, AudioProperties::ReadStyle) : EBML::File(file), d(0)
|
||||
{
|
||||
if(isValid() && isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
else
|
||||
d->lateCreate();
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Matroska::File::File(IOStream *stream, bool, AudioProperties::ReadStyle) : EBML::File(stream), d(0)
|
||||
{
|
||||
if(isValid() && isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
else
|
||||
d->lateCreate();
|
||||
}
|
||||
}
|
||||
|
||||
Tag *EBML::Matroska::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
PropertyMap EBML::Matroska::File::properties() const
|
||||
{
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
if(best == d->tags.end() || best->second.second > i->second.second)
|
||||
best = i;
|
||||
}
|
||||
return best->first;
|
||||
}
|
||||
|
||||
PropertyMap EBML::Matroska::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
if(best == d->tags.end() || best->second.second > i->second.second)
|
||||
best = i;
|
||||
}
|
||||
|
||||
std::pair<PropertyMap, std::pair<Element *, ulli> > replace(properties, best->second);
|
||||
d->tags.erase(best);
|
||||
d->tags.prepend(replace);
|
||||
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
AudioProperties *EBML::Matroska::File::audioProperties() const
|
||||
{
|
||||
return d->audio;
|
||||
}
|
||||
|
||||
bool EBML::Matroska::File::save()
|
||||
{
|
||||
if(readOnly())
|
||||
return false;
|
||||
|
||||
// C++11 features would be nice: for(auto &i : d->tags) { /* ... */ }
|
||||
// Well, here we just iterate each extracted element.
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
|
||||
for(PropertyMap::Iterator j = i->first.begin(); j != i->first.end(); ++j) {
|
||||
|
||||
// No element? Create it!
|
||||
if(!i->second.first) {
|
||||
// Should be save, since we already checked, when creating the object.
|
||||
Element *container = d->document->getDocumentRoot()
|
||||
->getChild(Constants::Segment)->getChild(Constants::Tags);
|
||||
|
||||
// Create Targets container
|
||||
i->second.first = container->addElement(Constants::Tag);
|
||||
Element *target = i->second.first->addElement(Constants::Targets);
|
||||
|
||||
if(i->second.second == Constants::MostCommonPartValue)
|
||||
target->addElement(Constants::TargetType, Constants::TRACK);
|
||||
else if(i->second.second == Constants::MostCommonGroupingValue)
|
||||
target->addElement(Constants::TargetType, Constants::ALBUM);
|
||||
|
||||
target->addElement(Constants::TargetTypeValue, i->second.second);
|
||||
}
|
||||
|
||||
// Find entries
|
||||
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
|
||||
StringList::Iterator str = j->second.begin();
|
||||
List<Element *>::Iterator k = simpleTags.begin();
|
||||
for(; k != simpleTags.end(); ++k) {
|
||||
|
||||
String name, value;
|
||||
if(!d->extractContent(*k, name, value))
|
||||
continue;
|
||||
|
||||
// Write entry from StringList
|
||||
if(name == j->first) {
|
||||
if(str == j->second.end()) {
|
||||
// We have all StringList elements but still found another element
|
||||
// with the same name? Let's delete it!
|
||||
i->second.first->removeChild(*k);
|
||||
}
|
||||
else {
|
||||
if(value != *str) {
|
||||
// extractContent already checked for availability
|
||||
(*k)->getChild(Constants::TagString)->setAsString(*str);
|
||||
}
|
||||
++str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't write the complete StringList, we have to write the rest.
|
||||
for(; str != j->second.end(); ++str) {
|
||||
Element *stag = i->second.first->addElement(Constants::SimpleTag);
|
||||
stag->addElement(Constants::TagName, j->first);
|
||||
stag->addElement(Constants::TagString, *str);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we have to find elements that are not in the PropertyMap and
|
||||
// remove them.
|
||||
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
|
||||
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end(); ++j) {
|
||||
|
||||
String name, value;
|
||||
if(!d->extractContent(*j, name, value))
|
||||
continue;
|
||||
|
||||
if(i->first.find(name) == i->first.end()){
|
||||
i->second.first->removeChild(*j);}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Tag
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class EBML::Matroska::File::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
// Creates a TagPrivate instance
|
||||
explicit TagPrivate(File *p_document) :
|
||||
document(p_document),
|
||||
title(document->d->tags.end()),
|
||||
artist(document->d->tags.end()),
|
||||
album(document->d->tags.end()),
|
||||
comment(document->d->tags.end()),
|
||||
genre(document->d->tags.end()),
|
||||
year(document->d->tags.end()),
|
||||
track(document->d->tags.end())
|
||||
{
|
||||
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
// Just save it, if the title is more specific, or there is no title yet.
|
||||
if(i->first.find(Constants::TITLE) != i->first.end() &&
|
||||
(title == document->d->tags.end() ||
|
||||
title->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
title = i;
|
||||
}
|
||||
|
||||
// Same goes for artist.
|
||||
if(i->first.find(Constants::ARTIST) != i->first.end() &&
|
||||
(artist == document->d->tags.end() ||
|
||||
artist->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
artist = i;
|
||||
}
|
||||
|
||||
// Here, we also look for a title (the album title), but since we
|
||||
// specified the granularity, we have to search for it exactly.
|
||||
// Therefore it is possible, that title and album are the same (if only
|
||||
// the title of the album is given).
|
||||
if(i->first.find(Constants::TITLE) != i->first.end() &&
|
||||
i->second.second == Constants::MostCommonGroupingValue) {
|
||||
|
||||
album = i;
|
||||
}
|
||||
|
||||
// Again the same as title and artist.
|
||||
if(i->first.find(Constants::COMMENT) != i->first.end() &&
|
||||
(comment == document->d->tags.end() ||
|
||||
comment->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
comment = i;
|
||||
}
|
||||
|
||||
// Same goes for genre.
|
||||
if(i->first.find(Constants::GENRE) != i->first.end() &&
|
||||
(genre == document->d->tags.end() ||
|
||||
genre->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
genre = i;
|
||||
}
|
||||
|
||||
// And year (in our case: DATE_REALEASE)
|
||||
if(i->first.find(Constants::DATE_RELEASE) != i->first.end() &&
|
||||
(year == document->d->tags.end() ||
|
||||
year->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
year = i;
|
||||
}
|
||||
|
||||
// And track (in our case: PART_NUMBER)
|
||||
if(i->first.find(Constants::PART_NUMBER) != i->first.end() &&
|
||||
(track == document->d->tags.end() ||
|
||||
track->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
track = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Searches for the Tag with given TargetTypeValue (returns the first one)
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator
|
||||
find(ulli ttv)
|
||||
{
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
if(i->second.second == ttv)
|
||||
return i;
|
||||
}
|
||||
return document->d->tags.end();
|
||||
}
|
||||
|
||||
// Updates the given information
|
||||
void update(
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator t,
|
||||
const String &tagname,
|
||||
const String &s
|
||||
)
|
||||
{
|
||||
t->first.find(tagname)->second.front() = s;
|
||||
}
|
||||
|
||||
// Inserts a tag with given information
|
||||
void insert(const String &tagname, const ulli ttv, const String &s)
|
||||
{
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
if(i->second.second == ttv) {
|
||||
i->first.insert(tagname, StringList(s));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found? Create new!
|
||||
PropertyMap pm;
|
||||
pm.insert(tagname, StringList(s));
|
||||
document->d->tags.append(
|
||||
std::pair<PropertyMap, std::pair<Element *, ulli> >(pm,
|
||||
std::pair<Element *, ulli>(static_cast<Element *>(0), ttv)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// The PropertyMap from the Matroska::File
|
||||
File *document;
|
||||
|
||||
// Iterators to the tags.
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator title;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator artist;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator album;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator comment;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator genre;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator year;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator track;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::File::Tag::~Tag()
|
||||
{
|
||||
delete e;
|
||||
}
|
||||
|
||||
EBML::Matroska::File::Tag::Tag(EBML::Matroska::File *document) :
|
||||
e(new EBML::Matroska::File::Tag::TagPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::title() const
|
||||
{
|
||||
if(e->title != e->document->d->tags.end())
|
||||
return e->title->first.find(Constants::TITLE)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::artist() const
|
||||
{
|
||||
if(e->artist != e->document->d->tags.end())
|
||||
return e->artist->first.find(Constants::ARTIST)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::album() const
|
||||
{
|
||||
if(e->album != e->document->d->tags.end())
|
||||
return e->album->first.find(Constants::TITLE)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
PictureMap EBML::Matroska::File::Tag::pictures() const
|
||||
{
|
||||
return PictureMap();
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::comment() const
|
||||
{
|
||||
if(e->comment != e->document->d->tags.end())
|
||||
return e->comment->first.find(Constants::COMMENT)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::genre() const
|
||||
{
|
||||
if(e->genre != e->document->d->tags.end())
|
||||
return e->genre->first.find(Constants::GENRE)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
unsigned int EBML::Matroska::File::Tag::year() const
|
||||
{
|
||||
if(e->year != e->document->d->tags.end())
|
||||
return e->year->first.find(Constants::DATE_RELEASE)->second.front().toInt();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int EBML::Matroska::File::Tag::track() const
|
||||
{
|
||||
if(e->track != e->document->d->tags.end())
|
||||
return e->track->first.find(Constants::PART_NUMBER)->second.front().toInt();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setTitle(const String &s)
|
||||
{
|
||||
if(e->title != e->document->d->tags.end())
|
||||
e->update(e->title, Constants::TITLE, s);
|
||||
else
|
||||
e->insert(Constants::TITLE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setArtist(const String &s)
|
||||
{
|
||||
if(e->artist != e->document->d->tags.end())
|
||||
e->update(e->artist, Constants::ARTIST, s);
|
||||
else
|
||||
e->insert(Constants::ARTIST, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setAlbum(const String &s)
|
||||
{
|
||||
if(e->album != e->document->d->tags.end())
|
||||
e->update(e->album, Constants::TITLE, s);
|
||||
else
|
||||
e->insert(Constants::TITLE, Constants::MostCommonGroupingValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setPictures(const PictureMap& p )
|
||||
{
|
||||
(void)p; // avoid warning for unused variable
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setComment(const String &s)
|
||||
{
|
||||
if(e->comment != e->document->d->tags.end())
|
||||
e->update(e->comment, Constants::COMMENT, s);
|
||||
else
|
||||
e->insert(Constants::COMMENT, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setGenre(const String &s)
|
||||
{
|
||||
if(e->genre != e->document->d->tags.end())
|
||||
e->update(e->genre, Constants::GENRE, s);
|
||||
else
|
||||
e->insert(Constants::GENRE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setYear(unsigned int i)
|
||||
{
|
||||
String s = String::number(i);
|
||||
if(e->year != e->document->d->tags.end())
|
||||
e->update(e->year, Constants::DATE_RELEASE, s);
|
||||
else
|
||||
e->insert(Constants::DATE_RELEASE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setTrack(unsigned int i)
|
||||
{
|
||||
String s = String::number(i);
|
||||
if(e->track != e->document->d->tags.end())
|
||||
e->update(e->track, Constants::PART_NUMBER, s);
|
||||
else
|
||||
e->insert(Constants::PART_NUMBER, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
228
taglib/ebml/matroska/ebmlmatroskafile.h
Normal file
228
taglib/ebml/matroska/ebmlmatroskafile.h
Normal file
@@ -0,0 +1,228 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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_EBMLMATROSKAFILE_H
|
||||
#define TAGLIB_EBMLMATROSKAFILE_H
|
||||
|
||||
#include "ebmlelement.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
//! Implementation for reading Matroska tags.
|
||||
namespace Matroska {
|
||||
|
||||
/*!
|
||||
* Implements the TagLib::File API and offers access to the tags of the
|
||||
* matroska file.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public EBML::File
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the file.
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
explicit File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska 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.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
explicit File(IOStream *stream, bool readproperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Returns the pointer to a tag that allow access on common tags.
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Exports the tags to a PropertyMap. Due to the diversity of the
|
||||
* Matroska format (e.g. multiple media streams in one file, each with
|
||||
* its own tags), only the best fitting tags are taken into account.
|
||||
* There are no unsupported tags.
|
||||
*/
|
||||
virtual PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Sets the tags of the file to those specified in properties. The
|
||||
* returned PropertyMap is always empty.
|
||||
* Note: Only the best fitting tags are taken into account.
|
||||
*/
|
||||
virtual PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to this file's audio properties.
|
||||
*
|
||||
* I'm glad about not having a setAudioProperties method ;)
|
||||
*/
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Saves the file. Returns true on success.
|
||||
*/
|
||||
bool save();
|
||||
|
||||
/*!
|
||||
* Offers access to a few common tag entries.
|
||||
*/
|
||||
class Tag : public TagLib::Tag
|
||||
{
|
||||
public:
|
||||
//! Destroys the tag.
|
||||
~Tag();
|
||||
|
||||
/*!
|
||||
* Creates a new Tag for Matroska files. The given properties are gained
|
||||
* by the Matroska::File.
|
||||
*/
|
||||
explicit Tag(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String title() const;
|
||||
|
||||
/*!
|
||||
* Returns the artist name; if no artist name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String artist() const;
|
||||
|
||||
/*!
|
||||
* Returns the album name; if no album name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String album() const;
|
||||
|
||||
/*!
|
||||
* Returns a list of pictures; if no picture is present in the tag
|
||||
* an empty PictureMap is returned
|
||||
*/
|
||||
virtual PictureMap pictures() const;
|
||||
|
||||
/*!
|
||||
* Returns the track comment; if no comment is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String comment() const;
|
||||
|
||||
/*!
|
||||
* Returns the genre name; if no genre is present in the tag String::null
|
||||
* will be returned.
|
||||
*/
|
||||
virtual String genre() const;
|
||||
|
||||
/*!
|
||||
* Returns the year; if there is no year set, this will return 0.
|
||||
*/
|
||||
virtual unsigned int year() const;
|
||||
|
||||
/*!
|
||||
* Returns the track number; if there is no track number set, this will
|
||||
* return 0.
|
||||
*/
|
||||
virtual unsigned int track() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setTitle(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the artist to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setArtist(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the album to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setAlbum(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the picture map to p. If p is empty then this value will be
|
||||
* cleared
|
||||
*/
|
||||
virtual void setPictures(const PictureMap& p );
|
||||
|
||||
/*!
|
||||
* Sets the comment to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setComment(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the genre to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setGenre(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the year to i. If s is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setYear(unsigned int i);
|
||||
|
||||
/*!
|
||||
* Sets the track to i. If s is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setTrack(unsigned int i);
|
||||
|
||||
private:
|
||||
class TagPrivate;
|
||||
TagPrivate *e;
|
||||
};
|
||||
|
||||
private:
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -27,84 +27,55 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tfilestream.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "fileref.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "tfilestream.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tvariant.h"
|
||||
#include "tdebug.h"
|
||||
#include "aifffile.h"
|
||||
#include "apefile.h"
|
||||
#include "asffile.h"
|
||||
#include "flacfile.h"
|
||||
#include "itfile.h"
|
||||
#include "modfile.h"
|
||||
#include "mp4file.h"
|
||||
#include "mpcfile.h"
|
||||
#include "mpegfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "opusfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "trueaudiofile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "wavfile.h"
|
||||
#include "flacfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "mpcfile.h"
|
||||
#include "mp4file.h"
|
||||
#include "wavpackfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "opusfile.h"
|
||||
#include "trueaudiofile.h"
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#include "apefile.h"
|
||||
#include "modfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "itfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
#include "ebmlmatroskafile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FileRef::FileTypeResolver::FileTypeResolverPrivate
|
||||
{
|
||||
};
|
||||
|
||||
class FileRef::StreamTypeResolver::StreamTypeResolverPrivate
|
||||
{
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
List<const FileRef::FileTypeResolver *> fileTypeResolvers;
|
||||
typedef List<const FileRef::FileTypeResolver *> ResolverList;
|
||||
ResolverList fileTypeResolvers;
|
||||
|
||||
// Detect the file type by user-defined resolvers.
|
||||
|
||||
File *detectByResolvers(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(::wcslen(fileName) == 0)
|
||||
return nullptr;
|
||||
#else
|
||||
if(::strlen(fileName) == 0)
|
||||
return nullptr;
|
||||
#endif
|
||||
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
|
||||
File *file = resolver->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
ResolverList::ConstIterator it = fileTypeResolvers.begin();
|
||||
for(; it != fileTypeResolvers.end(); ++it) {
|
||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
File *detectByResolvers(IOStream* stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
|
||||
if(auto streamResolver = dynamic_cast<const FileRef::StreamTypeResolver *>(resolver)) {
|
||||
if(File *file = streamResolver->createFileFromStream(
|
||||
stream, readAudioProperties, audioPropertiesStyle))
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Detect the file type based on the file extension.
|
||||
@@ -113,14 +84,14 @@ namespace
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const String s = stream->name().toString();
|
||||
const String s(stream->name().wstr());
|
||||
#else
|
||||
const String s(stream->name());
|
||||
#endif
|
||||
|
||||
String ext;
|
||||
const int pos = s.rfind(".");
|
||||
if(pos != -1)
|
||||
const size_t pos = s.rfind(".");
|
||||
if(pos != String::npos())
|
||||
ext = s.substr(pos + 1).upper();
|
||||
|
||||
// If this list is updated, the method defaultFileExtensions() should also be
|
||||
@@ -128,69 +99,54 @@ namespace
|
||||
// that a default file type resolver is created.
|
||||
|
||||
if(ext.isEmpty())
|
||||
return nullptr;
|
||||
return 0;
|
||||
|
||||
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
|
||||
|
||||
File *file = nullptr;
|
||||
|
||||
if(ext == "MP3" || ext == "MP2" || ext == "AAC")
|
||||
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "OGG")
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "OGA") {
|
||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
||||
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(!file->isValid()) {
|
||||
delete file;
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
}
|
||||
else if(ext == "FLAC")
|
||||
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "MPC")
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WV")
|
||||
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "SPX")
|
||||
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "OPUS")
|
||||
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "TTA")
|
||||
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
||||
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WMA" || ext == "ASF")
|
||||
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
||||
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WAV")
|
||||
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "APE")
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "FLAC")
|
||||
return new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "MPC")
|
||||
return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WV")
|
||||
return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "SPX")
|
||||
return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OPUS")
|
||||
return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "TTA")
|
||||
return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
||||
return new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WMA" || ext == "ASF")
|
||||
return new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
||||
return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WAV")
|
||||
return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "APE")
|
||||
return new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
else if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
file = new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "S3M")
|
||||
file = new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "IT")
|
||||
file = new IT::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "XM")
|
||||
file = new XM::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "DSF")
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "DFF" || ext == "DSDIFF")
|
||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
// if file is not valid, leave it to content-based detection.
|
||||
|
||||
if(file) {
|
||||
if(file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
return new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "S3M")
|
||||
return new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "IT")
|
||||
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "DFF" || ext == "DSDIFF")
|
||||
return new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "DSF")
|
||||
return new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if (ext == "MKA" || ext == "MKV") {
|
||||
return new EBML::Matroska::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Detect the file type based on the actual content of the stream.
|
||||
@@ -198,10 +154,10 @@ namespace
|
||||
File *detectByContent(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = nullptr;
|
||||
File *file = 0;
|
||||
|
||||
if(MPEG::File::isSupported(stream))
|
||||
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Vorbis::File::isSupported(stream))
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::FLAC::File::isSupported(stream))
|
||||
@@ -228,53 +184,46 @@ namespace
|
||||
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(APE::File::isSupported(stream))
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(DSF::File::isSupported(stream))
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(DSDIFF::File::isSupported(stream))
|
||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(DSF::File::isSupported(stream))
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
// isSupported() only does a quick check, so double check the file here.
|
||||
|
||||
if(file) {
|
||||
if(file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
else
|
||||
delete file;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
struct FileRefData
|
||||
{
|
||||
FileRefData() :
|
||||
file(0),
|
||||
stream(0) {}
|
||||
|
||||
~FileRefData() {
|
||||
delete file;
|
||||
delete stream;
|
||||
}
|
||||
|
||||
File *file;
|
||||
IOStream *stream;
|
||||
};
|
||||
}
|
||||
|
||||
class FileRef::FileRefPrivate
|
||||
{
|
||||
public:
|
||||
FileRefPrivate() = default;
|
||||
~FileRefPrivate()
|
||||
{
|
||||
delete file;
|
||||
delete stream;
|
||||
}
|
||||
FileRefPrivate() :
|
||||
data(new FileRefData()) {}
|
||||
|
||||
FileRefPrivate(const FileRefPrivate &) = delete;
|
||||
FileRefPrivate &operator=(const FileRefPrivate &) = delete;
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return !file || !file->isValid();
|
||||
}
|
||||
|
||||
bool isNullWithDebugMessage(const String &methodName) const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::" + methodName + "() - Called without a valid file.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
File *file { nullptr };
|
||||
IOStream *stream { nullptr };
|
||||
SHARED_PTR<FileRefData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -282,108 +231,99 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FileRef::FileRef() :
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
FileRef::FileRef(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
parse(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
parse(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(File *file) :
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
d->file = file;
|
||||
d->data->file = file;
|
||||
}
|
||||
|
||||
FileRef::FileRef(const FileRef &) = default;
|
||||
FileRef::FileRef(const FileRef &ref) :
|
||||
d(new FileRefPrivate(*ref.d))
|
||||
{
|
||||
}
|
||||
|
||||
FileRef::~FileRef() = default;
|
||||
FileRef::~FileRef()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
Tag *FileRef::tag() const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return nullptr;
|
||||
if(isNull()) {
|
||||
debug("FileRef::tag() - Called without a valid file.");
|
||||
return 0;
|
||||
}
|
||||
return d->file->tag();
|
||||
return d->data->file->tag();
|
||||
}
|
||||
|
||||
PropertyMap FileRef::properties() const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
if(isNull()) {
|
||||
debug("FileRef::properties() - Called without a valid file.");
|
||||
return PropertyMap();
|
||||
}
|
||||
return d->file->properties();
|
||||
|
||||
return d->data->file->properties();
|
||||
}
|
||||
|
||||
void FileRef::removeUnsupportedProperties(const StringList& properties)
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
if(isNull()) {
|
||||
debug("FileRef::removeUnsupportedProperties() - Called without a valid file.");
|
||||
return;
|
||||
}
|
||||
return d->file->removeUnsupportedProperties(properties);
|
||||
|
||||
d->data->file->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap FileRef::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
if(isNull()) {
|
||||
debug("FileRef::setProperties() - Called without a valid file.");
|
||||
return PropertyMap();
|
||||
}
|
||||
return d->file->setProperties(properties);
|
||||
}
|
||||
|
||||
StringList FileRef::complexPropertyKeys() const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return StringList();
|
||||
}
|
||||
return d->file->complexPropertyKeys();
|
||||
}
|
||||
|
||||
List<VariantMap> FileRef::complexProperties(const String &key) const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return List<VariantMap>();
|
||||
}
|
||||
return d->file->complexProperties(key);
|
||||
}
|
||||
|
||||
bool FileRef::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return false;
|
||||
}
|
||||
return d->file->setComplexProperties(key, value);
|
||||
return d->data->file->setProperties(properties);
|
||||
}
|
||||
|
||||
AudioProperties *FileRef::audioProperties() const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return nullptr;
|
||||
if(isNull()) {
|
||||
debug("FileRef::audioProperties() - Called without a valid file.");
|
||||
return 0;
|
||||
}
|
||||
return d->file->audioProperties();
|
||||
return d->data->file->audioProperties();
|
||||
}
|
||||
|
||||
File *FileRef::file() const
|
||||
{
|
||||
return d->file;
|
||||
return d->data->file;
|
||||
}
|
||||
|
||||
bool FileRef::save()
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
if(isNull()) {
|
||||
debug("FileRef::save() - Called without a valid file.");
|
||||
return false;
|
||||
}
|
||||
return d->file->save();
|
||||
return d->data->file->save();
|
||||
}
|
||||
|
||||
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
|
||||
@@ -392,11 +332,6 @@ const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::Fil
|
||||
return resolver;
|
||||
}
|
||||
|
||||
void FileRef::clearFileTypeResolvers() // static
|
||||
{
|
||||
fileTypeResolvers.clear();
|
||||
}
|
||||
|
||||
StringList FileRef::defaultFileExtensions()
|
||||
{
|
||||
StringList l;
|
||||
@@ -404,14 +339,11 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("ogg");
|
||||
l.append("flac");
|
||||
l.append("oga");
|
||||
l.append("opus");
|
||||
l.append("mp3");
|
||||
l.append("mp2");
|
||||
l.append("mpc");
|
||||
l.append("wv");
|
||||
l.append("spx");
|
||||
l.append("tta");
|
||||
l.append("aac");
|
||||
l.append("m4a");
|
||||
l.append("m4r");
|
||||
l.append("m4b");
|
||||
@@ -423,8 +355,6 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("asf");
|
||||
l.append("aif");
|
||||
l.append("aiff");
|
||||
l.append("afc");
|
||||
l.append("aifc");
|
||||
l.append("wav");
|
||||
l.append("ape");
|
||||
l.append("mod");
|
||||
@@ -437,18 +367,29 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("dsf");
|
||||
l.append("dff");
|
||||
l.append("dsdiff"); // alias for "dff"
|
||||
l.append("mka");
|
||||
l.append("mkv");
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
bool FileRef::isNull() const
|
||||
bool FileRef::isValid() const
|
||||
{
|
||||
return d->isNull();
|
||||
return (d->data->file && d->data->file->isValid());
|
||||
}
|
||||
|
||||
FileRef &FileRef::operator=(const FileRef &) = default;
|
||||
bool FileRef::isNull() const
|
||||
{
|
||||
return !isValid();
|
||||
}
|
||||
|
||||
void FileRef::swap(FileRef &ref) noexcept
|
||||
FileRef &FileRef::operator=(const FileRef &ref)
|
||||
{
|
||||
FileRef(ref).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void FileRef::swap(FileRef &ref)
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
@@ -457,12 +398,12 @@ void FileRef::swap(FileRef &ref) noexcept
|
||||
|
||||
bool FileRef::operator==(const FileRef &ref) const
|
||||
{
|
||||
return (ref.d->file == d->file);
|
||||
return (ref.d->data == d->data);
|
||||
}
|
||||
|
||||
bool FileRef::operator!=(const FileRef &ref) const
|
||||
{
|
||||
return (ref.d->file != d->file);
|
||||
return (ref.d->data != d->data);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -474,57 +415,41 @@ void FileRef::parse(FileName fileName, bool readAudioProperties,
|
||||
{
|
||||
// Try user-defined resolvers.
|
||||
|
||||
d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
d->data->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// Try to resolve file types based on the file extension.
|
||||
|
||||
d->stream = new FileStream(fileName);
|
||||
d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
d->data->stream = new FileStream(fileName);
|
||||
d->data->file = detectByExtension(d->data->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// At last, try to resolve file types based on the actual content.
|
||||
|
||||
d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
d->data->file = detectByContent(d->data->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// Stream have to be closed here if failed to resolve file types.
|
||||
|
||||
delete d->stream;
|
||||
d->stream = nullptr;
|
||||
delete d->data->stream;
|
||||
d->data->stream = 0;
|
||||
}
|
||||
|
||||
void FileRef::parse(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
// Try user-defined stream resolvers.
|
||||
|
||||
d->file = detectByResolvers(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
|
||||
// Try user-defined resolvers.
|
||||
|
||||
d->file = detectByResolvers(stream->name(), readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
// User-defined resolvers won't work with a stream.
|
||||
|
||||
// Try to resolve file types based on the file extension.
|
||||
|
||||
d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
d->data->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// At last, try to resolve file types based on the actual content of the file.
|
||||
|
||||
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
|
||||
d->data->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileTypeResolver::FileTypeResolver() = default;
|
||||
FileRef::FileTypeResolver::~FileTypeResolver() = default;
|
||||
|
||||
FileRef::StreamTypeResolver::StreamTypeResolver() = default;
|
||||
FileRef::StreamTypeResolver::~StreamTypeResolver() = default;
|
||||
|
||||
127
taglib/fileref.h
127
taglib/fileref.h
@@ -26,10 +26,10 @@
|
||||
#ifndef TAGLIB_FILEREF_H
|
||||
#define TAGLIB_FILEREF_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
#include "tstringlist.h"
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
@@ -63,7 +63,7 @@ namespace TagLib {
|
||||
//! A class for pluggable file type resolution.
|
||||
|
||||
/*!
|
||||
* This class is used to extend TagLib's very basic file name based file
|
||||
* This class is used to add extend TagLib's very basic file name based file
|
||||
* type resolution.
|
||||
*
|
||||
* This can be accomplished with:
|
||||
@@ -92,19 +92,12 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT FileTypeResolver
|
||||
{
|
||||
public:
|
||||
FileTypeResolver();
|
||||
/*!
|
||||
* Destroys this FileTypeResolver instance.
|
||||
*/
|
||||
virtual ~FileTypeResolver() = 0;
|
||||
|
||||
FileTypeResolver(const FileTypeResolver &) = delete;
|
||||
FileTypeResolver &operator=(const FileTypeResolver &) = delete;
|
||||
virtual ~FileTypeResolver() {}
|
||||
|
||||
/*!
|
||||
* This method must be overridden to provide an additional file type
|
||||
* resolver. If the resolver is able to determine the file type it should
|
||||
* return a valid File object; if not it should return nullptr.
|
||||
* return a valid File object; if not it should return 0.
|
||||
*
|
||||
* \note The created file is then owned by the FileRef and should not be
|
||||
* deleted. Deletion will happen automatically when the FileRef passes
|
||||
@@ -114,32 +107,6 @@ namespace TagLib {
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle
|
||||
audioPropertiesStyle = AudioProperties::Average) const = 0;
|
||||
private:
|
||||
class FileTypeResolverPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FileTypeResolverPrivate> d;
|
||||
};
|
||||
|
||||
class TAGLIB_EXPORT StreamTypeResolver : public FileTypeResolver
|
||||
{
|
||||
public:
|
||||
StreamTypeResolver();
|
||||
/*!
|
||||
* Destroys this StreamTypeResolver instance.
|
||||
*/
|
||||
~StreamTypeResolver() override = 0;
|
||||
|
||||
StreamTypeResolver(const StreamTypeResolver &) = delete;
|
||||
StreamTypeResolver &operator=(const StreamTypeResolver &) = delete;
|
||||
|
||||
virtual File *createFileFromStream(IOStream *stream,
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle
|
||||
audioPropertiesStyle = AudioProperties::Average) const = 0;
|
||||
private:
|
||||
class StreamTypeResolverPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<StreamTypeResolverPrivate> d;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -192,7 +159,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this FileRef instance.
|
||||
*/
|
||||
~FileRef();
|
||||
virtual ~FileRef();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to represented file's tag.
|
||||
@@ -200,8 +167,8 @@ namespace TagLib {
|
||||
* \warning This pointer will become invalid when this FileRef and all
|
||||
* copies pass out of scope.
|
||||
*
|
||||
* \warning Do not cast it to any subclasses of Tag.
|
||||
* Use tag returning methods of appropriate subclasses of File instead.
|
||||
* \warning Do not cast it to any subclasses of \class Tag.
|
||||
* Use tag returning methods of appropriate subclasses of \class File instead.
|
||||
*
|
||||
* \see File::tag()
|
||||
*/
|
||||
@@ -209,10 +176,10 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Exports the tags of the file as dictionary mapping (human readable) tag
|
||||
* names (uppercase Strings) to StringLists of tag values. Calls this
|
||||
* method on the wrapped File instance.
|
||||
* names (uppercase Strings) to StringLists of tag values. Calls the according
|
||||
* specialization in the File subclasses.
|
||||
* For each metadata object of the file that could not be parsed into the PropertyMap
|
||||
* format, the returned map's unsupportedData() list will contain one entry identifying
|
||||
* format, the returend map's unsupportedData() list will contain one entry identifying
|
||||
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
|
||||
* to remove (a subset of) them.
|
||||
* For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2
|
||||
@@ -228,10 +195,12 @@ namespace TagLib {
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
|
||||
/*!
|
||||
* Sets the tags of the wrapped File to those specified in \a properties.
|
||||
* If some value(s) could not be written to the specific metadata format,
|
||||
* Sets the tags of this File to those specified in \a properties. Calls the
|
||||
* according specialization method in the subclasses of File to do the translation
|
||||
* into the format-specific details.
|
||||
* If some value(s) could not be written imported to the specific metadata format,
|
||||
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
|
||||
* indicating that no problems occurred.
|
||||
* indicating that no problems occured.
|
||||
* With file types that support several tag formats (for instance, MP3 files can have
|
||||
* ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one
|
||||
* (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't
|
||||
@@ -240,51 +209,6 @@ namespace TagLib {
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Get the keys of complex properties, i.e. properties which cannot be
|
||||
* represented simply by a string.
|
||||
* Because such properties might be expensive to fetch, there are separate
|
||||
* operations to get the available keys - which is expected to be cheap -
|
||||
* and getting and setting the property values.
|
||||
* Calls the method on the wrapped File, which collects the keys from one
|
||||
* or more of its tags.
|
||||
*/
|
||||
StringList complexPropertyKeys() const;
|
||||
|
||||
/*!
|
||||
* Get the complex properties for a given \a key.
|
||||
* In order to be flexible for different metadata formats, the properties
|
||||
* are represented as variant maps. Despite this dynamic nature, some
|
||||
* degree of standardization should be achieved between formats:
|
||||
*
|
||||
* - PICTURE
|
||||
* - data: ByteVector with picture data
|
||||
* - description: String with description
|
||||
* - pictureType: String with type as specified for ID3v2,
|
||||
* e.g. "Front Cover", "Back Cover", "Band"
|
||||
* - mimeType: String with image format, e.g. "image/jpeg"
|
||||
* - optionally more information found in the tag, such as
|
||||
* "width", "height", "numColors", "colorDepth" int values
|
||||
* in FLAC pictures
|
||||
* - GENERALOBJECT
|
||||
* - data: ByteVector with object data
|
||||
* - description: String with description
|
||||
* - fileName: String with file name
|
||||
* - mimeType: String with MIME type
|
||||
* - this is currently only implemented for ID3v2 GEOB frames
|
||||
*
|
||||
* Calls the method on the wrapped File, which gets the properties from one
|
||||
* or more of its tags.
|
||||
*/
|
||||
List<VariantMap> complexProperties(const String &key) const;
|
||||
|
||||
/*!
|
||||
* Set all complex properties for a given \a key using variant maps as
|
||||
* \a value with the same format as returned by complexProperties().
|
||||
* An empty list as \a value removes all complex properties for \a key.
|
||||
*/
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value);
|
||||
|
||||
/*!
|
||||
* Returns the audio properties for this FileRef. If no audio properties
|
||||
* were read then this will returns a null pointer.
|
||||
@@ -327,11 +251,6 @@ namespace TagLib {
|
||||
*/
|
||||
static const FileTypeResolver *addFileTypeResolver(const FileTypeResolver *resolver);
|
||||
|
||||
/*!
|
||||
* Remove all resolvers added by addFileTypeResolver().
|
||||
*/
|
||||
static void clearFileTypeResolvers();
|
||||
|
||||
/*!
|
||||
* As is mentioned elsewhere in this class's documentation, the default file
|
||||
* type resolution code provided by TagLib only works by comparing file
|
||||
@@ -350,8 +269,17 @@ namespace TagLib {
|
||||
*/
|
||||
static StringList defaultFileExtensions();
|
||||
|
||||
/*!
|
||||
* Returns true if the file is open and readable.
|
||||
*
|
||||
* \note Just a negative of isNull().
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/*!
|
||||
* Returns true if the file (and as such other pointers) are null.
|
||||
*
|
||||
* \note Just a negative of isValid().
|
||||
*/
|
||||
bool isNull() const;
|
||||
|
||||
@@ -363,7 +291,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Exchanges the content of the FileRef by the content of \a ref.
|
||||
*/
|
||||
void swap(FileRef &ref) noexcept;
|
||||
void swap(FileRef &ref);
|
||||
|
||||
/*!
|
||||
* Returns true if this FileRef and \a ref point to the same File object.
|
||||
@@ -381,8 +309,7 @@ namespace TagLib {
|
||||
void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
|
||||
|
||||
class FileRefPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<FileRefPrivate> d;
|
||||
FileRefPrivate *d;
|
||||
};
|
||||
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,19 +23,22 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "flacfile.h"
|
||||
#include <tbytevector.h>
|
||||
#include <tstring.h>
|
||||
#include <tlist.h>
|
||||
#include <tdebug.h>
|
||||
#include <tagunion.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <id3v2header.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <id3v1tag.h>
|
||||
#include <xiphcomment.h>
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tagunion.h"
|
||||
#include "tagutils.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "id3v1tag.h"
|
||||
#include "xiphcomment.h"
|
||||
#include "flacpicture.h"
|
||||
#include "flacfile.h"
|
||||
#include "flacmetadatablock.h"
|
||||
#include "flacunknownmetadatablock.h"
|
||||
|
||||
@@ -43,38 +46,62 @@ using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
typedef List<SHARED_PTR<FLAC::MetadataBlock> > BlockList;
|
||||
typedef BlockList::Iterator BlockIterator;
|
||||
typedef BlockList::Iterator BlockConstIterator;
|
||||
|
||||
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
|
||||
|
||||
const long MinPaddingLength = 4096;
|
||||
const long MaxPaddingLegnth = 1024 * 1024;
|
||||
const long long MinPaddingLength = 4096;
|
||||
const long long MaxPaddingLegnth = 1024 * 1024;
|
||||
|
||||
const char LastBlockFlag = '\x80';
|
||||
} // namespace
|
||||
}
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
namespace FLAC
|
||||
{
|
||||
// Enables BlockList::find() to take raw pointers.
|
||||
|
||||
bool operator==(SHARED_PTR<MetadataBlock> lhs, MetadataBlock *rhs)
|
||||
{
|
||||
return lhs.get() == rhs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FLAC::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
|
||||
ID3v2FrameFactory(frameFactory)
|
||||
explicit FilePrivate(const ID3v2::FrameFactory *frameFactory) :
|
||||
ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
|
||||
ID3v2Location(-1),
|
||||
ID3v2OriginalSize(0),
|
||||
ID3v1Location(-1),
|
||||
flacStart(0),
|
||||
streamStart(0),
|
||||
scanned(false)
|
||||
{
|
||||
blocks.setAutoDelete(true);
|
||||
if(frameFactory)
|
||||
ID3v2FrameFactory = frameFactory;
|
||||
}
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
offset_t ID3v2Location { -1 };
|
||||
long ID3v2OriginalSize { 0 };
|
||||
long long ID3v2Location;
|
||||
long long ID3v2OriginalSize;
|
||||
|
||||
offset_t ID3v1Location { -1 };
|
||||
long long ID3v1Location;
|
||||
|
||||
TagUnion tag;
|
||||
TripleTagUnion tag;
|
||||
|
||||
std::unique_ptr<Properties> properties;
|
||||
SCOPED_PTR<AudioProperties> properties;
|
||||
ByteVector xiphCommentData;
|
||||
List<FLAC::MetadataBlock *> blocks;
|
||||
BlockList blocks;
|
||||
|
||||
offset_t flacStart { 0 };
|
||||
offset_t streamStart { 0 };
|
||||
bool scanned { false };
|
||||
long long flacStart;
|
||||
long long streamStart;
|
||||
bool scanned;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -86,141 +113,47 @@ bool FLAC::File::isSupported(IOStream *stream)
|
||||
// A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
|
||||
return (buffer.find("fLaC") >= 0);
|
||||
return (buffer.find("fLaC") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle,
|
||||
FLAC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>(
|
||||
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
|
||||
d(new FilePrivate(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle,
|
||||
FLAC::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(
|
||||
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
|
||||
d(new FilePrivate(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
FLAC::File::~File()
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
delete d;
|
||||
}
|
||||
|
||||
FLAC::File::~File() = default;
|
||||
|
||||
TagLib::Tag *FLAC::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
PropertyMap FLAC::File::properties() const
|
||||
{
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
d->tag.removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return xiphComment(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
StringList FLAC::File::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys = TagLib::File::complexPropertyKeys();
|
||||
if(!keys.contains("PICTURE")) {
|
||||
if(std::any_of(d->blocks.cbegin(), d->blocks.cend(),
|
||||
[](MetadataBlock *block) {
|
||||
return dynamic_cast<Picture *>(block) != nullptr;
|
||||
})) {
|
||||
keys.append("PICTURE");
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> FLAC::File::complexProperties(const String &key) const
|
||||
{
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
List<VariantMap> props;
|
||||
for(const auto &block : std::as_const(d->blocks)) {
|
||||
if(auto picture = dynamic_cast<Picture *>(block)) {
|
||||
VariantMap property;
|
||||
property.insert("data", picture->data());
|
||||
property.insert("mimeType", picture->mimeType());
|
||||
property.insert("description", picture->description());
|
||||
property.insert("pictureType",
|
||||
FLAC::Picture::typeToString(picture->type()));
|
||||
property.insert("width", picture->width());
|
||||
property.insert("height", picture->height());
|
||||
property.insert("numColors", picture->numColors());
|
||||
property.insert("colorDepth", picture->colorDepth());
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
return TagLib::File::complexProperties(key);
|
||||
}
|
||||
|
||||
bool FLAC::File::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
removePictures();
|
||||
|
||||
for(const auto &property : value) {
|
||||
auto picture = new FLAC::Picture;
|
||||
picture->setData(property.value("data").value<ByteVector>());
|
||||
picture->setMimeType(property.value("mimeType").value<String>());
|
||||
picture->setDescription(property.value("description").value<String>());
|
||||
picture->setType(FLAC::Picture::typeFromString(
|
||||
property.value("pictureType").value<String>()));
|
||||
picture->setWidth(property.value("width").value<int>());
|
||||
picture->setHeight(property.value("height").value<int>());
|
||||
picture->setNumColors(property.value("numColors").value<int>());
|
||||
picture->setColorDepth(property.value("colorDepth").value<int>());
|
||||
addPicture(picture);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return TagLib::File::setComplexProperties(key, value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FLAC::Properties *FLAC::File::audioProperties() const
|
||||
FLAC::AudioProperties *FLAC::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
@@ -245,40 +178,32 @@ bool FLAC::File::save()
|
||||
|
||||
// Replace metadata blocks
|
||||
|
||||
MetadataBlock *commentBlock =
|
||||
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
|
||||
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
if((*it)->code() == MetadataBlock::VorbisComment) {
|
||||
// Remove the old Vorbis Comment block
|
||||
delete *it;
|
||||
it = d->blocks.erase(it);
|
||||
continue;
|
||||
// Set the new Vorbis Comment block
|
||||
d->blocks.erase(it);
|
||||
break;
|
||||
}
|
||||
if(commentBlock && (*it)->code() == MetadataBlock::Picture) {
|
||||
// Set the new Vorbis Comment block before the first picture block
|
||||
d->blocks.insert(it, commentBlock);
|
||||
commentBlock = nullptr;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if(commentBlock)
|
||||
d->blocks.append(commentBlock);
|
||||
|
||||
d->blocks.append(SHARED_PTR<MetadataBlock>(
|
||||
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData)));
|
||||
|
||||
// Render data for the metadata blocks
|
||||
|
||||
ByteVector data;
|
||||
for(const auto &block : std::as_const(d->blocks)) {
|
||||
ByteVector blockData = block->render();
|
||||
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
|
||||
blockHeader[0] = block->code();
|
||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
ByteVector blockData = (*it)->render();
|
||||
ByteVector blockHeader = ByteVector::fromUInt32BE(blockData.size());
|
||||
blockHeader[0] = (*it)->code();
|
||||
data.append(blockHeader);
|
||||
data.append(blockData);
|
||||
}
|
||||
|
||||
// Compute the amount of padding, and append that to data.
|
||||
|
||||
offset_t originalLength = d->streamStart - d->flacStart;
|
||||
offset_t paddingLength = originalLength - data.size() - 4;
|
||||
long long originalLength = d->streamStart - d->flacStart;
|
||||
long long paddingLength = originalLength - data.size() - 4;
|
||||
|
||||
if(paddingLength <= 0) {
|
||||
paddingLength = MinPaddingLength;
|
||||
@@ -286,22 +211,22 @@ bool FLAC::File::save()
|
||||
else {
|
||||
// Padding won't increase beyond 1% of the file size or 1MB.
|
||||
|
||||
offset_t threshold = length() / 100;
|
||||
threshold = std::max<offset_t>(threshold, MinPaddingLength);
|
||||
threshold = std::min<offset_t>(threshold, MaxPaddingLegnth);
|
||||
long long threshold = length() / 100;
|
||||
threshold = std::max(threshold, MinPaddingLength);
|
||||
threshold = std::min(threshold, MaxPaddingLegnth);
|
||||
|
||||
if(paddingLength > threshold)
|
||||
paddingLength = MinPaddingLength;
|
||||
}
|
||||
|
||||
ByteVector paddingHeader = ByteVector::fromUInt(static_cast<unsigned int>(paddingLength));
|
||||
ByteVector paddingHeader = ByteVector::fromUInt32BE(static_cast<unsigned int>(paddingLength));
|
||||
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
|
||||
data.append(paddingHeader);
|
||||
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
|
||||
data.resize(static_cast<size_t>(data.size() + paddingLength));
|
||||
|
||||
// Write the data to the file
|
||||
|
||||
insert(data, d->flacStart, originalLength);
|
||||
insert(data, d->flacStart, static_cast<size_t>(originalLength));
|
||||
|
||||
d->streamStart += (static_cast<long>(data.size()) - originalLength);
|
||||
|
||||
@@ -318,7 +243,7 @@ bool FLAC::File::save()
|
||||
d->ID3v2Location = 0;
|
||||
|
||||
data = ID3v2Tag()->render();
|
||||
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
|
||||
insert(data, d->ID3v2Location, static_cast<size_t>(d->ID3v2OriginalSize));
|
||||
|
||||
d->flacStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
||||
d->streamStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
||||
@@ -333,7 +258,7 @@ bool FLAC::File::save()
|
||||
// ID3v2 tag is empty. Remove the old one.
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
|
||||
removeBlock(d->ID3v2Location, static_cast<size_t>(d->ID3v2OriginalSize));
|
||||
|
||||
d->flacStart -= d->ID3v2OriginalSize;
|
||||
d->streamStart -= d->ID3v2OriginalSize;
|
||||
@@ -375,8 +300,7 @@ bool FLAC::File::save()
|
||||
|
||||
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create,
|
||||
d->ID3v2FrameFactory);
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create);
|
||||
}
|
||||
|
||||
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
|
||||
@@ -392,8 +316,9 @@ Ogg::XiphComment *FLAC::File::xiphComment(bool create)
|
||||
List<FLAC::Picture *> FLAC::File::pictureList()
|
||||
{
|
||||
List<Picture *> pictures;
|
||||
for(const auto &block : std::as_const(d->blocks)) {
|
||||
if(auto picture = dynamic_cast<Picture *>(block)) {
|
||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
Picture *picture = dynamic_cast<Picture *>(it->get());
|
||||
if(picture) {
|
||||
pictures.append(picture);
|
||||
}
|
||||
}
|
||||
@@ -402,24 +327,20 @@ List<FLAC::Picture *> FLAC::File::pictureList()
|
||||
|
||||
void FLAC::File::addPicture(Picture *picture)
|
||||
{
|
||||
d->blocks.append(picture);
|
||||
d->blocks.append(SHARED_PTR<Picture>(picture));
|
||||
}
|
||||
|
||||
void FLAC::File::removePicture(Picture *picture, bool del)
|
||||
void FLAC::File::removePicture(Picture *picture)
|
||||
{
|
||||
auto it = d->blocks.find(picture);
|
||||
BlockIterator it = d->blocks.find(picture);
|
||||
if(it != d->blocks.end())
|
||||
d->blocks.erase(it);
|
||||
|
||||
if(del)
|
||||
delete picture;
|
||||
}
|
||||
|
||||
void FLAC::File::removePictures()
|
||||
{
|
||||
for(auto it = d->blocks.begin(); it != d->blocks.end(); ) {
|
||||
if(dynamic_cast<Picture *>(*it)) {
|
||||
delete *it;
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
|
||||
if(dynamic_cast<Picture *>(it->get())) {
|
||||
it = d->blocks.erase(it);
|
||||
}
|
||||
else {
|
||||
@@ -431,10 +352,10 @@ void FLAC::File::removePictures()
|
||||
void FLAC::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v1)
|
||||
d->tag.set(FlacID3v1Index, nullptr);
|
||||
d->tag.set(FlacID3v1Index, 0);
|
||||
|
||||
if(tags & ID3v2)
|
||||
d->tag.set(FlacID3v2Index, nullptr);
|
||||
d->tag.set(FlacID3v2Index, 0);
|
||||
|
||||
if(tags & XiphComment) {
|
||||
xiphComment()->removeAllFields();
|
||||
@@ -497,14 +418,14 @@ void FLAC::File::read(bool readProperties)
|
||||
|
||||
const ByteVector infoData = d->blocks.front()->render();
|
||||
|
||||
offset_t streamLength;
|
||||
long long streamLength;
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location - d->streamStart;
|
||||
else
|
||||
streamLength = length() - d->streamStart;
|
||||
|
||||
d->properties = std::make_unique<Properties>(infoData, streamLength);
|
||||
d->properties.reset(new AudioProperties(infoData, streamLength));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +439,7 @@ void FLAC::File::scan()
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
offset_t nextBlockOffset;
|
||||
long long nextBlockOffset;
|
||||
|
||||
if(d->ID3v2Location >= 0)
|
||||
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
|
||||
@@ -538,11 +459,6 @@ void FLAC::File::scan()
|
||||
|
||||
seek(nextBlockOffset);
|
||||
const ByteVector header = readBlock(4);
|
||||
if(header.size() != 4) {
|
||||
debug("FLAC::File::scan() -- Failed to read a block header");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Header format (from spec):
|
||||
// <1> Last-metadata-block flag
|
||||
@@ -558,7 +474,7 @@ void FLAC::File::scan()
|
||||
|
||||
const char blockType = header[0] & ~LastBlockFlag;
|
||||
const bool isLastBlock = (header[0] & LastBlockFlag) != 0;
|
||||
const unsigned int blockLength = header.toUInt(1U, 3U);
|
||||
const size_t blockLength = header.toUInt24BE(1);
|
||||
|
||||
// First block should be the stream_info metadata
|
||||
|
||||
@@ -583,33 +499,32 @@ void FLAC::File::scan()
|
||||
return;
|
||||
}
|
||||
|
||||
MetadataBlock *block = nullptr;
|
||||
SHARED_PTR<MetadataBlock> block;
|
||||
|
||||
// Found the vorbis-comment
|
||||
if(blockType == MetadataBlock::VorbisComment) {
|
||||
if(d->xiphCommentData.isEmpty()) {
|
||||
d->xiphCommentData = data;
|
||||
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
|
||||
block.reset(new UnknownMetadataBlock(MetadataBlock::VorbisComment, data));
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Picture) {
|
||||
auto picture = new FLAC::Picture();
|
||||
SHARED_PTR<FLAC::Picture> picture(new FLAC::Picture());
|
||||
if(picture->parse(data)) {
|
||||
block = picture;
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- invalid picture found, discarding");
|
||||
delete picture;
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Padding) {
|
||||
// Skip all padding blocks.
|
||||
}
|
||||
else {
|
||||
block = new UnknownMetadataBlock(blockType, data);
|
||||
block.reset(new UnknownMetadataBlock(blockType, data));
|
||||
}
|
||||
|
||||
if(block)
|
||||
|
||||
@@ -26,10 +26,11 @@
|
||||
#ifndef TAGLIB_FLACFILE_H
|
||||
#define TAGLIB_FLACFILE_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
#include "tlist.h"
|
||||
#include "taglib_export.h"
|
||||
#include "tag.h"
|
||||
|
||||
#include "flacpicture.h"
|
||||
#include "flacproperties.h"
|
||||
|
||||
@@ -82,73 +83,41 @@ namespace TagLib {
|
||||
AllTags = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Constructs an FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
TAGLIB_DEPRECATED
|
||||
File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
File(FileName file,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = 0);
|
||||
|
||||
/*!
|
||||
* Constructs a FLAC file from \a stream. If \a readProperties is true the
|
||||
* Constructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Constructs a FLAC file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
TAGLIB_DEPRECATED
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
File(IOStream *stream,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = 0);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file. This will be a union of XiphComment,
|
||||
@@ -158,17 +127,7 @@ namespace TagLib {
|
||||
* \see ID3v1Tag()
|
||||
* \see XiphComment()
|
||||
*/
|
||||
TagLib::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* If the file contains more than one tag (e.g. XiphComment and ID3v1),
|
||||
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
|
||||
* converted to the PropertyMap.
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &) override;
|
||||
virtual TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
@@ -177,30 +136,13 @@ namespace TagLib {
|
||||
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
|
||||
* in the FLAC specification.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns ["PICTURE"] if any picture is stored in METADATA_BLOCK_PICTURE.
|
||||
*/
|
||||
StringList complexPropertyKeys() const override;
|
||||
|
||||
/*!
|
||||
* Get the pictures stored in METADATA_BLOCK_PICTURE as complex properties
|
||||
* for \a key "PICTURE".
|
||||
*/
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
|
||||
/*!
|
||||
* Set the complex properties \a value as pictures in METADATA_BLOCK_PICTURE
|
||||
* for \a key "PICTURE".
|
||||
*/
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
virtual PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the FLAC::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file. This will primarily save the XiphComment, but
|
||||
@@ -209,7 +151,7 @@ namespace TagLib {
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*/
|
||||
bool save() override;
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v2 tag of the file.
|
||||
@@ -274,10 +216,9 @@ namespace TagLib {
|
||||
List<Picture *> pictureList();
|
||||
|
||||
/*!
|
||||
* Removes an attached picture. If \a del is true the picture's memory
|
||||
* will be freed; if it is false, it must be deleted by the user.
|
||||
* Removes an attached picture. The picture's memory will be freed.
|
||||
*/
|
||||
void removePicture(Picture *picture, bool del = true);
|
||||
void removePicture(Picture *picture);
|
||||
|
||||
/*!
|
||||
* Remove all attached images.
|
||||
@@ -338,14 +279,16 @@ namespace TagLib {
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties);
|
||||
void scan();
|
||||
|
||||
class FilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
FilePrivate *d;
|
||||
};
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,14 +23,25 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FLAC::MetadataBlock::MetadataBlockPrivate
|
||||
{
|
||||
public:
|
||||
MetadataBlockPrivate() {}
|
||||
|
||||
};
|
||||
|
||||
FLAC::MetadataBlock::MetadataBlock() = default;
|
||||
FLAC::MetadataBlock::MetadataBlock()
|
||||
{
|
||||
d = 0;
|
||||
}
|
||||
|
||||
FLAC::MetadataBlock::~MetadataBlock()
|
||||
{
|
||||
}
|
||||
|
||||
FLAC::MetadataBlock::~MetadataBlock() = default;
|
||||
|
||||
@@ -31,16 +31,15 @@
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class TAGLIB_EXPORT MetadataBlock
|
||||
{
|
||||
public:
|
||||
MetadataBlock();
|
||||
virtual ~MetadataBlock();
|
||||
|
||||
MetadataBlock(const MetadataBlock &item) = delete;
|
||||
MetadataBlock &operator=(const MetadataBlock &item) = delete;
|
||||
|
||||
enum BlockType {
|
||||
StreamInfo = 0,
|
||||
Padding,
|
||||
@@ -62,10 +61,15 @@ namespace TagLib {
|
||||
virtual ByteVector render() const = 0;
|
||||
|
||||
private:
|
||||
MetadataBlock(const MetadataBlock &item);
|
||||
MetadataBlock &operator=(const MetadataBlock &item);
|
||||
|
||||
class MetadataBlockPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<MetadataBlockPrivate> d;
|
||||
MetadataBlockPrivate *d;
|
||||
};
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,37 +23,48 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "flacpicture.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FLAC::Picture::PicturePrivate
|
||||
{
|
||||
public:
|
||||
Type type { FLAC::Picture::Other };
|
||||
PicturePrivate() :
|
||||
type(FLAC::Picture::Other),
|
||||
width(0),
|
||||
height(0),
|
||||
colorDepth(0),
|
||||
numColors(0)
|
||||
{}
|
||||
|
||||
Type type;
|
||||
String mimeType;
|
||||
String description;
|
||||
int width { 0 };
|
||||
int height { 0 };
|
||||
int colorDepth { 0 };
|
||||
int numColors { 0 };
|
||||
int width;
|
||||
int height;
|
||||
int colorDepth;
|
||||
int numColors;
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::Picture::Picture() :
|
||||
d(std::make_unique<PicturePrivate>())
|
||||
d(new PicturePrivate())
|
||||
{
|
||||
}
|
||||
|
||||
FLAC::Picture::Picture(const ByteVector &data) :
|
||||
d(std::make_unique<PicturePrivate>())
|
||||
d(new PicturePrivate())
|
||||
{
|
||||
parse(data);
|
||||
}
|
||||
|
||||
FLAC::Picture::~Picture() = default;
|
||||
FLAC::Picture::~Picture()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int FLAC::Picture::code() const
|
||||
{
|
||||
@@ -67,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int pos = 0;
|
||||
d->type = static_cast<FLAC::Picture::Type>(data.toUInt(pos));
|
||||
size_t pos = 0;
|
||||
d->type = FLAC::Picture::Type(data.toUInt32BE(pos));
|
||||
pos += 4;
|
||||
unsigned int mimeTypeLength = data.toUInt(pos);
|
||||
const unsigned int mimeTypeLength = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
if(pos + mimeTypeLength + 24 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -78,7 +89,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
|
||||
pos += mimeTypeLength;
|
||||
unsigned int descriptionLength = data.toUInt(pos);
|
||||
const unsigned int descriptionLength = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
if(pos + descriptionLength + 20 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -86,15 +97,15 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->description = String(data.mid(pos, descriptionLength), String::UTF8);
|
||||
pos += descriptionLength;
|
||||
d->width = data.toUInt(pos);
|
||||
d->width = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
d->height = data.toUInt(pos);
|
||||
d->height = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
d->colorDepth = data.toUInt(pos);
|
||||
d->colorDepth = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
d->numColors = data.toUInt(pos);
|
||||
d->numColors = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
unsigned int dataLength = data.toUInt(pos);
|
||||
const unsigned int dataLength = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
if(pos + dataLength > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -108,18 +119,18 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
ByteVector FLAC::Picture::render() const
|
||||
{
|
||||
ByteVector result;
|
||||
result.append(ByteVector::fromUInt(d->type));
|
||||
result.append(ByteVector::fromUInt32BE(d->type));
|
||||
ByteVector mimeTypeData = d->mimeType.data(String::UTF8);
|
||||
result.append(ByteVector::fromUInt(mimeTypeData.size()));
|
||||
result.append(ByteVector::fromUInt32BE(mimeTypeData.size()));
|
||||
result.append(mimeTypeData);
|
||||
ByteVector descriptionData = d->description.data(String::UTF8);
|
||||
result.append(ByteVector::fromUInt(descriptionData.size()));
|
||||
result.append(ByteVector::fromUInt32BE(descriptionData.size()));
|
||||
result.append(descriptionData);
|
||||
result.append(ByteVector::fromUInt(d->width));
|
||||
result.append(ByteVector::fromUInt(d->height));
|
||||
result.append(ByteVector::fromUInt(d->colorDepth));
|
||||
result.append(ByteVector::fromUInt(d->numColors));
|
||||
result.append(ByteVector::fromUInt(d->data.size()));
|
||||
result.append(ByteVector::fromUInt32BE(d->width));
|
||||
result.append(ByteVector::fromUInt32BE(d->height));
|
||||
result.append(ByteVector::fromUInt32BE(d->colorDepth));
|
||||
result.append(ByteVector::fromUInt32BE(d->numColors));
|
||||
result.append(ByteVector::fromUInt32BE(d->data.size()));
|
||||
result.append(d->data);
|
||||
return result;
|
||||
}
|
||||
@@ -203,3 +214,4 @@ void FLAC::Picture::setData(const ByteVector &data)
|
||||
{
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,13 @@
|
||||
#include "tlist.h"
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tpicturetype.h"
|
||||
#include "taglib_export.h"
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class TAGLIB_EXPORT Picture : public MetadataBlock
|
||||
{
|
||||
public:
|
||||
@@ -42,14 +43,54 @@ namespace TagLib {
|
||||
/*!
|
||||
* This describes the function or content of the picture.
|
||||
*/
|
||||
DECLARE_PICTURE_TYPE_ENUM(Type)
|
||||
enum Type {
|
||||
//! A type not enumerated below
|
||||
Other = 0x00,
|
||||
//! 32x32 PNG image that should be used as the file icon
|
||||
FileIcon = 0x01,
|
||||
//! File icon of a different size or format
|
||||
OtherFileIcon = 0x02,
|
||||
//! Front cover image of the album
|
||||
FrontCover = 0x03,
|
||||
//! Back cover image of the album
|
||||
BackCover = 0x04,
|
||||
//! Inside leaflet page of the album
|
||||
LeafletPage = 0x05,
|
||||
//! Image from the album itself
|
||||
Media = 0x06,
|
||||
//! Picture of the lead artist or soloist
|
||||
LeadArtist = 0x07,
|
||||
//! Picture of the artist or performer
|
||||
Artist = 0x08,
|
||||
//! Picture of the conductor
|
||||
Conductor = 0x09,
|
||||
//! Picture of the band or orchestra
|
||||
Band = 0x0A,
|
||||
//! Picture of the composer
|
||||
Composer = 0x0B,
|
||||
//! Picture of the lyricist or text writer
|
||||
Lyricist = 0x0C,
|
||||
//! Picture of the recording location or studio
|
||||
RecordingLocation = 0x0D,
|
||||
//! Picture of the artists during recording
|
||||
DuringRecording = 0x0E,
|
||||
//! Picture of the artists during performance
|
||||
DuringPerformance = 0x0F,
|
||||
//! Picture from a movie or video related to the track
|
||||
MovieScreenCapture = 0x10,
|
||||
//! Picture of a large, coloured fish
|
||||
ColouredFish = 0x11,
|
||||
//! Illustration related to the track
|
||||
Illustration = 0x12,
|
||||
//! Logo of the band or performer
|
||||
BandLogo = 0x13,
|
||||
//! Logo of the publisher (record company)
|
||||
PublisherLogo = 0x14
|
||||
};
|
||||
|
||||
Picture();
|
||||
Picture(const ByteVector &data);
|
||||
~Picture() override;
|
||||
|
||||
Picture(const Picture &item) = delete;
|
||||
Picture &operator=(const Picture &item) = delete;
|
||||
explicit Picture(const ByteVector &data);
|
||||
~Picture();
|
||||
|
||||
/*!
|
||||
* Returns the type of the image.
|
||||
@@ -138,12 +179,12 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the FLAC metadata block type.
|
||||
*/
|
||||
int code() const override;
|
||||
int code() const;
|
||||
|
||||
/*!
|
||||
* Render the content to the FLAC picture block format.
|
||||
*/
|
||||
ByteVector render() const override;
|
||||
ByteVector render() const;
|
||||
|
||||
/*!
|
||||
* Parse the picture data in the FLAC picture block format.
|
||||
@@ -151,12 +192,15 @@ namespace TagLib {
|
||||
bool parse(const ByteVector &rawData);
|
||||
|
||||
private:
|
||||
Picture(const Picture &item);
|
||||
Picture &operator=(const Picture &item);
|
||||
|
||||
class PicturePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PicturePrivate> d;
|
||||
PicturePrivate *d;
|
||||
};
|
||||
|
||||
using PictureList = List<Picture>;
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,24 +23,31 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "flacproperties.h"
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
#include "flacfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FLAC::Properties::PropertiesPrivate
|
||||
class FLAC::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
int channels { 0 };
|
||||
unsigned long long sampleFrames { 0 };
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
bitsPerSample(0),
|
||||
channels(0),
|
||||
sampleFrames(0) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int bitsPerSample;
|
||||
int channels;
|
||||
unsigned long long sampleFrames;
|
||||
ByteVector signature;
|
||||
};
|
||||
|
||||
@@ -48,46 +55,64 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::Properties::Properties(const ByteVector &data, offset_t streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
FLAC::AudioProperties::AudioProperties(const ByteVector &data, long long streamLength, ReadStyle) :
|
||||
TagLib::AudioProperties(),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
read(data, streamLength);
|
||||
}
|
||||
|
||||
FLAC::Properties::~Properties() = default;
|
||||
FLAC::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int FLAC::Properties::lengthInMilliseconds() const
|
||||
int FLAC::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int FLAC::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int FLAC::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int FLAC::Properties::bitrate() const
|
||||
int FLAC::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int FLAC::Properties::sampleRate() const
|
||||
int FLAC::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int FLAC::Properties::bitsPerSample() const
|
||||
int FLAC::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
int FLAC::Properties::channels() const
|
||||
int FLAC::AudioProperties::sampleWidth() const
|
||||
{
|
||||
return bitsPerSample();
|
||||
}
|
||||
|
||||
int FLAC::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
unsigned long long FLAC::Properties::sampleFrames() const
|
||||
unsigned long long FLAC::AudioProperties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
|
||||
ByteVector FLAC::Properties::signature() const
|
||||
ByteVector FLAC::AudioProperties::signature() const
|
||||
{
|
||||
return d->signature;
|
||||
}
|
||||
@@ -96,14 +121,14 @@ ByteVector FLAC::Properties::signature() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
|
||||
void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength)
|
||||
{
|
||||
if(data.size() < 18) {
|
||||
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
|
||||
debug("FLAC::AudioProperties::read() - FLAC properties must contain at least 18 bytes.");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int pos = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
// Minimum block size (in samples)
|
||||
pos += 2;
|
||||
@@ -117,7 +142,7 @@ void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
|
||||
// Maximum frame size (in bytes)
|
||||
pos += 3;
|
||||
|
||||
const unsigned int flags = data.toUInt(pos, true);
|
||||
const unsigned int flags = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
|
||||
d->sampleRate = flags >> 12;
|
||||
@@ -128,7 +153,7 @@ void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
|
||||
// stream length in samples. (Audio files measured in days)
|
||||
|
||||
const unsigned long long hi = flags & 0xf;
|
||||
const unsigned long long lo = data.toUInt(pos, true);
|
||||
const unsigned long long lo = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
|
||||
d->sampleFrames = (hi << 32) | lo;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#ifndef TAGLIB_FLACPROPERTIES_H
|
||||
#define TAGLIB_FLACPROPERTIES_H
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
@@ -34,6 +33,8 @@ namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for FLAC
|
||||
|
||||
/*!
|
||||
@@ -41,44 +42,59 @@ namespace TagLib {
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of FLAC::Properties with the data read from the
|
||||
* ByteVector \a data.
|
||||
* Creates an instance of FLAC::AudioProperties with the data read from
|
||||
* the ByteVector \a data.
|
||||
*/
|
||||
Properties(const ByteVector &data, offset_t streamLength, ReadStyle style = Average);
|
||||
AudioProperties(const ByteVector &data, long long streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this FLAC::Properties instance.
|
||||
* Destroys this FLAC::AudioProperties instance.
|
||||
*/
|
||||
~Properties() override;
|
||||
virtual ~AudioProperties();
|
||||
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
int lengthInMilliseconds() const override;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
int bitrate() const override;
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
int sampleRate() const override;
|
||||
virtual int sampleRate() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
int channels() const override;
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample as read from the FLAC
|
||||
@@ -86,6 +102,16 @@ namespace TagLib {
|
||||
*/
|
||||
int bitsPerSample() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample width as read from the FLAC identification
|
||||
* header.
|
||||
*
|
||||
* \note This method is just an alias of bitsPerSample().
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
int sampleWidth() const;
|
||||
|
||||
/*!
|
||||
* Return the number of sample frames.
|
||||
*/
|
||||
@@ -98,13 +124,12 @@ namespace TagLib {
|
||||
ByteVector signature() const;
|
||||
|
||||
private:
|
||||
void read(const ByteVector &data, offset_t streamLength);
|
||||
void read(const ByteVector &data, long long streamLength);
|
||||
|
||||
class PropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "flacunknownmetadatablock.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -30,18 +33,23 @@ using namespace TagLib;
|
||||
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
|
||||
{
|
||||
public:
|
||||
int code { 0 };
|
||||
UnknownMetadataBlockPrivate() : code(0) {}
|
||||
|
||||
int code;
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) :
|
||||
d(std::make_unique<UnknownMetadataBlockPrivate>())
|
||||
d(new UnknownMetadataBlockPrivate())
|
||||
{
|
||||
d->code = code;
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock() = default;
|
||||
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int FLAC::UnknownMetadataBlock::code() const
|
||||
{
|
||||
@@ -67,3 +75,4 @@ ByteVector FLAC::UnknownMetadataBlock::render() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,20 +32,19 @@
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock
|
||||
{
|
||||
public:
|
||||
UnknownMetadataBlock(int code, const ByteVector &data);
|
||||
~UnknownMetadataBlock() override;
|
||||
|
||||
UnknownMetadataBlock(const UnknownMetadataBlock &item) = delete;
|
||||
UnknownMetadataBlock &operator=(const UnknownMetadataBlock &item) = delete;
|
||||
UnknownMetadataBlock(int blockType, const ByteVector &data);
|
||||
~UnknownMetadataBlock();
|
||||
|
||||
/*!
|
||||
* Returns the FLAC metadata block type.
|
||||
*/
|
||||
int code() const override;
|
||||
int code() const;
|
||||
|
||||
/*!
|
||||
* Sets the FLAC metadata block type.
|
||||
@@ -65,13 +64,18 @@ namespace TagLib {
|
||||
/*!
|
||||
* Render the content of the block.
|
||||
*/
|
||||
ByteVector render() const override;
|
||||
ByteVector render() const;
|
||||
|
||||
private:
|
||||
UnknownMetadataBlock(const MetadataBlock &item);
|
||||
UnknownMetadataBlock &operator=(const MetadataBlock &item);
|
||||
|
||||
class UnknownMetadataBlockPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<UnknownMetadataBlockPrivate> d;
|
||||
UnknownMetadataBlockPrivate *d;
|
||||
};
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "itfile.h"
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "itfile.h"
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "modfileprivate.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace IT;
|
||||
@@ -36,19 +36,19 @@ using namespace IT;
|
||||
class IT::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
: properties(propertiesStyle)
|
||||
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
: tag(), properties(propertiesStyle)
|
||||
{
|
||||
}
|
||||
|
||||
Mod::Tag tag;
|
||||
IT::Properties properties;
|
||||
IT::AudioProperties properties;
|
||||
};
|
||||
|
||||
IT::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(file),
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -57,20 +57,23 @@ IT::File::File(FileName file, bool readProperties,
|
||||
IT::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(stream),
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
IT::File::~File() = default;
|
||||
IT::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
Mod::Tag *IT::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
IT::Properties *IT::File::audioProperties() const
|
||||
IT::AudioProperties *IT::File::audioProperties() const
|
||||
{
|
||||
return &d->properties;
|
||||
}
|
||||
@@ -100,8 +103,8 @@ bool IT::File::save()
|
||||
// write comment as instrument and sample names:
|
||||
StringList lines = d->tag.comment().split("\n");
|
||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + (static_cast<long>(i) << 2));
|
||||
unsigned long instrumentOffset = 0;
|
||||
seek(192L + length + ((long)i << 2));
|
||||
unsigned int instrumentOffset = 0;
|
||||
if(!readU32L(instrumentOffset))
|
||||
return false;
|
||||
|
||||
@@ -115,14 +118,14 @@ bool IT::File::save()
|
||||
}
|
||||
|
||||
for(unsigned short i = 0; i < sampleCount; ++ i) {
|
||||
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
|
||||
unsigned long sampleOffset = 0;
|
||||
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
|
||||
unsigned int sampleOffset = 0;
|
||||
if(!readU32L(sampleOffset))
|
||||
return false;
|
||||
|
||||
seek(sampleOffset + 20);
|
||||
|
||||
if(static_cast<unsigned int>(i + instrumentCount) < lines.size())
|
||||
if((unsigned int)(i + instrumentCount) < lines.size())
|
||||
writeString(lines[i + instrumentCount], 25);
|
||||
else
|
||||
writeString(String(), 25);
|
||||
@@ -139,18 +142,18 @@ bool IT::File::save()
|
||||
// terminating NUL but it does not hurt to add one:
|
||||
if(message.size() > 7999)
|
||||
message.resize(7999);
|
||||
message.append(static_cast<char>(0));
|
||||
message.append((char)0);
|
||||
|
||||
unsigned short special = 0;
|
||||
unsigned short messageLength = 0;
|
||||
unsigned long messageOffset = 0;
|
||||
unsigned int messageOffset = 0;
|
||||
|
||||
seek(46);
|
||||
if(!readU16L(special))
|
||||
return false;
|
||||
|
||||
auto fileSize = static_cast<unsigned long>(File::length());
|
||||
if(special & Properties::MessageAttached) {
|
||||
unsigned int fileSize = static_cast<unsigned int>(File::length());
|
||||
if(special & AudioProperties::MessageAttached) {
|
||||
seek(54);
|
||||
if(!readU16L(messageLength) || !readU32L(messageOffset))
|
||||
return false;
|
||||
@@ -168,7 +171,7 @@ bool IT::File::save()
|
||||
if(messageOffset + messageLength >= fileSize) {
|
||||
// append new message
|
||||
seek(54);
|
||||
writeU16L(message.size());
|
||||
writeU16L(static_cast<unsigned short>(message.size()));
|
||||
writeU32L(messageOffset);
|
||||
seek(messageOffset);
|
||||
writeBlock(message);
|
||||
@@ -220,14 +223,14 @@ void IT::File::read(bool)
|
||||
// sample/instrument names are abused as comments so
|
||||
// I just add all together.
|
||||
String message;
|
||||
if(special & Properties::MessageAttached) {
|
||||
if(special & AudioProperties::MessageAttached) {
|
||||
READ_U16L_AS(messageLength);
|
||||
READ_U32L_AS(messageOffset);
|
||||
seek(messageOffset);
|
||||
ByteVector messageBytes = readBlock(messageLength);
|
||||
READ_ASSERT(messageBytes.size() == messageLength);
|
||||
int index = messageBytes.find(static_cast<char>(0));
|
||||
if(index > -1)
|
||||
const size_t index = messageBytes.find((char) 0);
|
||||
if(index != ByteVector::npos())
|
||||
messageBytes.resize(index, 0);
|
||||
messageBytes.replace('\r', '\n');
|
||||
message = messageBytes;
|
||||
@@ -244,8 +247,8 @@ void IT::File::read(bool)
|
||||
// I don't count disabled and muted channels.
|
||||
// But this always gives 64 channels for all my files anyway.
|
||||
// Strangely VLC does report other values. I wonder how VLC
|
||||
// gets its values.
|
||||
if(static_cast<unsigned char>(pannings[i]) < 128 && volumes[i] > 0)
|
||||
// gets it's values.
|
||||
if((unsigned char) pannings[i] < 128 && volumes[i] > 0)
|
||||
++channels;
|
||||
}
|
||||
d->properties.setChannels(channels);
|
||||
@@ -264,10 +267,10 @@ void IT::File::read(bool)
|
||||
// in the instrument/sample names and more characters
|
||||
// afterwards. The spec does not mention such a case.
|
||||
// Currently I just discard anything after a nil, but
|
||||
// e.g. VLC seems to interpret a nil as a space. I
|
||||
// e.g. VLC seems to interprete a nil as a space. I
|
||||
// don't know what is the proper behaviour.
|
||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + (static_cast<long>(i) << 2));
|
||||
seek(192L + length + ((long)i << 2));
|
||||
READ_U32L_AS(instrumentOffset);
|
||||
seek(instrumentOffset);
|
||||
|
||||
@@ -283,7 +286,7 @@ void IT::File::read(bool)
|
||||
}
|
||||
|
||||
for(unsigned short i = 0; i < sampleCount; ++ i) {
|
||||
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
|
||||
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
|
||||
READ_U32L_AS(sampleOffset);
|
||||
|
||||
seek(sampleOffset);
|
||||
@@ -315,7 +318,7 @@ void IT::File::read(bool)
|
||||
comment.append(sampleName);
|
||||
}
|
||||
|
||||
if(!message.isEmpty())
|
||||
if(message.size() > 0)
|
||||
comment.append(message);
|
||||
d->tag.setComment(comment.toString("\n"));
|
||||
d->tag.setTrackerName("Impulse Tracker");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user