mirror of
https://github.com/taglib/taglib.git
synced 2026-06-17 11:49:21 -04:00
Compare commits
170 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd4c9cbf97 | ||
|
|
7dc8bfc806 | ||
|
|
1a1ee8b54f | ||
|
|
cd044bfc8f | ||
|
|
7eda31e6ef | ||
|
|
0e6f888d4d | ||
|
|
b07db7510f | ||
|
|
1635d4d563 | ||
|
|
6d0f0ad170 | ||
|
|
eaf7955c63 | ||
|
|
78c489d9cc | ||
|
|
56fa36934e | ||
|
|
0dff3150c1 | ||
|
|
e414987344 | ||
|
|
47184c4447 | ||
|
|
ab0437db0e | ||
|
|
1ee7493abc | ||
|
|
d869f1b3e4 | ||
|
|
2044b31698 | ||
|
|
6e0741bcdc | ||
|
|
e9a671476b | ||
|
|
5b19c7aed3 | ||
|
|
98a9530d23 | ||
|
|
4775c83d8d | ||
|
|
034262c518 | ||
|
|
57c8dbe014 | ||
|
|
bfaf1be6a6 | ||
|
|
c9f7772198 | ||
|
|
549bca3382 | ||
|
|
98961813dc | ||
|
|
9a08678098 | ||
|
|
c1d8159a34 | ||
|
|
fa234fc053 | ||
|
|
b4fe04f8e0 | ||
|
|
c9486731d9 | ||
|
|
ec734bbe08 | ||
|
|
c7babea74a | ||
|
|
8a42e552aa | ||
|
|
28baa03b23 | ||
|
|
10094f66b9 | ||
|
|
aca1d0d41c | ||
|
|
491322d1ba | ||
|
|
8b3f1a459e | ||
|
|
131918333b | ||
|
|
cf2bdce21d | ||
|
|
51d63ab285 | ||
|
|
3f11e0ae2f | ||
|
|
bd06012b02 | ||
|
|
9233ff1f5d | ||
|
|
88219f2493 | ||
|
|
9df243ef74 | ||
|
|
c1bb678695 | ||
|
|
56382e8cd4 | ||
|
|
8bb8fc5fe6 | ||
|
|
8b564baf01 | ||
|
|
3d67b139e4 | ||
|
|
59166f6757 | ||
|
|
b993e70cf4 | ||
|
|
b7dc1ab8ac | ||
|
|
c86a2fce70 | ||
|
|
faddc4aa06 | ||
|
|
16326b2479 | ||
|
|
c083d7ce15 | ||
|
|
9679b08120 | ||
|
|
0e395f4ec4 | ||
|
|
52b245f015 | ||
|
|
2f4c76b52a | ||
|
|
dfa33bec08 | ||
|
|
f202fa25c3 | ||
|
|
70b4ce79fb | ||
|
|
8c63c877ad | ||
|
|
3869aa189f | ||
|
|
a7c0b53f7a | ||
|
|
135c0eb647 | ||
|
|
0db487bf61 | ||
|
|
8d98ebf24b | ||
|
|
11f94903d0 | ||
|
|
dce8e016b9 | ||
|
|
6b5f28d56d | ||
|
|
19cceab211 | ||
|
|
182edcd3f9 | ||
|
|
a3ad2d0aaa | ||
|
|
1d213b8b98 | ||
|
|
b40b834b1b | ||
|
|
6be03b7ae1 | ||
|
|
75d4252480 | ||
|
|
304ab62957 | ||
|
|
89a54863ee | ||
|
|
3f25d3c342 | ||
|
|
2e3a66cc31 | ||
|
|
41077aa57e | ||
|
|
21b08c0dcb | ||
|
|
8a800b8c38 | ||
|
|
c332aa04f2 | ||
|
|
24e0ac7aa4 | ||
|
|
ad1696ade6 | ||
|
|
c13a42021a | ||
|
|
2154078187 | ||
|
|
eaf7ff8b94 | ||
|
|
39d1d68237 | ||
|
|
e275abb8a3 | ||
|
|
967aaf7af2 | ||
|
|
48c4bf9c05 | ||
|
|
bdf50eda99 | ||
|
|
653c2fe9e1 | ||
|
|
6ed0ca28db | ||
|
|
cf5ad66922 | ||
|
|
97a74ca3d8 | ||
|
|
7646184d6b | ||
|
|
2dc1aa4ec9 | ||
|
|
00cfee30b6 | ||
|
|
88cb197111 | ||
|
|
110a253ba3 | ||
|
|
d42e8ed3fe | ||
|
|
37ba5a5cc1 | ||
|
|
23186f24ff | ||
|
|
528b84fbde | ||
|
|
912897cd35 | ||
|
|
54f84cc924 | ||
|
|
47c4e0859c | ||
|
|
524b588a1e | ||
|
|
303b55fb97 | ||
|
|
ecf1d4fa53 | ||
|
|
d2bd56c519 | ||
|
|
040b069957 | ||
|
|
f44ea9b80b | ||
|
|
0ba61343a4 | ||
|
|
cdc87aec10 | ||
|
|
4a86489186 | ||
|
|
85c678f587 | ||
|
|
29e88cfe66 | ||
|
|
128c55bc53 | ||
|
|
cf352ac7f4 | ||
|
|
bbb8221301 | ||
|
|
f69c21c8e6 | ||
|
|
574604765f | ||
|
|
f2d0e664e7 | ||
|
|
185bb7042e | ||
|
|
843a8aac80 | ||
|
|
dcef356e3f | ||
|
|
868f4eef3d | ||
|
|
c0e9428218 | ||
|
|
bec59b4b7b | ||
|
|
bd6c3ba174 | ||
|
|
ee8124ed7a | ||
|
|
965260e3cb | ||
|
|
f903e1ad71 | ||
|
|
a1bdb0171d | ||
|
|
9bcba812af | ||
|
|
271bd05afa | ||
|
|
c54c924333 | ||
|
|
d314bfa06a | ||
|
|
cc7d23cdf5 | ||
|
|
2a1fb27735 | ||
|
|
b5516c9718 | ||
|
|
231772b2ad | ||
|
|
77ab5e9689 | ||
|
|
c2c9e8989c | ||
|
|
63922f2676 | ||
|
|
b273505c28 | ||
|
|
abc6c31890 | ||
|
|
9867bc947e | ||
|
|
e20a53afbb | ||
|
|
ceb142c9bd | ||
|
|
322085f90e | ||
|
|
250dece2ab | ||
|
|
a33cc0635a | ||
|
|
ca8c2e07ec | ||
|
|
bc915f5dc8 | ||
|
|
e5cf30e1e9 |
@@ -12,7 +12,7 @@ insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Trim traling whitespaces
|
||||
# Trim trailing whitespaces
|
||||
[*.{h,cpp,tcc,cmake,yml}]
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
|
||||
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
@@ -16,18 +16,20 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ubuntu
|
||||
run: sudo apt install -y libcppunit-dev zlib1g-dev
|
||||
run: sudo apt install -y libcppunit-dev libutfcpp-dev zlib1g-dev
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Set up macOS
|
||||
run: brew install cppunit
|
||||
run: |
|
||||
brew update
|
||||
brew install cppunit utf8cpp
|
||||
if: matrix.os == 'macos-latest'
|
||||
|
||||
- name: Set up Windows
|
||||
run: vcpkg install cppunit --triplet x64-windows
|
||||
run: vcpkg install cppunit utfcpp zlib --triplet x64-windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Configure
|
||||
@@ -49,6 +51,9 @@ jobs:
|
||||
- name: Test Windows
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: |
|
||||
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\cppunit_x64-windows\bin;$PWD\taglib\Release"
|
||||
$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'
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "3rdparty/utfcpp"]
|
||||
path = 3rdparty/utfcpp
|
||||
url = https://github.com/nemtrif/utfcpp.git
|
||||
327
3rdparty/utf8-cpp/checked.h
vendored
327
3rdparty/utf8-cpp/checked.h
vendored
@@ -1,327 +0,0 @@
|
||||
// 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
332
3rdparty/utf8-cpp/core.h
vendored
@@ -1,332 +0,0 @@
|
||||
// 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
Submodule
1
3rdparty/utfcpp
vendored
Submodule
Submodule 3rdparty/utfcpp added at df857efc5b
@@ -1,3 +1,123 @@
|
||||
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)
|
||||
===========================
|
||||
|
||||
@@ -220,7 +340,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 londer delete the previous handler,
|
||||
* ID3v1::Tag::setStringHandler will no longer delete the previous handler,
|
||||
the caller is responsible for this.
|
||||
* File objects will also no longer delete the passed IOStream objects. It
|
||||
should be done in the caller code after the File object is no longer
|
||||
@@ -363,7 +483,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/playcount) ID3v2 frame.
|
||||
* Added support for the POPM (rating/play count) ID3v2 frame.
|
||||
* Generic RIFF file format support:
|
||||
* Support for AIFF files with ID3v2 tags.
|
||||
* Support for WAV files with ID3v2 tags.
|
||||
@@ -1,12 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
|
||||
|
||||
project(taglib)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
include(CTest)
|
||||
include(FeatureSummary)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
if(APPLE)
|
||||
@@ -42,6 +44,9 @@ 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/")
|
||||
|
||||
@@ -59,11 +64,6 @@ if(MSVC)
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||
endforeach(flag_var)
|
||||
endif()
|
||||
# Disable warnings for internal invocations of API functions
|
||||
# that have been marked with TAGLIB_DEPRECATED
|
||||
# https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4996
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4996")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4996")
|
||||
endif()
|
||||
|
||||
# Read version information from file taglib/toolkit/taglib.h into variables
|
||||
@@ -88,17 +88,12 @@ else()
|
||||
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
|
||||
endif()
|
||||
|
||||
# 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 20)
|
||||
set(TAGLIB_SOVERSION_REVISION 1)
|
||||
set(TAGLIB_SOVERSION_AGE 19)
|
||||
|
||||
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}")
|
||||
# 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)
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
|
||||
@@ -106,37 +101,43 @@ include(ConfigureChecks.cmake)
|
||||
option(WITH_ZLIB "Build with ZLIB" ON)
|
||||
|
||||
if(WITH_ZLIB)
|
||||
if(NOT ZLIB_SOURCE)
|
||||
find_package(ZLIB)
|
||||
if(ZLIB_FOUND)
|
||||
set(HAVE_ZLIB 1)
|
||||
set(ZLIB_LIBRARIES_FLAGS -lz)
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
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()
|
||||
|
||||
if(NOT HAVE_ZLIB AND ZLIB_SOURCE)
|
||||
set(HAVE_ZLIB 1)
|
||||
set(HAVE_ZLIB_SOURCE 1)
|
||||
endif()
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
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}")
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmd.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd")
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config.cmd")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_FRAMEWORK)
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
else()
|
||||
set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR})
|
||||
else()
|
||||
set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}.pc")
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
@@ -149,6 +150,21 @@ 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)
|
||||
|
||||
@@ -34,53 +34,6 @@ if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
# Determine which kind of atomic operations your compiler supports.
|
||||
|
||||
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()
|
||||
|
||||
# Determine which kind of byte swap functions your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
@@ -105,7 +58,7 @@ if(NOT HAVE_GCC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GLIBC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
int main() {
|
||||
_byteswap_ushort(0);
|
||||
_byteswap_ulong(0);
|
||||
@@ -140,32 +93,6 @@ 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("
|
||||
@@ -178,5 +105,5 @@ check_cxx_source_compiles("
|
||||
|
||||
# Detect WinRT mode
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(PLATFORM WINRT 1)
|
||||
set(PLATFORM_WINRT 1)
|
||||
endif()
|
||||
|
||||
@@ -122,7 +122,8 @@ INPUT = @CMAKE_SOURCE_DIR@/taglib
|
||||
INPUT_ENCODING = UTF-8
|
||||
FILE_PATTERNS = *.h \
|
||||
*.hh \
|
||||
*.H
|
||||
*.H \
|
||||
*.dox
|
||||
RECURSIVE = YES
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
|
||||
530
INSTALL.md
530
INSTALL.md
@@ -1,5 +1,4 @@
|
||||
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.
|
||||
@@ -19,16 +18,190 @@ If you want to build TagLib without ZLib, you can use
|
||||
make
|
||||
sudo make install
|
||||
|
||||
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
|
||||
running CMake.
|
||||
See [cmake(1)](https://cmake.org/cmake/help/latest/manual/cmake.1.html) for
|
||||
generic help on running CMake.
|
||||
|
||||
Mac OS X
|
||||
--------
|
||||
## Build Options
|
||||
|
||||
On Mac OS X, you might want to build a framework that can be easily integrated
|
||||
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
|
||||
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 Mac OS X 10.10 as the deployment target:
|
||||
a framework with macOS 10.10 as the deployment target:
|
||||
|
||||
mkdir build; cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
||||
@@ -51,178 +224,217 @@ For a 10.10 static library, use:
|
||||
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
|
||||
|
||||
It's Windows ... Systems vary!
|
||||
This means you need to adjust things to suit your system, especially paths.
|
||||
### Using Visual Studio Build Tools
|
||||
|
||||
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
|
||||
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`.
|
||||
|
||||
Requirements:
|
||||
* Tool chain, build environment, whatever ya want to call it ...
|
||||
Installed and working.
|
||||
* CMake program. (Available at: www.cmake.org)
|
||||
Installed and working.
|
||||
#### Building TagLib (MSVC)
|
||||
|
||||
Optional:
|
||||
* Zlib library.
|
||||
Available in some tool chains, not all.
|
||||
Search the web, take your choice.
|
||||
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`.
|
||||
|
||||
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.
|
||||
Now you can build TagLib from PowerShell.
|
||||
|
||||
| option | description |
|
||||
| ----------------------- | ----------- |
|
||||
| `WITH_ZLIB=` | Whether to build with ZLib |
|
||||
| `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`.
|
||||
# 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
|
||||
|
||||
**Note**: As Visual Studio 2017 supports CMake, you can skip this step and open the taglib
|
||||
folder in VS instead.
|
||||
* For MinGW: `MinGW Makefiles`
|
||||
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
|
||||
|
||||
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
|
||||
|
||||
Or use the CMake GUI:
|
||||
1. Open CMake GUI.
|
||||
2. Set paths: *Where is the source code* and *Where to build the binaries*.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
# Install to \pkg_static folder on current drive
|
||||
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg_static --strip
|
||||
```
|
||||
|
||||
Including `ENABLE_STATIC_RUNTIME=ON` indicates you want TagLib built using the
|
||||
static runtime library, rather than the DLL form of the runtime.
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
#### Building a Project Using TagLib (MSVC)
|
||||
|
||||
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_TESTING=ON` when running cmake.
|
||||
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.
|
||||
|
||||
The test suite has a custom target in the build system, so you can run
|
||||
the tests using make:
|
||||
```
|
||||
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)
|
||||
```
|
||||
|
||||
make check
|
||||
To build into a folder build inside this directory, just run
|
||||
|
||||
Windows MinGW:
|
||||
```
|
||||
# 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
|
||||
|
||||
* Get cppunit from https://www.freedesktop.org/wiki/Software/cppunit/
|
||||
* Build it for MinGW:
|
||||
- `./autogen.sh`
|
||||
- `./configure --disable-shared`
|
||||
- `mingw32-make; mingw32-make install DESTDIR=/path/to/install/dir`
|
||||
* Build TagLib with testing enabled:
|
||||
- ```
|
||||
cmake -G "MinGW Makefiles" -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=OFF \
|
||||
-DCPPUNIT_INCLUDE_DIR=/path/to/cppunit/include \
|
||||
-DCPPUNIT_LIBRARIES=/path/to/cppunit/lib/libcppunit.a \
|
||||
-DCPPUNIT_INSTALLED_VERSION=1.15.1
|
||||
```
|
||||
- `mingw32-make`
|
||||
- `mingw32-make check`
|
||||
# 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
|
||||
```
|
||||
|
||||
Windows MSVS:
|
||||
To use the C-bindings with tagreader_c.c, you can change the last two lines in
|
||||
CMakeLists.txt to
|
||||
|
||||
* Get cppunit from https://www.freedesktop.org/wiki/Software/cppunit/
|
||||
* Build it in Visual Studio:
|
||||
- Open examples/examples2008.sln, convert all, do not overwrite.
|
||||
- Set architecture to x64, build configuration, e.g. Debug DLL.
|
||||
- It may fail, but the needed libraries should be available in src\cppunit\DebugDll.
|
||||
* Build TagLib with testing enabled:
|
||||
- ```
|
||||
cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON
|
||||
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
|
||||
-DCPPUNIT_INCLUDE_DIR=\path\to\cppunit\include
|
||||
-DCPPUNIT_LIBRARIES=\path\to\cppunit\DebugDll\cppunitd_dll.lib
|
||||
-DCPPUNIT_INSTALLED_VERSION=1.15.1
|
||||
```
|
||||
- `msbuild all_build.vcxproj /p:Configuration=Debug`
|
||||
- `path %path%;\path\to\cppunit\DebugDll`
|
||||
- `tests\Debug\test_runner.exe`
|
||||
```
|
||||
add_executable(tagreader_c tagreader_c.c)
|
||||
target_link_libraries(tagreader_c TagLib::tag_c)
|
||||
```
|
||||
|
||||
macOS:
|
||||
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
|
||||
|
||||
* Install cppunit using a package manager, e.g. `brew install cppunit`
|
||||
* Build TagLib with testing enabled:
|
||||
- `cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=OFF`
|
||||
- `make`
|
||||
- `make check`
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
https://taglib.org/
|
||||
|
||||
TagLib is a library for reading and editing the metadata of several
|
||||
popular audio formats. Currently it supports both ID3v1 and [ID3v2][]
|
||||
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.
|
||||
|
||||
@@ -1 +1 @@
|
||||
add_subdirectory(c)
|
||||
add_subdirectory(c)
|
||||
|
||||
@@ -14,20 +14,34 @@ 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_link_libraries(tag_c tag)
|
||||
target_include_directories(tag_c INTERFACE
|
||||
$<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}>
|
||||
)
|
||||
|
||||
target_link_libraries(tag_c PRIVATE tag)
|
||||
set_target_properties(tag_c PROPERTIES
|
||||
PUBLIC_HEADER "${tag_c_HDRS}"
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden
|
||||
)
|
||||
set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
endif()
|
||||
if(BUILD_FRAMEWORK)
|
||||
set_target_properties(tag_c PROPERTIES FRAMEWORK TRUE)
|
||||
@@ -45,7 +59,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)
|
||||
@@ -55,21 +69,42 @@ if(HAVE_CRUN_LIB)
|
||||
endif()
|
||||
|
||||
set_target_properties(tag_c PROPERTIES
|
||||
VERSION 0.0.0
|
||||
SOVERSION 0
|
||||
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
|
||||
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
|
||||
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
)
|
||||
|
||||
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
|
||||
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
|
||||
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}
|
||||
)
|
||||
|
||||
if(NOT BUILD_FRAMEWORK)
|
||||
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)
|
||||
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)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -19,29 +19,46 @@
|
||||
* USA *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
#include <asffile.h>
|
||||
#include <vorbisfile.h>
|
||||
#include <mpegfile.h>
|
||||
#include <flacfile.h>
|
||||
#include <oggflacfile.h>
|
||||
#include <mpcfile.h>
|
||||
#include <wavpackfile.h>
|
||||
#include <speexfile.h>
|
||||
#include <trueaudiofile.h>
|
||||
#include <mp4file.h>
|
||||
#include <tag.h>
|
||||
#include <string.h>
|
||||
#include <id3v2framefactory.h>
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#ifdef HAVE_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"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
@@ -87,67 +104,130 @@ void taglib_free(void* pointer)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TagLib::File wrapper
|
||||
// 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 *taglib_file_new(const char *filename)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(FileRef::create(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
|
||||
}
|
||||
|
||||
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
{
|
||||
File *file = NULL;
|
||||
switch(type) {
|
||||
case TagLib_File_MPEG:
|
||||
return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
|
||||
file = new MPEG::File(filename);
|
||||
break;
|
||||
case TagLib_File_OggVorbis:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Vorbis::File(filename));
|
||||
file = new Ogg::Vorbis::File(filename);
|
||||
break;
|
||||
case TagLib_File_FLAC:
|
||||
return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
|
||||
file = new FLAC::File(filename);
|
||||
break;
|
||||
case TagLib_File_MPC:
|
||||
return reinterpret_cast<TagLib_File *>(new MPC::File(filename));
|
||||
file = new MPC::File(filename);
|
||||
break;
|
||||
case TagLib_File_OggFlac:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::FLAC::File(filename));
|
||||
file = new Ogg::FLAC::File(filename);
|
||||
break;
|
||||
case TagLib_File_WavPack:
|
||||
return reinterpret_cast<TagLib_File *>(new WavPack::File(filename));
|
||||
file = new WavPack::File(filename);
|
||||
break;
|
||||
case TagLib_File_Speex:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
|
||||
file = new Ogg::Speex::File(filename);
|
||||
break;
|
||||
case TagLib_File_TrueAudio:
|
||||
return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
|
||||
file = new TrueAudio::File(filename);
|
||||
break;
|
||||
case TagLib_File_MP4:
|
||||
return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
|
||||
file = new MP4::File(filename);
|
||||
break;
|
||||
case TagLib_File_ASF:
|
||||
return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
|
||||
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;
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
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)
|
||||
{
|
||||
delete reinterpret_cast<File *>(file);
|
||||
delete reinterpret_cast<FileRef *>(file);
|
||||
}
|
||||
|
||||
BOOL taglib_file_is_valid(const TagLib_File *file)
|
||||
{
|
||||
return reinterpret_cast<const File *>(file)->isValid();
|
||||
return !reinterpret_cast<const FileRef *>(file)->isNull();
|
||||
}
|
||||
|
||||
TagLib_Tag *taglib_file_tag(const TagLib_File *file)
|
||||
{
|
||||
const File *f = reinterpret_cast<const File *>(file);
|
||||
auto f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<TagLib_Tag *>(f->tag());
|
||||
}
|
||||
|
||||
const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
|
||||
{
|
||||
const File *f = reinterpret_cast<const File *>(file);
|
||||
auto f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
|
||||
}
|
||||
|
||||
BOOL taglib_file_save(TagLib_File *file)
|
||||
{
|
||||
return reinterpret_cast<File *>(file)->save();
|
||||
return reinterpret_cast<FileRef *>(file)->save();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -258,8 +338,8 @@ void taglib_tag_free_strings()
|
||||
if(!stringManagementEnabled)
|
||||
return;
|
||||
|
||||
for(List<char *>::ConstIterator it = strings.begin(); it != strings.end(); ++it)
|
||||
free(*it);
|
||||
for(auto &string : std::as_const(strings))
|
||||
free(string);
|
||||
strings.clear();
|
||||
}
|
||||
|
||||
@@ -269,25 +349,25 @@ void taglib_tag_free_strings()
|
||||
|
||||
int taglib_audioproperties_length(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->length();
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->lengthInSeconds();
|
||||
}
|
||||
|
||||
int taglib_audioproperties_bitrate(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->bitrate();
|
||||
}
|
||||
|
||||
int taglib_audioproperties_samplerate(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->sampleRate();
|
||||
}
|
||||
|
||||
int taglib_audioproperties_channels(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->channels();
|
||||
}
|
||||
|
||||
@@ -313,3 +393,423 @@ 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,7 +43,10 @@ extern "C" {
|
||||
#define TAGLIB_C_EXPORT
|
||||
#endif
|
||||
|
||||
#ifndef BOOL
|
||||
#ifdef _MSC_VER
|
||||
/* minwindef.h contains typedef int BOOL */
|
||||
#include <windows.h>
|
||||
#elif !defined BOOL
|
||||
#define BOOL int
|
||||
#endif
|
||||
|
||||
@@ -57,14 +60,15 @@ extern "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++
|
||||
* 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++
|
||||
* types in the implementation.
|
||||
*/
|
||||
|
||||
typedef struct { int dummy; } TagLib_File;
|
||||
typedef struct { int dummy; } TagLib_Tag;
|
||||
typedef struct { int dummy; } TagLib_AudioProperties;
|
||||
typedef struct { int dummy; } TagLib_IOStream;
|
||||
|
||||
/*!
|
||||
* By default all strings coming into or out of TagLib's C API are in UTF8.
|
||||
@@ -86,6 +90,22 @@ 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
|
||||
******************************************************************************/
|
||||
@@ -100,7 +120,17 @@ typedef enum {
|
||||
TagLib_File_Speex,
|
||||
TagLib_File_TrueAudio,
|
||||
TagLib_File_MP4,
|
||||
TagLib_File_ASF
|
||||
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_Type;
|
||||
|
||||
/*!
|
||||
@@ -118,6 +148,16 @@ 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.
|
||||
*/
|
||||
@@ -292,6 +332,275 @@ 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,11 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
libdir=@CMAKE_PC_LIBDIR@
|
||||
includedir=@CMAKE_PC_INCLUDEDIR@
|
||||
|
||||
Name: TagLib C Bindings
|
||||
Description: Audio meta-data library (C bindings)
|
||||
Requires: taglib
|
||||
Version: @TAGLIB_LIB_VERSION_STRING@
|
||||
Libs: -L${libdir} -ltag_c
|
||||
Cflags: -I${includedir}/taglib
|
||||
Libs: -L${libdir} -ltag_c@TAGLIB_INSTALL_SUFFIX@
|
||||
Cflags: -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@
|
||||
|
||||
@@ -10,16 +10,6 @@
|
||||
#cmakedefine HAVE_MAC_BYTESWAP 1
|
||||
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
|
||||
|
||||
/* Defined if your compiler supports some atomic operations */
|
||||
#cmakedefine HAVE_GCC_ATOMIC 1
|
||||
#cmakedefine HAVE_MAC_ATOMIC 1
|
||||
#cmakedefine HAVE_WIN_ATOMIC 1
|
||||
#cmakedefine HAVE_IA64_ATOMIC 1
|
||||
|
||||
/* Defined if your compiler supports some safer version of vsprintf */
|
||||
#cmakedefine HAVE_VSNPRINTF 1
|
||||
#cmakedefine HAVE_VSPRINTF_S 1
|
||||
|
||||
/* Defined if your compiler supports ISO _strdup */
|
||||
#cmakedefine HAVE_ISO_STRDUP 1
|
||||
|
||||
|
||||
@@ -23,20 +23,16 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
|
||||
#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 "commentsframe.h"
|
||||
#include "id3v1tag.h"
|
||||
#include "apetag.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
@@ -65,11 +61,11 @@ int main(int argc, char *argv[])
|
||||
<< " bytes in tag"
|
||||
<< endl;
|
||||
|
||||
ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
|
||||
for(; it != id3v2tag->frameList().end(); it++) {
|
||||
const auto &frames = id3v2tag->frameList();
|
||||
for(auto it = frames.begin(); it != frames.end(); it++) {
|
||||
cout << (*it)->frameID();
|
||||
|
||||
if(ID3v2::CommentsFrame *comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
|
||||
if(auto comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
|
||||
if(!comment->description().isEmpty())
|
||||
cout << " [" << comment->description() << "]";
|
||||
|
||||
@@ -100,8 +96,8 @@ int main(int argc, char *argv[])
|
||||
cout << endl << "APE" << endl;
|
||||
|
||||
if(ape) {
|
||||
for(APE::ItemListMap::ConstIterator it = ape->itemListMap().begin();
|
||||
it != ape->itemListMap().end(); ++it)
|
||||
const auto &items = ape->itemListMap();
|
||||
for(auto it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
if((*it).second.type() != APE::Item::Binary)
|
||||
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <mpegfile.h>
|
||||
#include <tstring.h>
|
||||
|
||||
#include "tstring.h"
|
||||
#include "mpegfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
|
||||
@@ -24,11 +24,13 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include <fileref.h>
|
||||
#include <tag.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "tpropertymap.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tvariant.h"
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -53,36 +55,66 @@ int main(int argc, char *argv[])
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
|
||||
TagLib::PropertyMap tags = f.file()->properties();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int longest = 0;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
if (i->first.size() > longest) {
|
||||
longest = i->first.size();
|
||||
cout << "-- TAG (properties) --" << endl;
|
||||
for(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!f.isNull() && f.audioProperties()) {
|
||||
|
||||
TagLib::AudioProperties *properties = f.audioProperties();
|
||||
|
||||
int seconds = properties->length() % 60;
|
||||
int minutes = (properties->length() - seconds) / 60;
|
||||
int seconds = properties->lengthInSeconds() % 60;
|
||||
int minutes = (properties->lengthInSeconds() - 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) << seconds << endl;
|
||||
cout << "length - " << minutes << ":" << setfill('0') << setw(2) << right << seconds << endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <tag_c.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
@@ -32,15 +34,16 @@
|
||||
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(FALSE);
|
||||
taglib_set_strings_unicode(1);
|
||||
|
||||
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]);
|
||||
@@ -50,21 +53,115 @@ 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 --\n");
|
||||
printf("-- TAG (basic) --\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 - \"%i\"\n", taglib_tag_year(tag));
|
||||
printf("year - \"%u\"\n", taglib_tag_year(tag));
|
||||
printf("comment - \"%s\"\n", taglib_tag_comment(tag));
|
||||
printf("track - \"%i\"\n", taglib_tag_track(tag));
|
||||
printf("track - \"%u\"\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) {
|
||||
seconds = taglib_audioproperties_length(properties) % 60;
|
||||
minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
|
||||
int seconds = taglib_audioproperties_length(properties) % 60;
|
||||
int minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
|
||||
|
||||
printf("-- AUDIO --\n");
|
||||
printf("bitrate - %i\n", taglib_audioproperties_bitrate(properties));
|
||||
@@ -73,6 +170,7 @@ 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);
|
||||
}
|
||||
|
||||
@@ -24,18 +24,20 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <tlist.h>
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
#include <tag.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "tlist.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tvariant.h"
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -70,6 +72,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 << endl;
|
||||
|
||||
exit(1);
|
||||
@@ -79,14 +82,14 @@ void checkForRejectedProperties(const TagLib::PropertyMap &tags)
|
||||
{ // stolen from tagreader.cpp
|
||||
if(tags.size() > 0) {
|
||||
unsigned int longest = 0;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(auto i = tags.begin(); i != tags.end(); ++i) {
|
||||
if(i->first.size() > longest) {
|
||||
longest = i->first.size();
|
||||
}
|
||||
}
|
||||
cout << "-- rejected TAGs (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) {
|
||||
for(auto i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(auto j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
|
||||
}
|
||||
}
|
||||
@@ -110,17 +113,18 @@ int main(int argc, char *argv[])
|
||||
if(fileList.isEmpty())
|
||||
usage();
|
||||
|
||||
for(int i = 1; i < argc - 1; i += 2) {
|
||||
int i = 1;
|
||||
while(i < argc - 1) {
|
||||
|
||||
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>::ConstIterator it;
|
||||
for(it = fileList.begin(); it != fileList.end(); ++it) {
|
||||
for(auto &f : fileList) {
|
||||
|
||||
TagLib::Tag *t = (*it).tag();
|
||||
TagLib::Tag *t = f.tag();
|
||||
|
||||
switch (field) {
|
||||
case 't':
|
||||
@@ -147,24 +151,61 @@ int main(int argc, char *argv[])
|
||||
case 'R':
|
||||
case 'I':
|
||||
if(i + 2 < argc) {
|
||||
TagLib::PropertyMap map = (*it).file()->properties ();
|
||||
TagLib::PropertyMap map = f.properties();
|
||||
if(field == 'R') {
|
||||
map.replace(value, TagLib::String(argv[i + 2]));
|
||||
}
|
||||
else {
|
||||
map.insert(value, TagLib::String(argv[i + 2]));
|
||||
}
|
||||
++i;
|
||||
checkForRejectedProperties((*it).file()->setProperties(map));
|
||||
numArgsConsumed = 3;
|
||||
checkForRejectedProperties(f.setProperties(map));
|
||||
}
|
||||
else {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case 'D': {
|
||||
TagLib::PropertyMap map = (*it).file()->properties();
|
||||
TagLib::PropertyMap map = f.properties();
|
||||
map.erase(value);
|
||||
checkForRejectedProperties((*it).file()->setProperties(map));
|
||||
checkForRejectedProperties(f.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}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
// empty value, remove pictures
|
||||
f.setComplexProperties("PICTURE", {});
|
||||
}
|
||||
}
|
||||
else {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -172,14 +213,14 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
}
|
||||
i += numArgsConsumed;
|
||||
}
|
||||
else
|
||||
usage();
|
||||
}
|
||||
|
||||
TagLib::List<TagLib::FileRef>::ConstIterator it;
|
||||
for(it = fileList.begin(); it != fileList.end(); ++it)
|
||||
(*it).file()->save();
|
||||
for(auto &f : fileList)
|
||||
f.save();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
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.
|
||||
@@ -39,22 +39,22 @@ while test $# -gt 0
|
||||
do
|
||||
case $1 in
|
||||
--libs)
|
||||
flags="$flags -L$libdir -ltag @ZLIB_LIBRARIES_FLAGS@"
|
||||
;;
|
||||
flags="$flags -L$libdir -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@"
|
||||
;;
|
||||
--cflags)
|
||||
flags="$flags -I$includedir -I$includedir/taglib"
|
||||
;;
|
||||
flags="$flags -I$includedir -I$includedir/taglib@TAGLIB_INSTALL_SUFFIX@"
|
||||
;;
|
||||
--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:-@CMAKE_INSTALL_PREFIX@}
|
||||
;;
|
||||
*)
|
||||
echo "$0: unknown option $1"
|
||||
echo
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
12
taglib-config.cmake.in
Normal file
12
taglib-config.cmake.in
Normal file
@@ -0,0 +1,12 @@
|
||||
@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()
|
||||
@@ -14,11 +14,11 @@ 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]
|
||||
echo usage: %0 [OPTIONS]
|
||||
echo [--libs]
|
||||
echo [--cflags]
|
||||
echo [--version]
|
||||
echo [--prefix]
|
||||
goto theend
|
||||
|
||||
*
|
||||
@@ -27,10 +27,9 @@ goto theend
|
||||
* 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
|
||||
if /i "%1#" == "--cflags#" echo -I${CMAKE_INSTALL_FULL_INCLUDEDIR} -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib
|
||||
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
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
libdir=@CMAKE_PC_LIBDIR@
|
||||
includedir=@CMAKE_PC_INCLUDEDIR@
|
||||
|
||||
Name: TagLib
|
||||
Description: Audio meta-data library
|
||||
Requires:
|
||||
Version: @TAGLIB_LIB_VERSION_STRING@
|
||||
Libs: -L${libdir} -ltag @ZLIB_LIBRARIES_FLAGS@
|
||||
Cflags: -I${includedir} -I${includedir}/taglib
|
||||
Libs: -L${libdir} -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@
|
||||
Cflags: -I${includedir} -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@
|
||||
|
||||
@@ -24,15 +24,10 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
${taglib_SOURCE_DIR}/3rdparty
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
elseif(HAVE_ZLIB_SOURCE)
|
||||
include_directories(${ZLIB_SOURCE})
|
||||
endif()
|
||||
|
||||
set(tag_HDRS
|
||||
tag.h
|
||||
fileref.h
|
||||
@@ -46,15 +41,17 @@ set(tag_HDRS
|
||||
toolkit/tstringlist.h
|
||||
toolkit/tbytevector.h
|
||||
toolkit/tbytevectorlist.h
|
||||
toolkit/tvariant.h
|
||||
toolkit/tbytevectorstream.h
|
||||
toolkit/tiostream.h
|
||||
toolkit/tfile.h
|
||||
toolkit/tfilestream.h
|
||||
toolkit/tmap.h
|
||||
toolkit/tmap.tcc
|
||||
toolkit/tpicturetype.h
|
||||
toolkit/tpropertymap.h
|
||||
toolkit/trefcounter.h
|
||||
toolkit/tdebuglistener.h
|
||||
toolkit/tversionnumber.h
|
||||
mpeg/mpegfile.h
|
||||
mpeg/mpegproperties.h
|
||||
mpeg/mpegheader.h
|
||||
@@ -129,6 +126,7 @@ set(tag_HDRS
|
||||
mp4/mp4item.h
|
||||
mp4/mp4properties.h
|
||||
mp4/mp4coverart.h
|
||||
mp4/mp4itemfactory.h
|
||||
mod/modfilebase.h
|
||||
mod/modfile.h
|
||||
mod/modtag.h
|
||||
@@ -139,6 +137,11 @@ set(tag_HDRS
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
dsf/dsffile.h
|
||||
dsf/dsfproperties.h
|
||||
dsdiff/dsdifffile.h
|
||||
dsdiff/dsdiffproperties.h
|
||||
dsdiff/dsdiffdiintag.h
|
||||
)
|
||||
|
||||
set(mpeg_SRCS
|
||||
@@ -219,6 +222,7 @@ set(mp4_SRCS
|
||||
mp4/mp4item.cpp
|
||||
mp4/mp4properties.cpp
|
||||
mp4/mp4coverart.cpp
|
||||
mp4/mp4itemfactory.cpp
|
||||
)
|
||||
|
||||
set(ape_SRCS
|
||||
@@ -294,39 +298,41 @@ set(xm_SRCS
|
||||
xm/xmproperties.cpp
|
||||
)
|
||||
|
||||
set(dsf_SRCS
|
||||
dsf/dsffile.cpp
|
||||
dsf/dsfproperties.cpp
|
||||
)
|
||||
|
||||
set(dsdiff_SRCS
|
||||
dsdiff/dsdifffile.cpp
|
||||
dsdiff/dsdiffproperties.cpp
|
||||
dsdiff/dsdiffdiintag.cpp
|
||||
)
|
||||
|
||||
set(toolkit_SRCS
|
||||
toolkit/tstring.cpp
|
||||
toolkit/tstringlist.cpp
|
||||
toolkit/tbytevector.cpp
|
||||
toolkit/tbytevectorlist.cpp
|
||||
toolkit/tvariant.cpp
|
||||
toolkit/tbytevectorstream.cpp
|
||||
toolkit/tiostream.cpp
|
||||
toolkit/tfile.cpp
|
||||
toolkit/tfilestream.cpp
|
||||
toolkit/tdebug.cpp
|
||||
toolkit/tpicturetype.cpp
|
||||
toolkit/tpropertymap.cpp
|
||||
toolkit/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}
|
||||
${zlib_SRCS}
|
||||
${dsf_SRCS} ${dsdiff_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
@@ -335,27 +341,33 @@ set(tag_LIB_SRCS
|
||||
)
|
||||
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
|
||||
set_property(TARGET tag PROPERTY CXX_STANDARD 98)
|
||||
|
||||
if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
|
||||
target_link_libraries(tag ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
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>
|
||||
)
|
||||
|
||||
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}
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
||||
LINK_INTERFACE_LIBRARIES ""
|
||||
INTERFACE_LINK_LIBRARIES "${ZLIB_INTERFACE_LINK_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)
|
||||
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"
|
||||
@@ -363,10 +375,43 @@ 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
|
||||
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}
|
||||
)
|
||||
|
||||
@@ -31,16 +31,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#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 "apefile.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "id3v1tag.h"
|
||||
#include "id3v2header.h"
|
||||
#include "tagunion.h"
|
||||
#include "tagutils.h"
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
|
||||
@@ -54,33 +52,18 @@ namespace
|
||||
class APE::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
APELocation(-1),
|
||||
APESize(0),
|
||||
ID3v1Location(-1),
|
||||
ID3v2Header(0),
|
||||
ID3v2Location(-1),
|
||||
ID3v2Size(0),
|
||||
properties(0) {}
|
||||
offset_t APELocation { -1 };
|
||||
long APESize { 0 };
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete ID3v2Header;
|
||||
delete properties;
|
||||
}
|
||||
offset_t ID3v1Location { -1 };
|
||||
|
||||
long APELocation;
|
||||
long APESize;
|
||||
|
||||
long ID3v1Location;
|
||||
|
||||
ID3v2::Header *ID3v2Header;
|
||||
long ID3v2Location;
|
||||
long ID3v2Size;
|
||||
std::unique_ptr<ID3v2::Header> ID3v2Header;
|
||||
offset_t ID3v2Location { -1 };
|
||||
long ID3v2Size { 0 };
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
Properties *properties;
|
||||
std::unique_ptr<Properties> properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -101,7 +84,7 @@ bool APE::File::isSupported(IOStream *stream)
|
||||
|
||||
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -109,16 +92,13 @@ APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
|
||||
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
APE::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::File::~File() = default;
|
||||
|
||||
TagLib::Tag *APE::File::tag() const
|
||||
{
|
||||
@@ -145,7 +125,7 @@ PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
||||
|
||||
APE::Properties *APE::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool APE::File::save()
|
||||
@@ -233,10 +213,10 @@ APE::Tag *APE::File::APETag(bool create)
|
||||
void APE::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v1)
|
||||
d->tag.set(ApeID3v1Index, 0);
|
||||
d->tag.set(ApeID3v1Index, nullptr);
|
||||
|
||||
if(tags & APE)
|
||||
d->tag.set(ApeAPEIndex, 0);
|
||||
d->tag.set(ApeAPEIndex, nullptr);
|
||||
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
@@ -264,7 +244,7 @@ void APE::File::read(bool readProperties)
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
seek(d->ID3v2Location);
|
||||
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
|
||||
d->ID3v2Header = std::make_unique<ID3v2::Header>(readBlock(ID3v2::Header::size()));
|
||||
d->ID3v2Size = d->ID3v2Header->completeTagSize();
|
||||
}
|
||||
|
||||
@@ -292,7 +272,7 @@ void APE::File::read(bool readProperties)
|
||||
|
||||
if(readProperties) {
|
||||
|
||||
long streamLength;
|
||||
offset_t streamLength;
|
||||
|
||||
if(d->APELocation >= 0)
|
||||
streamLength = d->APELocation;
|
||||
@@ -309,6 +289,6 @@ void APE::File::read(bool readProperties)
|
||||
seek(0);
|
||||
}
|
||||
|
||||
d->properties = new Properties(this, streamLength);
|
||||
d->properties = std::make_unique<Properties>(this, streamLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,39 +107,42 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
|
||||
* or a combination of the two.
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
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;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
|
||||
* tag will be updated as well.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the APE::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Saves the file.
|
||||
@@ -147,7 +150,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.
|
||||
*/
|
||||
virtual bool save();
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
@@ -221,13 +224,11 @@ namespace TagLib {
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -24,37 +24,25 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "apefooter.h"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace APE;
|
||||
|
||||
class APE::Footer::FooterPrivate
|
||||
{
|
||||
public:
|
||||
FooterPrivate() :
|
||||
version(0),
|
||||
footerPresent(true),
|
||||
headerPresent(false),
|
||||
isHeader(false),
|
||||
itemCount(0),
|
||||
tagSize(0) {}
|
||||
unsigned int version { 0 };
|
||||
|
||||
unsigned int version;
|
||||
bool footerPresent { true };
|
||||
bool headerPresent { false };
|
||||
|
||||
bool footerPresent;
|
||||
bool headerPresent;
|
||||
bool isHeader { false };
|
||||
|
||||
bool isHeader;
|
||||
|
||||
unsigned int itemCount;
|
||||
unsigned int tagSize;
|
||||
unsigned int itemCount { 0 };
|
||||
unsigned int tagSize { 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -76,20 +64,17 @@ ByteVector APE::Footer::fileIdentifier()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Footer::Footer() :
|
||||
d(new FooterPrivate())
|
||||
d(std::make_unique<FooterPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
APE::Footer::Footer(const ByteVector &data) :
|
||||
d(new FooterPrivate())
|
||||
d(std::make_unique<FooterPrivate>())
|
||||
{
|
||||
parse(data);
|
||||
}
|
||||
|
||||
APE::Footer::~Footer()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::Footer::~Footer() = default;
|
||||
|
||||
unsigned int APE::Footer::version() const
|
||||
{
|
||||
@@ -222,7 +207,7 @@ ByteVector APE::Footer::render(bool isHeader) const
|
||||
flags[30] = false; // footer is always present
|
||||
flags[29] = isHeader;
|
||||
|
||||
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
|
||||
v.append(ByteVector::fromUInt(static_cast<unsigned int>(flags.to_ulong()), false));
|
||||
|
||||
// add the reserved 64bit
|
||||
|
||||
|
||||
@@ -61,6 +61,9 @@ namespace TagLib {
|
||||
*/
|
||||
virtual ~Footer();
|
||||
|
||||
Footer(const Footer &) = delete;
|
||||
Footer &operator=(const Footer &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the version number. (Note: This is the 1000 or 2000.)
|
||||
*/
|
||||
@@ -160,11 +163,9 @@ namespace TagLib {
|
||||
ByteVector render(bool isHeader) const;
|
||||
|
||||
private:
|
||||
Footer(const Footer &);
|
||||
Footer &operator=(const Footer &);
|
||||
|
||||
class FooterPrivate;
|
||||
FooterPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FooterPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace APE
|
||||
|
||||
@@ -23,26 +23,24 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "apeitem.h"
|
||||
|
||||
#include <utility>
|
||||
#include <numeric>
|
||||
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace APE;
|
||||
|
||||
class APE::Item::ItemPrivate
|
||||
{
|
||||
public:
|
||||
ItemPrivate() :
|
||||
type(Text),
|
||||
readOnly(false) {}
|
||||
|
||||
Item::ItemTypes type;
|
||||
Item::ItemTypes type { Text };
|
||||
String key;
|
||||
ByteVector value;
|
||||
StringList text;
|
||||
bool readOnly;
|
||||
bool readOnly { false };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -50,26 +48,19 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Item::Item() :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_unique<ItemPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const String &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->key = key;
|
||||
d->text.append(value);
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const StringList &values) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_unique<ItemPrivate>())
|
||||
{
|
||||
d->key = key;
|
||||
d->text = values;
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_unique<ItemPrivate>())
|
||||
{
|
||||
d->key = key;
|
||||
if(binary) {
|
||||
@@ -82,14 +73,11 @@ APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
|
||||
}
|
||||
|
||||
APE::Item::Item(const Item &item) :
|
||||
d(new ItemPrivate(*item.d))
|
||||
d(std::make_unique<ItemPrivate>(*item.d))
|
||||
{
|
||||
}
|
||||
|
||||
APE::Item::~Item()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::Item::~Item() = default;
|
||||
|
||||
Item &APE::Item::operator=(const Item &item)
|
||||
{
|
||||
@@ -97,7 +85,7 @@ Item &APE::Item::operator=(const Item &item)
|
||||
return *this;
|
||||
}
|
||||
|
||||
void APE::Item::swap(Item &item)
|
||||
void APE::Item::swap(Item &item) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
@@ -141,14 +129,6 @@ void APE::Item::setBinaryData(const ByteVector &value)
|
||||
d->text.clear();
|
||||
}
|
||||
|
||||
ByteVector APE::Item::value() const
|
||||
{
|
||||
// This seems incorrect as it won't be actually rendering the value to keep it
|
||||
// up to date.
|
||||
|
||||
return d->value;
|
||||
}
|
||||
|
||||
void APE::Item::setKey(const String &key)
|
||||
{
|
||||
d->key = key;
|
||||
@@ -188,12 +168,10 @@ int APE::Item::size() const
|
||||
switch(d->type) {
|
||||
case Text:
|
||||
if(!d->text.isEmpty()) {
|
||||
StringList::ConstIterator it = d->text.begin();
|
||||
|
||||
result += it->data(String::UTF8).size();
|
||||
it++;
|
||||
for(; it != d->text.end(); ++it)
|
||||
result += 1 + it->data(String::UTF8).size();
|
||||
result = std::accumulate(d->text.cbegin(), d->text.cend(), result,
|
||||
[](int sz, const String &t) {
|
||||
return sz + 1 + t.data(String::UTF8).size();
|
||||
}) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -205,11 +183,6 @@ int APE::Item::size() const
|
||||
return result;
|
||||
}
|
||||
|
||||
StringList APE::Item::toStringList() const
|
||||
{
|
||||
return d->text;
|
||||
}
|
||||
|
||||
StringList APE::Item::values() const
|
||||
{
|
||||
return d->text;
|
||||
@@ -254,45 +227,44 @@ void APE::Item::parse(const ByteVector &data)
|
||||
|
||||
d->key = String(&data[8], String::Latin1);
|
||||
|
||||
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
|
||||
const ByteVector val = data.mid(8 + d->key.size() + 1, valueLength);
|
||||
|
||||
setReadOnly(flags & 1);
|
||||
setType(static_cast<ItemTypes>((flags >> 1) & 3));
|
||||
|
||||
if(Text == d->type)
|
||||
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
|
||||
d->text = StringList(ByteVectorList::split(val, '\0'), String::UTF8);
|
||||
else
|
||||
d->value = value;
|
||||
d->value = val;
|
||||
}
|
||||
|
||||
ByteVector APE::Item::render() const
|
||||
{
|
||||
ByteVector data;
|
||||
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
|
||||
ByteVector value;
|
||||
ByteVector val;
|
||||
|
||||
if(isEmpty())
|
||||
return data;
|
||||
|
||||
if(d->type == Text) {
|
||||
StringList::ConstIterator it = d->text.begin();
|
||||
auto it = d->text.cbegin();
|
||||
|
||||
value.append(it->data(String::UTF8));
|
||||
it++;
|
||||
for(; it != d->text.end(); ++it) {
|
||||
value.append('\0');
|
||||
value.append(it->data(String::UTF8));
|
||||
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));
|
||||
}
|
||||
d->value = value;
|
||||
d->value = val;
|
||||
}
|
||||
else
|
||||
value.append(d->value);
|
||||
val.append(d->value);
|
||||
|
||||
data.append(ByteVector::fromUInt(value.size(), false));
|
||||
data.append(ByteVector::fromUInt(val.size(), false));
|
||||
data.append(ByteVector::fromUInt(flags, false));
|
||||
data.append(d->key.data(String::Latin1));
|
||||
data.append(ByteVector('\0'));
|
||||
data.append(value);
|
||||
data.append(val);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -56,12 +56,6 @@ namespace TagLib {
|
||||
*/
|
||||
Item();
|
||||
|
||||
/*!
|
||||
* Constructs a text item with \a key and \a value.
|
||||
*/
|
||||
// BIC: Remove this, StringList has a constructor from a single string
|
||||
Item(const String &key, const String &value);
|
||||
|
||||
/*!
|
||||
* Constructs a text item with \a key and \a values.
|
||||
*/
|
||||
@@ -91,7 +85,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Exchanges the content of this item by the content of \a item.
|
||||
*/
|
||||
void swap(Item &item);
|
||||
void swap(Item &item) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns the key.
|
||||
@@ -110,11 +104,6 @@ namespace TagLib {
|
||||
*/
|
||||
void setBinaryData(const ByteVector &value);
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* Remove in next binary incompatible release */
|
||||
ByteVector value() const;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Sets the key for the item to \a key.
|
||||
*/
|
||||
@@ -131,7 +120,7 @@ namespace TagLib {
|
||||
* Sets the text value of the item to the list of values in \a value and clears
|
||||
* any previous contents.
|
||||
*
|
||||
* \see toStringList()
|
||||
* \see values()
|
||||
*/
|
||||
void setValues(const StringList &values);
|
||||
|
||||
@@ -145,7 +134,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Appends \a values to extend the current list of text values.
|
||||
*
|
||||
* \see toStringList()
|
||||
* \see values()
|
||||
*/
|
||||
void appendValues(const StringList &values);
|
||||
|
||||
@@ -161,11 +150,6 @@ namespace TagLib {
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* Remove in next binary incompatible release */
|
||||
StringList toStringList() const;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Returns the list of text values. If the data type is not \a Text, always
|
||||
* returns an empty StringList.
|
||||
@@ -193,11 +177,11 @@ namespace TagLib {
|
||||
bool isReadOnly() const;
|
||||
|
||||
/*!
|
||||
* Sets the type of the item to \a type.
|
||||
* Sets the type of the item to \a val.
|
||||
*
|
||||
* \see ItemTypes
|
||||
*/
|
||||
void setType(ItemTypes type);
|
||||
void setType(ItemTypes val);
|
||||
|
||||
/*!
|
||||
* Returns the type of the item.
|
||||
@@ -211,7 +195,8 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class ItemPrivate;
|
||||
ItemPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ItemPrivate> d;
|
||||
};
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -27,70 +27,39 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <bitset>
|
||||
#include "id3v2tag.h"
|
||||
#include "apeproperties.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "apefile.h"
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
#include "apetag.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class APE::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
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;
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int version { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
unsigned int sampleFrames { 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Properties::Properties(File *, ReadStyle style) :
|
||||
APE::Properties::Properties(File *file, offset_t streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
debug("APE::Properties::Properties() -- This constructor is no longer used.");
|
||||
}
|
||||
|
||||
APE::Properties::Properties(File *file, long streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
read(file, streamLength);
|
||||
}
|
||||
|
||||
APE::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int APE::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int APE::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
APE::Properties::~Properties() = default;
|
||||
|
||||
int APE::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
@@ -142,25 +111,25 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void APE::Properties::read(File *file, long streamLength)
|
||||
void APE::Properties::read(File *file, offset_t streamLength)
|
||||
{
|
||||
// First, we assume that the file pointer is set at the first descriptor.
|
||||
long offset = file->tell();
|
||||
int version = headerVersion(file->readBlock(6));
|
||||
offset_t offset = file->tell();
|
||||
int vers = headerVersion(file->readBlock(6));
|
||||
|
||||
// Next, we look for the descriptor.
|
||||
if(version < 0) {
|
||||
if(vers < 0) {
|
||||
offset = file->find("MAC ", offset);
|
||||
file->seek(offset);
|
||||
version = headerVersion(file->readBlock(6));
|
||||
vers = headerVersion(file->readBlock(6));
|
||||
}
|
||||
|
||||
if(version < 0) {
|
||||
if(vers < 0) {
|
||||
debug("APE::Properties::read() -- APE descriptor not found");
|
||||
return;
|
||||
}
|
||||
|
||||
d->version = version;
|
||||
d->version = vers;
|
||||
|
||||
if(d->version >= 3980)
|
||||
analyzeCurrent(file);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#ifndef TAGLIB_APEPROPERTIES_H
|
||||
#define TAGLIB_APEPROPERTIES_H
|
||||
|
||||
#include "taglib.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
@@ -52,63 +53,38 @@ namespace TagLib {
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
* APE::File \a file.
|
||||
*
|
||||
* \deprecated Use Properties(File *, long, ReadStyle).
|
||||
*/
|
||||
TAGLIB_DEPRECATED Properties(File *file, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
* APE::File \a file.
|
||||
*/
|
||||
Properties(File *file, long streamLength, ReadStyle style = Average);
|
||||
Properties(File *file, offset_t streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this APE::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated Use lengthInSeconds().
|
||||
*/
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
@@ -126,16 +102,14 @@ namespace TagLib {
|
||||
int version() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read(File *file, long streamLength);
|
||||
void read(File *file, offset_t streamLength);
|
||||
|
||||
void analyzeCurrent(File *file);
|
||||
void analyzeOld(File *file);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -31,14 +31,14 @@
|
||||
#define WANT_CLASS_INSTANTIATION_OF_MAP (1)
|
||||
#endif
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tstring.h>
|
||||
#include <tmap.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,37 +50,26 @@ 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)
|
||||
{
|
||||
const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 };
|
||||
static constexpr std::array invalidKeys { "ID3", "TAG", "OGGS", "MP+" };
|
||||
|
||||
// only allow printable ASCII including space (32..126)
|
||||
|
||||
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;
|
||||
return std::none_of(key.begin(), key.end(),
|
||||
[](unsigned char c) { return c < 32 || c > 126; })
|
||||
&& std::none_of(invalidKeys.begin(), invalidKeys.end(),
|
||||
[upperKey = String(key).upper()](auto k) { return upperKey == k; });
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class APE::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate() :
|
||||
file(0),
|
||||
footerLocation(0) {}
|
||||
|
||||
File *file;
|
||||
long footerLocation;
|
||||
File *file { nullptr };
|
||||
offset_t footerLocation { 0 };
|
||||
|
||||
Footer footer;
|
||||
ItemListMap itemListMap;
|
||||
@@ -91,12 +80,12 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Tag::Tag() :
|
||||
d(new TagPrivate())
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
|
||||
d(new TagPrivate())
|
||||
APE::Tag::Tag(TagLib::File *file, offset_t footerLocation) :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
d->file = file;
|
||||
d->footerLocation = footerLocation;
|
||||
@@ -104,10 +93,7 @@ APE::Tag::Tag(TagLib::File *file, long footerLocation) :
|
||||
read();
|
||||
}
|
||||
|
||||
APE::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::Tag::~Tag() = default;
|
||||
|
||||
ByteVector APE::Tag::fileIdentifier()
|
||||
{
|
||||
@@ -116,44 +102,44 @@ ByteVector APE::Tag::fileIdentifier()
|
||||
|
||||
String APE::Tag::title() const
|
||||
{
|
||||
Item value = d->itemListMap.value("TITLE");
|
||||
return value.isEmpty() ? String() : value.values().toString();
|
||||
Item val = d->itemListMap.value("TITLE");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
String APE::Tag::artist() const
|
||||
{
|
||||
Item value = d->itemListMap.value("ARTIST");
|
||||
return value.isEmpty() ? String() : value.values().toString();
|
||||
Item val = d->itemListMap.value("ARTIST");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
String APE::Tag::album() const
|
||||
{
|
||||
Item value = d->itemListMap.value("ALBUM");
|
||||
return value.isEmpty() ? String() : value.values().toString();
|
||||
Item val = d->itemListMap.value("ALBUM");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
String APE::Tag::comment() const
|
||||
{
|
||||
Item value = d->itemListMap.value("COMMENT");
|
||||
return value.isEmpty() ? String() : value.values().toString();
|
||||
Item val = d->itemListMap.value("COMMENT");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
String APE::Tag::genre() const
|
||||
{
|
||||
Item value = d->itemListMap.value("GENRE");
|
||||
return value.isEmpty() ? String() : value.values().toString();
|
||||
Item val = d->itemListMap.value("GENRE");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
unsigned int APE::Tag::year() const
|
||||
{
|
||||
Item value = d->itemListMap.value("YEAR");
|
||||
return value.isEmpty() ? 0 : value.toString().toInt();
|
||||
Item val = d->itemListMap.value("YEAR");
|
||||
return val.isEmpty() ? 0 : val.toString().toInt();
|
||||
}
|
||||
|
||||
unsigned int APE::Tag::track() const
|
||||
{
|
||||
Item value = d->itemListMap.value("TRACK");
|
||||
return value.isEmpty() ? 0 : value.toString().toInt();
|
||||
Item val = d->itemListMap.value("TRACK");
|
||||
return val.isEmpty() ? 0 : val.toString().toInt();
|
||||
}
|
||||
|
||||
void APE::Tag::setTitle(const String &s)
|
||||
@@ -202,36 +188,34 @@ namespace
|
||||
// conversions of tag keys between what we use in PropertyMap and what's usual
|
||||
// for APE tags
|
||||
// usual, APE
|
||||
const std::pair<const char *, const char *> keyConversions[] = {
|
||||
std::make_pair("TRACKNUMBER", "TRACK"),
|
||||
std::make_pair("DATE", "YEAR"),
|
||||
std::make_pair("ALBUMARTIST", "ALBUM ARTIST"),
|
||||
std::make_pair("DISCNUMBER", "DISC"),
|
||||
std::make_pair("REMIXER", "MIXARTIST"),
|
||||
std::make_pair("RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS"),
|
||||
std::make_pair("RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE"),
|
||||
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"),
|
||||
};
|
||||
const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]);
|
||||
} // namespace
|
||||
|
||||
PropertyMap APE::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
ItemListMap::ConstIterator it = itemListMap().begin();
|
||||
for(; it != itemListMap().end(); ++it) {
|
||||
String tagName = it->first.upper();
|
||||
for(const auto &[tag, item] : std::as_const(itemListMap())) {
|
||||
String tagName = tag.upper();
|
||||
// if the item is Binary or Locator, or if the key is an invalid string,
|
||||
// add to unsupportedData
|
||||
if(it->second.type() != Item::Text || tagName.isEmpty()) {
|
||||
properties.unsupportedData().append(it->first);
|
||||
if(item.type() != Item::Text || tagName.isEmpty()) {
|
||||
properties.addUnsupportedData(tag);
|
||||
}
|
||||
else {
|
||||
// Some tags need to be handled specially
|
||||
for(size_t i = 0; i < keyConversionsSize; ++i) {
|
||||
if(tagName == keyConversions[i].second)
|
||||
tagName = keyConversions[i].first;
|
||||
for(const auto &[k, t] : keyConversions) {
|
||||
if(tagName == t)
|
||||
tagName = k;
|
||||
}
|
||||
properties[tagName].append(it->second.toStringList());
|
||||
properties[tagName].append(item.values());
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
@@ -239,57 +223,141 @@ PropertyMap APE::Tag::properties() const
|
||||
|
||||
void APE::Tag::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
StringList::ConstIterator it = properties.begin();
|
||||
for(; it != properties.end(); ++it)
|
||||
removeItem(*it);
|
||||
for(const auto &property : properties)
|
||||
removeItem(property);
|
||||
}
|
||||
|
||||
PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap properties(origProps); // make a local copy that can be modified
|
||||
PropertyMap props(origProps); // make a local copy that can be modified
|
||||
|
||||
// see comment in properties()
|
||||
for(size_t i = 0; i < keyConversionsSize; ++i)
|
||||
if(properties.contains(keyConversions[i].first)) {
|
||||
properties.insert(keyConversions[i].second, properties[keyConversions[i].first]);
|
||||
properties.erase(keyConversions[i].first);
|
||||
for(const auto &[k, t] : keyConversions)
|
||||
if(props.contains(k)) {
|
||||
props.insert(t, props[k]);
|
||||
props.erase(k);
|
||||
}
|
||||
|
||||
// first check if tags need to be removed completely
|
||||
StringList toRemove;
|
||||
ItemListMap::ConstIterator remIt = itemListMap().begin();
|
||||
for(; remIt != itemListMap().end(); ++remIt) {
|
||||
String key = remIt->first.upper();
|
||||
for(const auto &[k, t] : std::as_const(itemListMap())) {
|
||||
String key = k.upper();
|
||||
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
|
||||
if(!key.isEmpty() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
|
||||
toRemove.append(remIt->first);
|
||||
if(!key.isEmpty() && t.type() == APE::Item::Text && !props.contains(key))
|
||||
toRemove.append(k);
|
||||
}
|
||||
|
||||
for(StringList::ConstIterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
|
||||
removeItem(*removeIt);
|
||||
for(const auto &item : std::as_const(toRemove))
|
||||
removeItem(item);
|
||||
|
||||
// now sync in the "forward direction"
|
||||
PropertyMap::ConstIterator it = properties.begin();
|
||||
PropertyMap invalid;
|
||||
for(; it != properties.end(); ++it) {
|
||||
const String &tagName = it->first;
|
||||
for(const auto &[tagName, val] : std::as_const(props)) {
|
||||
if(!checkKey(tagName))
|
||||
invalid.insert(it->first, it->second);
|
||||
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
||||
if(it->second.isEmpty())
|
||||
invalid.insert(tagName, val);
|
||||
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == val)) {
|
||||
if(val.isEmpty())
|
||||
removeItem(tagName);
|
||||
else {
|
||||
StringList::ConstIterator valueIt = it->second.begin();
|
||||
addValue(tagName, *valueIt, true);
|
||||
++valueIt;
|
||||
for(; valueIt != it->second.end(); ++valueIt)
|
||||
addValue(tagName, *valueIt, false);
|
||||
addValue(tagName, *val.begin(), true);
|
||||
for(auto it = std::next(val.begin()); it != val.end(); ++it)
|
||||
addValue(tagName, *it, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
|
||||
StringList APE::Tag::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys;
|
||||
if(d->itemListMap.contains(FRONT_COVER) ||
|
||||
d->itemListMap.contains(BACK_COVER)) {
|
||||
keys.append("PICTURE");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> APE::Tag::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props;
|
||||
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)
|
||||
@@ -324,7 +392,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.
|
||||
|
||||
ItemListMap::Iterator it = d->itemListMap.find(key.upper());
|
||||
auto it = d->itemListMap.find(key.upper());
|
||||
|
||||
if(it != d->itemListMap.end() && it->second.type() == Item::Text)
|
||||
it->second.appendValue(value);
|
||||
@@ -382,8 +450,8 @@ ByteVector APE::Tag::render() const
|
||||
ByteVector data;
|
||||
unsigned int itemCount = 0;
|
||||
|
||||
for(ItemListMap::ConstIterator it = d->itemListMap.begin(); it != d->itemListMap.end(); ++it) {
|
||||
data.append(it->second.render());
|
||||
for(const auto &[_, list] : std::as_const(d->itemListMap)) {
|
||||
data.append(list.render());
|
||||
itemCount++;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,11 @@
|
||||
#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"
|
||||
|
||||
namespace TagLib {
|
||||
@@ -49,8 +48,7 @@ namespace TagLib {
|
||||
*
|
||||
* \see APE::Tag::itemListMap()
|
||||
*/
|
||||
typedef Map<const String, Item> ItemListMap;
|
||||
|
||||
using ItemListMap = Map<const String, Item>;
|
||||
|
||||
//! An APE tag implementation
|
||||
|
||||
@@ -66,12 +64,15 @@ namespace TagLib {
|
||||
* Create an APE tag and parse the data in \a file with APE footer at
|
||||
* \a tagOffset.
|
||||
*/
|
||||
Tag(TagLib::File *file, long footerLocation);
|
||||
Tag(TagLib::File *file, offset_t footerLocation);
|
||||
|
||||
/*!
|
||||
* Destroys this Tag instance.
|
||||
*/
|
||||
virtual ~Tag();
|
||||
~Tag() override;
|
||||
|
||||
Tag(const Tag &) = delete;
|
||||
Tag &operator=(const Tag &) = delete;
|
||||
|
||||
/*!
|
||||
* Renders the in memory values to a ByteVector suitable for writing to
|
||||
@@ -87,21 +88,21 @@ namespace TagLib {
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
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;
|
||||
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 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);
|
||||
void setTitle(const String &s) override;
|
||||
void setArtist(const String &s) override;
|
||||
void setAlbum(const String &s) override;
|
||||
void setComment(const String &s) override;
|
||||
void setGenre(const String &s) override;
|
||||
void setYear(unsigned int i) override;
|
||||
void setTrack(unsigned int i) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- export function.
|
||||
@@ -117,9 +118,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;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function. The same
|
||||
@@ -127,7 +128,11 @@ 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 &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
StringList complexPropertyKeys() const override;
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
|
||||
/*!
|
||||
* Check if the given String is a valid APE tag key.
|
||||
@@ -181,7 +186,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns true if the tag does not contain any data.
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
bool isEmpty() const override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -196,11 +201,9 @@ namespace TagLib {
|
||||
void parse(const ByteVector &data);
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
};
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,31 +23,29 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <trefcounter.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::Attribute::AttributePrivate : public RefCounter
|
||||
class ASF::Attribute::AttributePrivate
|
||||
{
|
||||
public:
|
||||
AttributePrivate() :
|
||||
pictureValue(ASF::Picture::fromInvalid()),
|
||||
numericValue(0),
|
||||
stream(0),
|
||||
language(0) {}
|
||||
pictureValue(ASF::Picture::fromInvalid())
|
||||
{
|
||||
}
|
||||
AttributeTypes type;
|
||||
String stringValue;
|
||||
ByteVector byteVectorValue;
|
||||
ASF::Picture pictureValue;
|
||||
unsigned long long numericValue;
|
||||
int stream;
|
||||
int language;
|
||||
unsigned long long numericValue { 0 };
|
||||
int stream { 0 };
|
||||
int language { 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -55,84 +53,72 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Attribute::Attribute() :
|
||||
d(new AttributePrivate())
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d->type = UnicodeType;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ASF::Attribute &other) :
|
||||
d(other.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
ASF::Attribute::Attribute(const ASF::Attribute &) = default;
|
||||
|
||||
ASF::Attribute::Attribute(const String &value) :
|
||||
d(new AttributePrivate())
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d->type = UnicodeType;
|
||||
d->stringValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ByteVector &value) :
|
||||
d(new AttributePrivate())
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d->type = BytesType;
|
||||
d->byteVectorValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ASF::Picture &value) :
|
||||
d(new AttributePrivate())
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d->type = BytesType;
|
||||
d->pictureValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned int value) :
|
||||
d(new AttributePrivate())
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d->type = DWordType;
|
||||
d->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned long long value) :
|
||||
d(new AttributePrivate())
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d->type = QWordType;
|
||||
d->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned short value) :
|
||||
d(new AttributePrivate())
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d->type = WordType;
|
||||
d->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(bool value) :
|
||||
d(new AttributePrivate())
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d->type = BoolType;
|
||||
d->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
|
||||
{
|
||||
Attribute(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &) = default;
|
||||
|
||||
void ASF::Attribute::swap(Attribute &other)
|
||||
void ASF::Attribute::swap(Attribute &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
ASF::Attribute::~Attribute()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
}
|
||||
ASF::Attribute::~Attribute() = default;
|
||||
|
||||
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
|
||||
{
|
||||
@@ -262,8 +248,10 @@ int ASF::Attribute::dataSize() const
|
||||
case UnicodeType:
|
||||
return d->stringValue.size() * 2 + 2;
|
||||
case BytesType:
|
||||
if(d->pictureValue.isValid())
|
||||
if(d->pictureValue.isValid()) {
|
||||
return d->pictureValue.dataSize();
|
||||
}
|
||||
return d->byteVectorValue.size();
|
||||
case GuidType:
|
||||
return d->byteVectorValue.size();
|
||||
}
|
||||
@@ -303,8 +291,11 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
case BytesType:
|
||||
if(d->pictureValue.isValid()) {
|
||||
data.append(d->pictureValue.render());
|
||||
break;
|
||||
}
|
||||
else {
|
||||
data.append(d->byteVectorValue);
|
||||
}
|
||||
break;
|
||||
case GuidType:
|
||||
data.append(d->byteVectorValue);
|
||||
break;
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace TagLib
|
||||
/*!
|
||||
* Construct an attribute as a copy of \a other.
|
||||
*/
|
||||
Attribute(const Attribute &item);
|
||||
Attribute(const Attribute &other);
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this item.
|
||||
@@ -116,7 +116,7 @@ namespace TagLib
|
||||
/*!
|
||||
* Exchanges the content of the Attribute by the content of \a other.
|
||||
*/
|
||||
void swap(Attribute &other);
|
||||
void swap(Attribute &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys the attribute.
|
||||
@@ -197,7 +197,8 @@ namespace TagLib
|
||||
ByteVector render(const String &name, int kind = 0) const;
|
||||
|
||||
class AttributePrivate;
|
||||
AttributePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<AttributePrivate> d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,13 +23,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
#include <tagutils.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"
|
||||
@@ -50,37 +51,28 @@ public:
|
||||
class MetadataObject;
|
||||
class MetadataLibraryObject;
|
||||
|
||||
FilePrivate():
|
||||
headerSize(0),
|
||||
tag(0),
|
||||
properties(0),
|
||||
contentDescriptionObject(0),
|
||||
extendedContentDescriptionObject(0),
|
||||
headerExtensionObject(0),
|
||||
metadataObject(0),
|
||||
metadataLibraryObject(0)
|
||||
FilePrivate()
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete tag;
|
||||
delete properties;
|
||||
}
|
||||
~FilePrivate() = default;
|
||||
|
||||
unsigned long long headerSize;
|
||||
FilePrivate(const FilePrivate &) = delete;
|
||||
FilePrivate &operator=(const FilePrivate &) = delete;
|
||||
|
||||
ASF::Tag *tag;
|
||||
ASF::Properties *properties;
|
||||
unsigned long long headerSize { 0 };
|
||||
|
||||
std::unique_ptr<ASF::Tag> tag;
|
||||
std::unique_ptr<ASF::Properties> properties;
|
||||
|
||||
List<BaseObject *> objects;
|
||||
|
||||
ContentDescriptionObject *contentDescriptionObject;
|
||||
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
|
||||
HeaderExtensionObject *headerExtensionObject;
|
||||
MetadataObject *metadataObject;
|
||||
MetadataLibraryObject *metadataLibraryObject;
|
||||
ContentDescriptionObject *contentDescriptionObject { nullptr };
|
||||
ExtendedContentDescriptionObject *extendedContentDescriptionObject { nullptr };
|
||||
HeaderExtensionObject *headerExtensionObject { nullptr };
|
||||
MetadataObject *metadataObject { nullptr };
|
||||
MetadataLibraryObject *metadataLibraryObject { nullptr };
|
||||
};
|
||||
|
||||
namespace
|
||||
@@ -103,9 +95,9 @@ class ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector data;
|
||||
virtual ~BaseObject() {}
|
||||
virtual ~BaseObject() = default;
|
||||
virtual ByteVector guid() const = 0;
|
||||
virtual void parse(ASF::File *file, unsigned int size);
|
||||
virtual void parse(ASF::File *file, long long size);
|
||||
virtual ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
@@ -114,56 +106,56 @@ class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::Bas
|
||||
ByteVector myGuid;
|
||||
public:
|
||||
UnknownObject(const ByteVector &guid);
|
||||
ByteVector guid() const;
|
||||
ByteVector guid() const override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
|
||||
@@ -171,16 +163,16 @@ class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePriv
|
||||
public:
|
||||
List<ASF::File::FilePrivate::BaseObject *> objects;
|
||||
HeaderExtensionObject();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
|
||||
private:
|
||||
enum CodecType
|
||||
@@ -191,10 +183,10 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
|
||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
data.clear();
|
||||
if(size > 24 && size <= static_cast<unsigned int>(file->length()))
|
||||
if(size > 24 && size <= file->length())
|
||||
data = file->readBlock(size - 24);
|
||||
else
|
||||
data = ByteVector();
|
||||
@@ -219,7 +211,7 @@ ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
|
||||
return filePropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size)
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() < 64) {
|
||||
@@ -237,7 +229,7 @@ ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
|
||||
return streamPropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size)
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() < 70) {
|
||||
@@ -257,7 +249,7 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
||||
return contentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
const int titleLength = readWORD(file);
|
||||
const int artistLength = readWORD(file);
|
||||
@@ -297,7 +289,7 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() cons
|
||||
return extendedContentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
@@ -320,7 +312,7 @@ ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
|
||||
return metadataGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
@@ -343,7 +335,7 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
|
||||
return metadataLibraryGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
@@ -371,14 +363,14 @@ ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
||||
return headerExtensionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->seek(18, File::Current);
|
||||
long long dataSize = readDWORD(file);
|
||||
long long dataPos = 0;
|
||||
while(dataPos < dataSize) {
|
||||
ByteVector guid = file->readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
ByteVector uid = file->readBlock(16);
|
||||
if(uid.size() != 16) {
|
||||
file->setValid(false);
|
||||
break;
|
||||
}
|
||||
@@ -389,18 +381,18 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
|
||||
break;
|
||||
}
|
||||
BaseObject *obj;
|
||||
if(guid == metadataGuid) {
|
||||
if(uid == metadataGuid) {
|
||||
file->d->metadataObject = new MetadataObject();
|
||||
obj = file->d->metadataObject;
|
||||
}
|
||||
else if(guid == metadataLibraryGuid) {
|
||||
else if(uid == metadataLibraryGuid) {
|
||||
file->d->metadataLibraryObject = new MetadataLibraryObject();
|
||||
obj = file->d->metadataLibraryObject;
|
||||
}
|
||||
else {
|
||||
obj = new UnknownObject(guid);
|
||||
obj = new UnknownObject(uid);
|
||||
}
|
||||
obj->parse(file, static_cast<unsigned int>(size));
|
||||
obj->parse(file, size);
|
||||
objects.append(obj);
|
||||
dataPos += size;
|
||||
}
|
||||
@@ -409,8 +401,8 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
|
||||
data.append((*it)->render(file));
|
||||
for(const auto &object : std::as_const(objects)) {
|
||||
data.append(object->render(file));
|
||||
}
|
||||
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
|
||||
return BaseObject::render(file);
|
||||
@@ -421,7 +413,7 @@ ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
|
||||
return codecListGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size)
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() <= 20) {
|
||||
@@ -439,7 +431,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
|
||||
if(pos >= data.size())
|
||||
break;
|
||||
|
||||
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
|
||||
const auto type = static_cast<CodecType>(data.toUShort(pos, false));
|
||||
pos += 2;
|
||||
|
||||
int nameLength = data.toUShort(pos, false);
|
||||
@@ -489,7 +481,7 @@ bool ASF::File::isSupported(IOStream *stream)
|
||||
|
||||
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(isOpen())
|
||||
read();
|
||||
@@ -497,20 +489,17 @@ ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
|
||||
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(isOpen())
|
||||
read();
|
||||
}
|
||||
|
||||
ASF::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
ASF::File::~File() = default;
|
||||
|
||||
ASF::Tag *ASF::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::properties() const
|
||||
@@ -530,7 +519,7 @@ PropertyMap ASF::File::setProperties(const PropertyMap &properties)
|
||||
|
||||
ASF::Properties *ASF::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool ASF::File::save()
|
||||
@@ -570,19 +559,11 @@ bool ASF::File::save()
|
||||
d->metadataObject->attributeData.clear();
|
||||
d->metadataLibraryObject->attributeData.clear();
|
||||
|
||||
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;
|
||||
|
||||
for(const auto &[name, attributes] : std::as_const(d->tag->attributeListMap())) {
|
||||
bool inExtendedContentDescriptionObject = false;
|
||||
bool inMetadataObject = false;
|
||||
|
||||
for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) {
|
||||
|
||||
const Attribute &attribute = *jt;
|
||||
for(const auto &attribute : attributes) {
|
||||
const bool largeValue = (attribute.dataSize() > 65535);
|
||||
const bool guid = (attribute.type() == Attribute::GuidType);
|
||||
|
||||
@@ -601,8 +582,8 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
ByteVector data;
|
||||
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
|
||||
data.append((*it)->render(this));
|
||||
for(const auto &object : std::as_const(d->objects)) {
|
||||
data.append(object->render(this));
|
||||
}
|
||||
|
||||
seek(16);
|
||||
@@ -632,8 +613,8 @@ void ASF::File::read()
|
||||
return;
|
||||
}
|
||||
|
||||
d->tag = new ASF::Tag();
|
||||
d->properties = new ASF::Properties();
|
||||
d->tag = std::make_unique<ASF::Tag>();
|
||||
d->properties = std::make_unique<ASF::Properties>();
|
||||
|
||||
bool ok;
|
||||
d->headerSize = readQWORD(this, &ok);
|
||||
@@ -648,15 +629,15 @@ void ASF::File::read()
|
||||
}
|
||||
seek(2, Current);
|
||||
|
||||
FilePrivate::FilePropertiesObject *filePropertiesObject = 0;
|
||||
FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0;
|
||||
FilePrivate::FilePropertiesObject *filePropertiesObject = nullptr;
|
||||
FilePrivate::StreamPropertiesObject *streamPropertiesObject = nullptr;
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
const ByteVector guid = readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
long size = static_cast<long>(readQWORD(this, &ok));
|
||||
auto size = readQWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
break;
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
#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"
|
||||
|
||||
@@ -71,7 +71,10 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ASF tag of the file.
|
||||
@@ -83,35 +86,35 @@ namespace TagLib {
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*/
|
||||
virtual Tag *tag() const;
|
||||
Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the ASF audio properties for this file.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*/
|
||||
virtual bool save();
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an ASF
|
||||
@@ -126,7 +129,8 @@ namespace TagLib {
|
||||
void read();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,18 +23,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <trefcounter.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
#include "asffile.h"
|
||||
#include "asfpicture.h"
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::Picture::PicturePrivate : public RefCounter
|
||||
class ASF::Picture::PicturePrivate
|
||||
{
|
||||
public:
|
||||
bool valid;
|
||||
@@ -49,22 +45,13 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Picture::Picture() :
|
||||
d(new PicturePrivate())
|
||||
d(std::make_shared<PicturePrivate>())
|
||||
{
|
||||
d->valid = true;
|
||||
}
|
||||
|
||||
ASF::Picture::Picture(const Picture& other) :
|
||||
d(other.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
ASF::Picture::~Picture()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
}
|
||||
ASF::Picture::Picture(const Picture &) = default;
|
||||
ASF::Picture::~Picture() = default;
|
||||
|
||||
bool ASF::Picture::isValid() const
|
||||
{
|
||||
@@ -118,13 +105,9 @@ int ASF::Picture::dataSize() const
|
||||
d->picture.size();
|
||||
}
|
||||
|
||||
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
|
||||
{
|
||||
Picture(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
ASF::Picture &ASF::Picture::operator=(const ASF::Picture &) = default;
|
||||
|
||||
void ASF::Picture::swap(Picture &other)
|
||||
void ASF::Picture::swap(Picture &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tpicturetype.h"
|
||||
#include "taglib_export.h"
|
||||
#include "attachedpictureframe.h"
|
||||
|
||||
@@ -52,50 +53,7 @@ namespace TagLib
|
||||
/*!
|
||||
* This describes the function or content of the picture.
|
||||
*/
|
||||
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
|
||||
};
|
||||
DECLARE_PICTURE_TYPE_ENUM(Type)
|
||||
|
||||
/*!
|
||||
* Constructs an empty picture.
|
||||
@@ -120,7 +78,7 @@ namespace TagLib
|
||||
/*!
|
||||
* Exchanges the content of the Picture by the content of \a other.
|
||||
*/
|
||||
void swap(Picture &other);
|
||||
void swap(Picture &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns true if Picture stores valid picture
|
||||
@@ -214,7 +172,8 @@ namespace TagLib
|
||||
|
||||
private:
|
||||
class PicturePrivate;
|
||||
PicturePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<PicturePrivate> d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "asfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -32,24 +30,15 @@ using namespace TagLib;
|
||||
class ASF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
bitsPerSample(0),
|
||||
codec(ASF::Properties::Unknown),
|
||||
encrypted(false) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int bitsPerSample;
|
||||
ASF::Properties::Codec codec;
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
ASF::Properties::Codec codec { ASF::Properties::Unknown };
|
||||
String codecName;
|
||||
String codecDescription;
|
||||
bool encrypted;
|
||||
bool encrypted { false };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -58,24 +47,11 @@ public:
|
||||
|
||||
ASF::Properties::Properties() :
|
||||
AudioProperties(AudioProperties::Average),
|
||||
d(new PropertiesPrivate())
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
ASF::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int ASF::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int ASF::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
ASF::Properties::~Properties() = default;
|
||||
|
||||
int ASF::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
@@ -126,11 +102,6 @@ bool ASF::Properties::isEncrypted() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ASF::Properties::setLength(int /*length*/)
|
||||
{
|
||||
debug("ASF::Properties::setLength() -- This method is deprecated. Do not use.");
|
||||
}
|
||||
|
||||
void ASF::Properties::setLengthInMilliseconds(int value)
|
||||
{
|
||||
d->length = value;
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
#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 {
|
||||
@@ -76,49 +76,32 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this ASF::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated Use lengthInSeconds().
|
||||
*/
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
@@ -158,9 +141,6 @@ namespace TagLib {
|
||||
bool isEncrypted() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
// deprecated
|
||||
void setLength(int value);
|
||||
|
||||
void setLengthInMilliseconds(int value);
|
||||
void setBitrate(int value);
|
||||
void setSampleRate(int value);
|
||||
@@ -174,7 +154,8 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,11 +23,29 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#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:
|
||||
@@ -40,14 +58,11 @@ public:
|
||||
};
|
||||
|
||||
ASF::Tag::Tag() :
|
||||
d(new TagPrivate())
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
ASF::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
ASF::Tag::~Tag() = default;
|
||||
|
||||
String ASF::Tag::title() const
|
||||
{
|
||||
@@ -62,7 +77,8 @@ String ASF::Tag::artist() const
|
||||
String ASF::Tag::album() const
|
||||
{
|
||||
if(d->attributeListMap.contains("WM/AlbumTitle"))
|
||||
return d->attributeListMap["WM/AlbumTitle"][0].toString();
|
||||
return joinTagValues(
|
||||
attributeListToStringList(d->attributeListMap.value("WM/AlbumTitle")));
|
||||
return String();
|
||||
}
|
||||
|
||||
@@ -104,7 +120,8 @@ unsigned int ASF::Tag::track() const
|
||||
String ASF::Tag::genre() const
|
||||
{
|
||||
if(d->attributeListMap.contains("WM/Genre"))
|
||||
return d->attributeListMap["WM/Genre"][0].toString();
|
||||
return joinTagValues(
|
||||
attributeListToStringList(d->attributeListMap.value("WM/Genre")));
|
||||
return String();
|
||||
}
|
||||
|
||||
@@ -180,9 +197,9 @@ ASF::AttributeList ASF::Tag::attribute(const String &name) const
|
||||
|
||||
void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
|
||||
{
|
||||
AttributeList value;
|
||||
value.append(attribute);
|
||||
d->attributeListMap.insert(name, value);
|
||||
AttributeList val;
|
||||
val.append(attribute);
|
||||
d->attributeListMap.insert(name, val);
|
||||
}
|
||||
|
||||
void ASF::Tag::setAttribute(const String &name, const AttributeList &values)
|
||||
@@ -210,60 +227,68 @@ bool ASF::Tag::isEmpty() const
|
||||
|
||||
namespace
|
||||
{
|
||||
const std::pair<const char *, const char *> keyTranslation[] = {
|
||||
std::make_pair("WM/AlbumTitle", "ALBUM"),
|
||||
std::make_pair("WM/AlbumArtist", "ALBUMARTIST"),
|
||||
std::make_pair("WM/Composer", "COMPOSER"),
|
||||
std::make_pair("WM/Writer", "LYRICIST"),
|
||||
std::make_pair("WM/Conductor", "CONDUCTOR"),
|
||||
std::make_pair("WM/ModifiedBy", "REMIXER"),
|
||||
std::make_pair("WM/Year", "DATE"),
|
||||
std::make_pair("WM/OriginalReleaseYear", "ORIGINALDATE"),
|
||||
std::make_pair("WM/Producer", "PRODUCER"),
|
||||
std::make_pair("WM/ContentGroupDescription", "WORK"),
|
||||
std::make_pair("WM/SubTitle", "SUBTITLE"),
|
||||
std::make_pair("WM/SetSubTitle", "DISCSUBTITLE"),
|
||||
std::make_pair("WM/TrackNumber", "TRACKNUMBER"),
|
||||
std::make_pair("WM/PartOfSet", "DISCNUMBER"),
|
||||
std::make_pair("WM/Genre", "GENRE"),
|
||||
std::make_pair("WM/BeatsPerMinute", "BPM"),
|
||||
std::make_pair("WM/Mood", "MOOD"),
|
||||
std::make_pair("WM/ISRC", "ISRC"),
|
||||
std::make_pair("WM/Lyrics", "LYRICS"),
|
||||
std::make_pair("WM/Media", "MEDIA"),
|
||||
std::make_pair("WM/Publisher", "LABEL"),
|
||||
std::make_pair("WM/CatalogNo", "CATALOGNUMBER"),
|
||||
std::make_pair("WM/Barcode", "BARCODE"),
|
||||
std::make_pair("WM/EncodedBy", "ENCODEDBY"),
|
||||
std::make_pair("WM/AlbumSortOrder", "ALBUMSORT"),
|
||||
std::make_pair("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT"),
|
||||
std::make_pair("WM/ArtistSortOrder", "ARTISTSORT"),
|
||||
std::make_pair("WM/TitleSortOrder", "TITLESORT"),
|
||||
std::make_pair("WM/Script", "SCRIPT"),
|
||||
std::make_pair("WM/Language", "LANGUAGE"),
|
||||
std::make_pair("WM/ARTISTS", "ARTISTS"),
|
||||
std::make_pair("ASIN", "ASIN"),
|
||||
std::make_pair("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID"),
|
||||
std::make_pair("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID"),
|
||||
std::make_pair("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID"),
|
||||
std::make_pair("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"),
|
||||
std::make_pair("MusicBrainz/Album Release Country", "RELEASECOUNTRY"),
|
||||
std::make_pair("MusicBrainz/Album Status", "RELEASESTATUS"),
|
||||
std::make_pair("MusicBrainz/Album Type", "RELEASETYPE"),
|
||||
std::make_pair("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"),
|
||||
std::make_pair("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID"),
|
||||
std::make_pair("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID"),
|
||||
std::make_pair("MusicIP/PUID", "MUSICIP_PUID"),
|
||||
std::make_pair("Acoustid/Id", "ACOUSTID_ID"),
|
||||
std::make_pair("Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT"),
|
||||
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 size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
|
||||
String translateKey(const String &key)
|
||||
{
|
||||
for(size_t i = 0; i < keyTranslationSize; ++i) {
|
||||
if(key == keyTranslation[i].first)
|
||||
return keyTranslation[i].second;
|
||||
for(const auto &[k, t] : keyTranslation) {
|
||||
if(key == k)
|
||||
return t;
|
||||
}
|
||||
|
||||
return String();
|
||||
@@ -287,25 +312,23 @@ PropertyMap ASF::Tag::properties() const
|
||||
props["COMMENT"] = d->comment;
|
||||
}
|
||||
|
||||
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
|
||||
for(; it != d->attributeListMap.end(); ++it) {
|
||||
const String key = translateKey(it->first);
|
||||
for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) {
|
||||
const String key = translateKey(k);
|
||||
if(!key.isEmpty()) {
|
||||
AttributeList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
for(const auto &attr : attributes) {
|
||||
if(key == "TRACKNUMBER") {
|
||||
if(it2->type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(it2->toUInt()));
|
||||
if(attr.type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(attr.toUInt()));
|
||||
else
|
||||
props.insert(key, it2->toString());
|
||||
props.insert(key, attr.toString());
|
||||
}
|
||||
else {
|
||||
props.insert(key, it2->toString());
|
||||
props.insert(key, attr.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
props.unsupportedData().append(it->first);
|
||||
props.addUnsupportedData(k);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
@@ -313,69 +336,116 @@ PropertyMap ASF::Tag::properties() const
|
||||
|
||||
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
|
||||
{
|
||||
StringList::ConstIterator it = props.begin();
|
||||
for(; it != props.end(); ++it)
|
||||
d->attributeListMap.erase(*it);
|
||||
for(const auto &prop : props)
|
||||
d->attributeListMap.erase(prop);
|
||||
}
|
||||
|
||||
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
|
||||
{
|
||||
static Map<String, String> reverseKeyMap;
|
||||
if(reverseKeyMap.isEmpty()) {
|
||||
for(size_t i = 0; i < keyTranslationSize; i++) {
|
||||
reverseKeyMap[keyTranslation[i].second] = keyTranslation[i].first;
|
||||
for(const auto &[k, t] : keyTranslation) {
|
||||
reverseKeyMap[t] = k;
|
||||
}
|
||||
}
|
||||
|
||||
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") {
|
||||
const PropertyMap origProps = properties();
|
||||
for(const auto &[prop, _] : origProps) {
|
||||
if(!props.contains(prop) || props[prop].isEmpty()) {
|
||||
if(prop == "TITLE") {
|
||||
d->title.clear();
|
||||
}
|
||||
else if(it->first == "ARTIST") {
|
||||
else if(prop == "ARTIST") {
|
||||
d->artist.clear();
|
||||
}
|
||||
else if(it->first == "COMMENT") {
|
||||
else if(prop == "COMMENT") {
|
||||
d->comment.clear();
|
||||
}
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
else if(prop == "COPYRIGHT") {
|
||||
d->copyright.clear();
|
||||
}
|
||||
else {
|
||||
d->attributeListMap.erase(reverseKeyMap[it->first]);
|
||||
d->attributeListMap.erase(reverseKeyMap[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap ignoredProps;
|
||||
it = props.begin();
|
||||
for(; it != props.end(); ++it) {
|
||||
if(reverseKeyMap.contains(it->first)) {
|
||||
String name = reverseKeyMap[it->first];
|
||||
for(const auto &[prop, attributes] : props) {
|
||||
if(reverseKeyMap.contains(prop)) {
|
||||
String name = reverseKeyMap[prop];
|
||||
removeItem(name);
|
||||
StringList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
addAttribute(name, *it2);
|
||||
for(const auto &attr : attributes) {
|
||||
addAttribute(name, attr);
|
||||
}
|
||||
}
|
||||
else if(it->first == "TITLE") {
|
||||
d->title = it->second.toString();
|
||||
else if(prop == "TITLE") {
|
||||
d->title = attributes.toString();
|
||||
}
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist = it->second.toString();
|
||||
else if(prop == "ARTIST") {
|
||||
d->artist = attributes.toString();
|
||||
}
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment = it->second.toString();
|
||||
else if(prop == "COMMENT") {
|
||||
d->comment = attributes.toString();
|
||||
}
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright = it->second.toString();
|
||||
else if(prop == "COPYRIGHT") {
|
||||
d->copyright = attributes.toString();
|
||||
}
|
||||
else {
|
||||
ignoredProps.insert(it->first, it->second);
|
||||
ignoredProps.insert(prop, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
return ignoredProps;
|
||||
}
|
||||
|
||||
StringList ASF::Tag::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys;
|
||||
if(d->attributeListMap.contains("WM/Picture")) {
|
||||
keys.append("PICTURE");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> ASF::Tag::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props;
|
||||
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 {
|
||||
|
||||
typedef List<Attribute> AttributeList;
|
||||
typedef Map<String, AttributeList> AttributeListMap;
|
||||
using AttributeList = List<Attribute>;
|
||||
using AttributeListMap = Map<String, AttributeList>;
|
||||
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag {
|
||||
|
||||
@@ -47,34 +47,37 @@ namespace TagLib {
|
||||
|
||||
Tag();
|
||||
|
||||
virtual ~Tag();
|
||||
~Tag() override;
|
||||
|
||||
Tag(const Tag &) = delete;
|
||||
Tag &operator=(const Tag &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the track name.
|
||||
*/
|
||||
virtual String title() const;
|
||||
String title() const override;
|
||||
|
||||
/*!
|
||||
* Returns the artist name.
|
||||
*/
|
||||
virtual String artist() const;
|
||||
String artist() const override;
|
||||
|
||||
/*!
|
||||
* Returns the album name; if no album name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String album() const;
|
||||
String album() const override;
|
||||
|
||||
/*!
|
||||
* Returns the track comment.
|
||||
*/
|
||||
virtual String comment() const;
|
||||
String comment() const override;
|
||||
|
||||
/*!
|
||||
* Returns the genre name; if no genre is present in the tag String::null
|
||||
* will be returned.
|
||||
*/
|
||||
virtual String genre() const;
|
||||
String genre() const override;
|
||||
|
||||
/*!
|
||||
* Returns the rating.
|
||||
@@ -90,69 +93,70 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the year; if there is no year set, this will return 0.
|
||||
*/
|
||||
virtual unsigned int year() const;
|
||||
unsigned int year() const override;
|
||||
|
||||
/*!
|
||||
* Returns the track number; if there is no track number set, this will
|
||||
* return 0.
|
||||
*/
|
||||
virtual unsigned int track() const;
|
||||
unsigned int track() const override;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a s.
|
||||
* Sets the title to \a value.
|
||||
*/
|
||||
virtual void setTitle(const String &s);
|
||||
void setTitle(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the artist to \a s.
|
||||
* Sets the artist to \a value.
|
||||
*/
|
||||
virtual void setArtist(const String &s);
|
||||
void setArtist(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the album to \a s. If \a s is String::null then this value will be
|
||||
* Sets the album to \a value. If \a value is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setAlbum(const String &s);
|
||||
void setAlbum(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the comment to \a s.
|
||||
* Sets the comment to \a value.
|
||||
*/
|
||||
virtual void setComment(const String &s);
|
||||
void setComment(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the rating to \a s.
|
||||
* Sets the rating to \a value.
|
||||
*/
|
||||
virtual void setRating(const String &s);
|
||||
virtual void setRating(const String &value);
|
||||
|
||||
/*!
|
||||
* Sets the copyright to \a s.
|
||||
* Sets the copyright to \a value.
|
||||
*/
|
||||
virtual void setCopyright(const String &s);
|
||||
virtual void setCopyright(const String &value);
|
||||
|
||||
/*!
|
||||
* Sets the genre to \a s.
|
||||
* Sets the genre to \a value.
|
||||
*/
|
||||
virtual void setGenre(const String &s);
|
||||
void setGenre(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the year to \a i. If \a s is 0 then this value will be cleared.
|
||||
* Sets the year to \a value. If \a value is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setYear(unsigned int i);
|
||||
void setYear(unsigned int value) override;
|
||||
|
||||
/*!
|
||||
* Sets the track to \a i. If \a s is 0 then this value will be cleared.
|
||||
* Sets the track to \a value. If \a value is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setTrack(unsigned int i);
|
||||
void setTrack(unsigned int value) override;
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
*/
|
||||
virtual bool isEmpty() const;
|
||||
bool isEmpty() const override;
|
||||
|
||||
/*!
|
||||
* \deprecated Use attributeListMap() const, contains(), removeItem(),
|
||||
* \warning You should not modify this data structure directly, instead
|
||||
* use attributeListMap() const, contains(), removeItem(),
|
||||
* attribute(), setAttribute(), addAttribute().
|
||||
*/
|
||||
AttributeListMap &attributeListMap();
|
||||
@@ -161,18 +165,17 @@ namespace TagLib {
|
||||
* Returns a reference to the item list map. This is an AttributeListMap of
|
||||
* all of the items in the tag.
|
||||
*/
|
||||
// BIC: return by value
|
||||
const AttributeListMap &attributeListMap() const;
|
||||
|
||||
/*!
|
||||
* \return True if a value for \a attribute is currently set.
|
||||
* \return True if a value for \a key is currently set.
|
||||
*/
|
||||
bool contains(const String &name) const;
|
||||
bool contains(const String &key) const;
|
||||
|
||||
/*!
|
||||
* Removes the \a key attribute from the tag
|
||||
*/
|
||||
void removeItem(const String &name);
|
||||
void removeItem(const String &key);
|
||||
|
||||
/*!
|
||||
* \return The list of values for the key \a name, or an empty list if no
|
||||
@@ -181,8 +184,8 @@ namespace TagLib {
|
||||
AttributeList attribute(const String &name) const;
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
void setAttribute(const String &name, const Attribute &attribute);
|
||||
|
||||
@@ -192,19 +195,24 @@ namespace TagLib {
|
||||
void setAttribute(const String &name, const AttributeList &values);
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
void addAttribute(const String &name, const Attribute &attribute);
|
||||
|
||||
PropertyMap properties() const;
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
PropertyMap properties() const override;
|
||||
void removeUnsupportedProperties(const StringList &props) override;
|
||||
PropertyMap setProperties(const PropertyMap &props) override;
|
||||
|
||||
StringList complexPropertyKeys() const override;
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
|
||||
private:
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
};
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace TagLib
|
||||
namespace
|
||||
{
|
||||
|
||||
inline unsigned short readWORD(File *file, bool *ok = 0)
|
||||
inline unsigned short readWORD(File *file, bool *ok = nullptr)
|
||||
{
|
||||
const ByteVector v = file->readBlock(2);
|
||||
if(v.size() != 2) {
|
||||
@@ -48,7 +48,7 @@ namespace TagLib
|
||||
return v.toUShort(false);
|
||||
}
|
||||
|
||||
inline unsigned int readDWORD(File *file, bool *ok = 0)
|
||||
inline unsigned int readDWORD(File *file, bool *ok = nullptr)
|
||||
{
|
||||
const ByteVector v = file->readBlock(4);
|
||||
if(v.size() != 4) {
|
||||
@@ -59,7 +59,7 @@ namespace TagLib
|
||||
return v.toUInt(false);
|
||||
}
|
||||
|
||||
inline long long readQWORD(File *file, bool *ok = 0)
|
||||
inline long long readQWORD(File *file, bool *ok = nullptr)
|
||||
{
|
||||
const ByteVector v = file->readBlock(8);
|
||||
if(v.size() != 8) {
|
||||
|
||||
@@ -23,88 +23,49 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
|
||||
#include "aiffproperties.h"
|
||||
#include "apeproperties.h"
|
||||
#include "asfproperties.h"
|
||||
#include "flacproperties.h"
|
||||
#include "mp4properties.h"
|
||||
#include "mpcproperties.h"
|
||||
#include "mpegproperties.h"
|
||||
#include "opusproperties.h"
|
||||
#include "speexproperties.h"
|
||||
#include "trueaudioproperties.h"
|
||||
#include "vorbisproperties.h"
|
||||
#include "wavproperties.h"
|
||||
#include "wavpackproperties.h"
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
// This macro is a workaround for the fact that we can't add virtual functions.
|
||||
// Should be true virtual functions in taglib2.
|
||||
|
||||
#define VIRTUAL_FUNCTION_WORKAROUND(function_name, default_value) \
|
||||
if(dynamic_cast<const APE::Properties*>(this)) \
|
||||
return dynamic_cast<const APE::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const ASF::Properties*>(this)) \
|
||||
return dynamic_cast<const ASF::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const FLAC::Properties*>(this)) \
|
||||
return dynamic_cast<const FLAC::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const MP4::Properties*>(this)) \
|
||||
return dynamic_cast<const MP4::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const MPC::Properties*>(this)) \
|
||||
return dynamic_cast<const MPC::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const MPEG::Properties*>(this)) \
|
||||
return dynamic_cast<const MPEG::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const Ogg::Opus::Properties*>(this)) \
|
||||
return dynamic_cast<const Ogg::Opus::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const Ogg::Speex::Properties*>(this)) \
|
||||
return dynamic_cast<const Ogg::Speex::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const TrueAudio::Properties*>(this)) \
|
||||
return dynamic_cast<const TrueAudio::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const RIFF::AIFF::Properties*>(this)) \
|
||||
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const RIFF::WAV::Properties*>(this)) \
|
||||
return dynamic_cast<const RIFF::WAV::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const Vorbis::Properties*>(this)) \
|
||||
return dynamic_cast<const Vorbis::Properties*>(this)->function_name(); \
|
||||
if(dynamic_cast<const WavPack::Properties*>(this)) \
|
||||
return dynamic_cast<const WavPack::Properties*>(this)->function_name(); \
|
||||
return (default_value);
|
||||
|
||||
class AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioProperties::~AudioProperties()
|
||||
{
|
||||
AudioProperties::~AudioProperties() = default;
|
||||
|
||||
int AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
VIRTUAL_FUNCTION_WORKAROUND(lengthInSeconds, 0)
|
||||
return lengthInMilliseconds() / 1000;
|
||||
}
|
||||
|
||||
int AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
VIRTUAL_FUNCTION_WORKAROUND(lengthInMilliseconds, 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioProperties::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioProperties::sampleRate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioProperties::AudioProperties(ReadStyle) :
|
||||
d(0)
|
||||
AudioProperties::AudioProperties(ReadStyle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#ifndef TAGLIB_AUDIOPROPERTIES_H
|
||||
#define TAGLIB_AUDIOPROPERTIES_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "taglib.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
@@ -64,10 +67,19 @@ namespace TagLib {
|
||||
*/
|
||||
virtual ~AudioProperties();
|
||||
|
||||
AudioProperties(const AudioProperties &) = delete;
|
||||
AudioProperties &operator=(const AudioProperties &) = delete;
|
||||
|
||||
/*!
|
||||
* 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
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated Use lengthInSeconds().
|
||||
*/
|
||||
TAGLIB_DEPRECATED
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -75,28 +87,26 @@ namespace TagLib {
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* 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 = 0;
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const = 0;
|
||||
virtual int sampleRate() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
@@ -115,11 +125,9 @@ namespace TagLib {
|
||||
AudioProperties(ReadStyle style);
|
||||
|
||||
private:
|
||||
AudioProperties(const AudioProperties &);
|
||||
AudioProperties &operator=(const AudioProperties &);
|
||||
|
||||
class AudioPropertiesPrivate;
|
||||
AudioPropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AudioPropertiesPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace TagLib
|
||||
|
||||
156
taglib/dsdiff/dsdiffdiintag.cpp
Normal file
156
taglib/dsdiff/dsdiffdiintag.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsdiffdiintag.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace DSDIFF::DIIN;
|
||||
|
||||
class DSDIFF::DIIN::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
String title;
|
||||
String artist;
|
||||
};
|
||||
|
||||
DSDIFF::DIIN::Tag::Tag() :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag::~Tag() = default;
|
||||
|
||||
String DSDIFF::DIIN::Tag::title() const
|
||||
{
|
||||
return d->title;
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::artist() const
|
||||
{
|
||||
return d->artist;
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::album() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::comment() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::genre() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
unsigned int DSDIFF::DIIN::Tag::year() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int DSDIFF::DIIN::Tag::track() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setTitle(const String &title)
|
||||
{
|
||||
d->title = title;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setArtist(const String &artist)
|
||||
{
|
||||
d->artist = artist;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setAlbum(const String &)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setAlbum() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setComment(const String &)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setComment() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setGenre(const String &)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setGenre() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setYear(unsigned int)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setYear() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setTrack() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::DIIN::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
properties["TITLE"] = d->title;
|
||||
properties["ARTIST"] = d->artist;
|
||||
return properties;
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap props(origProps);
|
||||
props.removeEmpty();
|
||||
StringList oneValueSet;
|
||||
|
||||
if(props.contains("TITLE")) {
|
||||
d->title = props["TITLE"].front();
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
d->title.clear();
|
||||
|
||||
if(props.contains("ARTIST")) {
|
||||
d->artist = props["ARTIST"].front();
|
||||
oneValueSet.append("ARTIST");
|
||||
} else
|
||||
d->artist.clear();
|
||||
|
||||
// for each tag that has been set above, remove the first entry in the corresponding
|
||||
// value list. The others will be returned as unsupported by this format.
|
||||
for(const auto &entry : std::as_const(oneValueSet)) {
|
||||
if(props[entry].size() == 1)
|
||||
props.erase(entry);
|
||||
else
|
||||
props[entry].erase(props[entry].begin());
|
||||
}
|
||||
return props;
|
||||
}
|
||||
147
taglib/dsdiff/dsdiffdiintag.h
Normal file
147
taglib/dsdiff/dsdiffdiintag.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSDIFFDIINTAG_H
|
||||
#define TAGLIB_DSDIFFDIINTAG_H
|
||||
|
||||
#include "tag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSDIFF {
|
||||
|
||||
namespace DIIN {
|
||||
|
||||
/*!
|
||||
* Tags from the Edited Master Chunk Info
|
||||
*
|
||||
* Only Title and Artist tags are supported
|
||||
*/
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
||||
{
|
||||
public:
|
||||
Tag();
|
||||
~Tag() override;
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
String title() const override;
|
||||
|
||||
/*!
|
||||
* Returns the artist name; if no artist name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
String artist() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String album() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String comment() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String genre() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
unsigned int year() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
unsigned int track() const override;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a title. If \a title is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
void setTitle(const String &title) override;
|
||||
|
||||
/*!
|
||||
* Sets the artist to \a artist. If \a artist is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
void setArtist(const String &artist) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setAlbum(const String &album) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setComment(const String &comment) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setGenre(const String &genre) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setYear(unsigned int year) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setTrack(unsigned int track) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Since the DIIN tag is very limited, the exported map is as well.
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Because of the limitations of the DIIN file tag, any tags besides
|
||||
* TITLE and ARTIST, will be
|
||||
* returned. Additionally, if the map contains tags with multiple values,
|
||||
* all but the first will be contained in the returned map of unsupported
|
||||
* properties.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
private:
|
||||
class TagPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
};
|
||||
} // namespace DIIN
|
||||
} // namespace DSDIFF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
934
taglib/dsdiff/dsdifffile.cpp
Normal file
934
taglib/dsdiff/dsdifffile.cpp
Normal file
@@ -0,0 +1,934 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsdifffile.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tdebug.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "tagutils.h"
|
||||
#include "tagunion.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Chunk64
|
||||
{
|
||||
ByteVector name;
|
||||
unsigned long long offset;
|
||||
unsigned long long size;
|
||||
char padding;
|
||||
};
|
||||
|
||||
typedef std::vector<Chunk64> ChunkList;
|
||||
|
||||
int chunkIndex(const ChunkList &chunks, const ByteVector &id)
|
||||
{
|
||||
for(size_t i = 0; i < chunks.size(); i++) {
|
||||
if(chunks[i].name == id)
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool isValidChunkID(const ByteVector &name)
|
||||
{
|
||||
if(name.size() != 4)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(name[i] < 32 || name[i] > 126)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum {
|
||||
ID3v2Index = 0,
|
||||
DIINIndex = 1
|
||||
};
|
||||
enum {
|
||||
PROPChunk = 0,
|
||||
DIINChunk = 1
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class DSDIFF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(ID3v2::FrameFactory *frameFactory)
|
||||
: ID3v2FrameFactory(frameFactory ? frameFactory
|
||||
: ID3v2::FrameFactory::instance())
|
||||
{
|
||||
}
|
||||
|
||||
~FilePrivate() = default;
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
Endianness endianness { BigEndian };
|
||||
ByteVector type;
|
||||
unsigned long long size { 0 };
|
||||
ByteVector format;
|
||||
ChunkList chunks;
|
||||
std::array<ChunkList, 2> childChunks;
|
||||
std::array<int, 2> childChunkIndex { -1, -1 };
|
||||
/*
|
||||
* Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
|
||||
*/
|
||||
bool isID3InPropChunk { false };
|
||||
/*
|
||||
* ID3 chunks are present. This is then the index of the one in PROP chunk that
|
||||
* will be removed upon next save to remove duplicates.
|
||||
*/
|
||||
int duplicateID3V2chunkIndex { -1 };
|
||||
|
||||
std::unique_ptr<Properties> properties;
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
ByteVector id3v2TagChunkID { "ID3 " };
|
||||
|
||||
bool hasID3v2 { false };
|
||||
bool hasDiin { false };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool DSDIFF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A DSDIFF file has to start with "FRM8????????DSD ".
|
||||
const ByteVector id = Utils::readHeader(stream, 16, false);
|
||||
return (id.startsWith("FRM8") && id.containsAt("DSD ", 12));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSDIFF::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSDIFF::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSDIFF::File::~File() = default;
|
||||
|
||||
TagLib::Tag *DSDIFF::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
ID3v2::Tag *DSDIFF::File::ID3v2Tag(bool create) const
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, create, d->ID3v2FrameFactory);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->hasID3v2;
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag(bool create) const
|
||||
{
|
||||
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, create);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasDIINTag() const
|
||||
{
|
||||
return d->hasDiin;
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::File::properties() const
|
||||
{
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
d->tag.removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return ID3v2Tag(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
DSDIFF::Properties *DSDIFF::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool DSDIFF::File::save()
|
||||
{
|
||||
return save(AllTags);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::save(int tags, StripTags strip, ID3v2::Version version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("DSDIFF::File::save() -- File is read only.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isValid()) {
|
||||
debug("DSDIFF::File::save() -- Trying to save invalid file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(strip == StripOthers)
|
||||
File::strip(~tags);
|
||||
|
||||
// First: save ID3V2 chunk
|
||||
|
||||
ID3v2::Tag *id3v2Tag = ID3v2Tag();
|
||||
|
||||
if((tags & ID3v2) && id3v2Tag) {
|
||||
if(d->isID3InPropChunk) {
|
||||
if(!id3v2Tag->isEmpty()) {
|
||||
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(version), PROPChunk);
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!id3v2Tag->isEmpty()) {
|
||||
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render(version));
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setRootChunkData(d->id3v2TagChunkID, ByteVector());
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second: save the DIIN chunk
|
||||
|
||||
DSDIFF::DIIN::Tag *diinTag = DIINTag();
|
||||
|
||||
if((tags & DIIN) && diinTag) {
|
||||
if(!diinTag->title().isEmpty()) {
|
||||
ByteVector diinTitle;
|
||||
diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian));
|
||||
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
|
||||
setChildChunkData("DITI", diinTitle, DIINChunk);
|
||||
}
|
||||
else
|
||||
setChildChunkData("DITI", ByteVector(), DIINChunk);
|
||||
|
||||
if(!diinTag->artist().isEmpty()) {
|
||||
ByteVector diinArtist;
|
||||
diinArtist.append(ByteVector::fromUInt(diinTag->artist().size(), d->endianness == BigEndian));
|
||||
diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString()));
|
||||
setChildChunkData("DIAR", diinArtist, DIINChunk);
|
||||
}
|
||||
else
|
||||
setChildChunkData("DIAR", ByteVector(), DIINChunk);
|
||||
}
|
||||
|
||||
// Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any
|
||||
if(d->duplicateID3V2chunkIndex >= 0) {
|
||||
setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk);
|
||||
d->duplicateID3V2chunkIndex = -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DSDIFF::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v2) {
|
||||
removeRootChunk("ID3 ");
|
||||
removeRootChunk("id3 ");
|
||||
removeChildChunk("ID3 ", PROPChunk);
|
||||
removeChildChunk("id3 ", PROPChunk);
|
||||
d->hasID3v2 = false;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(nullptr, 0, d->ID3v2FrameFactory));
|
||||
d->duplicateID3V2chunkIndex = -1;
|
||||
d->isID3InPropChunk = false;
|
||||
d->id3v2TagChunkID.setData("ID3 ");
|
||||
}
|
||||
if(tags & DIIN) {
|
||||
removeChildChunk("DITI", DIINChunk);
|
||||
removeChildChunk("DIAR", DIINChunk);
|
||||
if(d->childChunks[DIINIndex].empty()) {
|
||||
removeRootChunk("DIIN");
|
||||
}
|
||||
|
||||
d->hasDiin = false;
|
||||
d->tag.set(DIINIndex, new DIIN::Tag);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSDIFF::File::removeRootChunk(unsigned int i)
|
||||
{
|
||||
unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12;
|
||||
|
||||
d->size -= chunkSize;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
removeBlock(d->chunks[i].offset - 12, chunkSize);
|
||||
|
||||
// Update the internal offsets
|
||||
|
||||
d->chunks.erase(d->chunks.begin() + i);
|
||||
for(int &cci : d->childChunkIndex) {
|
||||
if(cci > static_cast<int>(i)) {
|
||||
--cci;
|
||||
}
|
||||
}
|
||||
updateRootChunksStructure(i);
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeRootChunk(const ByteVector &id)
|
||||
{
|
||||
int i = chunkIndex(d->chunks, id);
|
||||
|
||||
if(i >= 0)
|
||||
removeRootChunk(i);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
|
||||
{
|
||||
if(data.isEmpty()) {
|
||||
removeRootChunk(i);
|
||||
return;
|
||||
}
|
||||
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(d->chunks[i].name,
|
||||
data,
|
||||
d->chunks[i].offset - 12,
|
||||
static_cast<unsigned long>(d->chunks[i].size + d->chunks[i].padding + 12));
|
||||
|
||||
d->chunks[i].size = data.size();
|
||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Finally update the internal offsets
|
||||
|
||||
updateRootChunksStructure(i + 1);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data)
|
||||
{
|
||||
if(d->chunks.empty()) {
|
||||
debug("DSDIFF::File::setRootChunkData('" + name + "') - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
int i = chunkIndex(d->chunks, name);
|
||||
|
||||
if(i >= 0) {
|
||||
setRootChunkData(i, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
i = static_cast<int>(d->chunks.size()) - 1;
|
||||
unsigned long long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding;
|
||||
|
||||
// First we update the global size
|
||||
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// Now add the chunk to the file
|
||||
const unsigned long long fileLength = length();
|
||||
writeChunk(name,
|
||||
data,
|
||||
offset,
|
||||
static_cast<unsigned long>(fileLength > offset ? fileLength - offset : 0),
|
||||
(offset & 1) ? 1 : 0);
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = name;
|
||||
chunk.size = data.size();
|
||||
chunk.offset = offset + 12;
|
||||
chunk.padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum)
|
||||
{
|
||||
ChunkList &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
// Update global size
|
||||
|
||||
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
|
||||
d->size -= removedChunkTotalSize;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// Update child chunk size
|
||||
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize;
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
d->endianness == BigEndian),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
// Remove the chunk
|
||||
|
||||
removeBlock(childChunks[i].offset - 12, removedChunkTotalSize);
|
||||
|
||||
// Update the internal offsets
|
||||
// For child chunks
|
||||
|
||||
if((i + 1) < childChunks.size()) {
|
||||
childChunks[i + 1].offset = childChunks[i].offset;
|
||||
for(unsigned int c = i + 2; c < childChunks.size(); ++c)
|
||||
childChunks[c].offset = childChunks[c - 1].offset + 12
|
||||
+ childChunks[c - 1].size + childChunks[c - 1].padding;
|
||||
}
|
||||
|
||||
// And for root chunks
|
||||
|
||||
childChunks.erase(childChunks.begin() + i);
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeChildChunk(const ByteVector &id, unsigned int childChunkNum)
|
||||
{
|
||||
int i = chunkIndex(d->childChunks[childChunkNum], id);
|
||||
|
||||
if(i >= 0)
|
||||
removeChildChunk(i, childChunkNum);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setChildChunkData(unsigned int i,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
{
|
||||
ChunkList &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(data.isEmpty()) {
|
||||
removeChildChunk(i, childChunkNum);
|
||||
return;
|
||||
}
|
||||
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
|
||||
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
|
||||
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// And the PROP chunk size
|
||||
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size +=
|
||||
((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
d->endianness == BigEndian),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(childChunks[i].name,
|
||||
data,
|
||||
childChunks[i].offset - 12,
|
||||
static_cast<unsigned long>(childChunks[i].size + childChunks[i].padding + 12));
|
||||
|
||||
childChunks[i].size = data.size();
|
||||
childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Now update the internal offsets
|
||||
// For child Chunks
|
||||
for(i++; i < childChunks.size(); i++)
|
||||
childChunks[i].offset = childChunks[i - 1].offset + 12
|
||||
+ childChunks[i - 1].size + childChunks[i - 1].padding;
|
||||
|
||||
// And for root chunks
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setChildChunkData(const ByteVector &name,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
{
|
||||
ChunkList &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(int i = chunkIndex(childChunks, name); i >= 0) {
|
||||
setChildChunkData(i, data, childChunkNum);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not attempt to remove a non existing chunk
|
||||
|
||||
if(data.isEmpty())
|
||||
return;
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
|
||||
unsigned long long offset = 0;
|
||||
if(!childChunks.empty()) {
|
||||
size_t i = childChunks.size() - 1;
|
||||
offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding;
|
||||
}
|
||||
else if(childChunkNum == DIINChunk) {
|
||||
int i = d->childChunkIndex[DIINChunk];
|
||||
if(i < 0) {
|
||||
setRootChunkData("DIIN", ByteVector());
|
||||
const int lastChunkIndex = static_cast<int>(d->chunks.size()) - 1;
|
||||
if(lastChunkIndex >= 0 && d->chunks[lastChunkIndex].name == "DIIN") {
|
||||
i = lastChunkIndex;
|
||||
d->childChunkIndex[DIINChunk] = lastChunkIndex;
|
||||
d->hasDiin = true;
|
||||
}
|
||||
}
|
||||
if(i >= 0) {
|
||||
offset = d->chunks[i].offset; // 12 is already added in setRootChunkData()
|
||||
}
|
||||
}
|
||||
if(offset == 0) {
|
||||
debug("DSDIFF::File::setChildChunkData - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// First we update the global size
|
||||
|
||||
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// And the child chunk size
|
||||
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1)
|
||||
+ ((data.size() + 1) & ~1) + 12;
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
d->endianness == BigEndian),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Now add the chunk to the file
|
||||
|
||||
unsigned long long nextRootChunkIdx = length();
|
||||
if((d->childChunkIndex[childChunkNum] + 1) < static_cast<int>(d->chunks.size()))
|
||||
nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12;
|
||||
|
||||
writeChunk(name, data, offset,
|
||||
static_cast<unsigned long>(
|
||||
nextRootChunkIdx > offset ? nextRootChunkIdx - offset : 0),
|
||||
(offset & 1) ? 1 : 0);
|
||||
|
||||
// For root chunks
|
||||
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = name;
|
||||
chunk.size = data.size();
|
||||
chunk.offset = offset + 12;
|
||||
chunk.padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
childChunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
|
||||
{
|
||||
for(unsigned int i = startingChunk; i < d->chunks.size(); i++)
|
||||
d->chunks[i].offset = d->chunks[i - 1].offset + 12
|
||||
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
|
||||
|
||||
// Update child chunks structure as well
|
||||
|
||||
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
|
||||
ChunkList &childChunksToUpdate = d->childChunks[PROPChunk];
|
||||
if(!childChunksToUpdate.empty()) {
|
||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12;
|
||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
||||
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
|
||||
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
|
||||
}
|
||||
|
||||
}
|
||||
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
|
||||
ChunkList &childChunksToUpdate = d->childChunks[DIINChunk];
|
||||
if(!childChunksToUpdate.empty()) {
|
||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12;
|
||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
||||
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
|
||||
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||
{
|
||||
bool bigEndian = d->endianness == BigEndian;
|
||||
|
||||
d->type = readBlock(4);
|
||||
d->size = readBlock(8).toLongLong(bigEndian);
|
||||
d->format = readBlock(4);
|
||||
|
||||
// + 12: chunk header at least, fix for additional junk bytes
|
||||
|
||||
while(tell() + 12 <= length()) {
|
||||
ByteVector chunkName = readBlock(4);
|
||||
unsigned long long chunkSize = readBlock(8).toLongLong(bigEndian);
|
||||
|
||||
if(!isValidChunkID(chunkName)) {
|
||||
debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<unsigned long long>(tell()) + chunkSize >
|
||||
static_cast<unsigned long long>(length())) {
|
||||
debug("DSDIFF::File::read() -- Chunk '" + chunkName
|
||||
+ "' has invalid size (larger than the file size)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = chunkName;
|
||||
chunk.size = chunkSize;
|
||||
chunk.offset = tell();
|
||||
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
|
||||
chunk.padding = 0;
|
||||
offset_t uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
else
|
||||
chunk.padding = 1;
|
||||
}
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
// For DSD uncompressed
|
||||
unsigned long long lengthDSDSamplesTimeChannels = 0;
|
||||
// For computing bitrate
|
||||
unsigned long long audioDataSizeinBytes = 0;
|
||||
// For DST compressed frames
|
||||
unsigned long dstNumFrames = 0;
|
||||
// For DST compressed frames
|
||||
unsigned short dstFrameRate = 0;
|
||||
|
||||
for(unsigned int i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == "DSD ") {
|
||||
lengthDSDSamplesTimeChannels = d->chunks[i].size * 8;
|
||||
audioDataSizeinBytes = d->chunks[i].size;
|
||||
}
|
||||
else if(d->chunks[i].name == "DST ") {
|
||||
// Now decode the chunks inside the DST chunk to read the DST Frame Information one
|
||||
long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
||||
seek(d->chunks[i].offset);
|
||||
|
||||
audioDataSizeinBytes = d->chunks[i].size;
|
||||
|
||||
while(tell() + 12 <= dstChunkEnd) {
|
||||
ByteVector dstChunkName = readBlock(4);
|
||||
long long dstChunkSize = readBlock(8).toLongLong(bigEndian);
|
||||
|
||||
if(!isValidChunkID(dstChunkName)) {
|
||||
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(tell()) + dstChunkSize > dstChunkEnd) {
|
||||
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName
|
||||
+ "' has invalid size (larger than the DST chunk)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(dstChunkName == "FRTE") {
|
||||
// Found the DST frame information chunk
|
||||
dstNumFrames = readBlock(4).toUInt(bigEndian);
|
||||
dstFrameRate = readBlock(2).toUShort(bigEndian);
|
||||
// Found the wanted one, no need to look at the others
|
||||
break;
|
||||
}
|
||||
|
||||
seek(dstChunkSize, Current);
|
||||
|
||||
// Check padding
|
||||
offset_t uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(d->chunks[i].name == "PROP") {
|
||||
d->childChunkIndex[PROPChunk] = i;
|
||||
// Now decodes the chunks inside the PROP chunk
|
||||
long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
||||
// +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
|
||||
seek(d->chunks[i].offset + 4);
|
||||
while(tell() + 12 <= propChunkEnd) {
|
||||
ByteVector propChunkName = readBlock(4);
|
||||
long long propChunkSize = readBlock(8).toLongLong(bigEndian);
|
||||
|
||||
if(!isValidChunkID(propChunkName)) {
|
||||
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(tell()) + propChunkSize > propChunkEnd) {
|
||||
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName
|
||||
+ "' has invalid size (larger than the PROP chunk)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = propChunkName;
|
||||
chunk.size = propChunkSize;
|
||||
chunk.offset = tell();
|
||||
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
chunk.padding = 0;
|
||||
offset_t uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
else
|
||||
chunk.padding = 1;
|
||||
}
|
||||
d->childChunks[PROPChunk].push_back(chunk);
|
||||
}
|
||||
}
|
||||
else if(d->chunks[i].name == "DIIN") {
|
||||
d->childChunkIndex[DIINChunk] = i;
|
||||
d->hasDiin = true;
|
||||
|
||||
// Now decode the chunks inside the DIIN chunk
|
||||
|
||||
long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
||||
seek(d->chunks[i].offset);
|
||||
|
||||
while(tell() + 12 <= diinChunkEnd) {
|
||||
ByteVector diinChunkName = readBlock(4);
|
||||
long long diinChunkSize = readBlock(8).toLongLong(bigEndian);
|
||||
|
||||
if(!isValidChunkID(diinChunkName)) {
|
||||
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(tell()) + diinChunkSize > diinChunkEnd) {
|
||||
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName
|
||||
+ "' has invalid size (larger than the DIIN chunk)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = diinChunkName;
|
||||
chunk.size = diinChunkSize;
|
||||
chunk.offset = tell();
|
||||
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
|
||||
chunk.padding = 0;
|
||||
offset_t uPosNotPadded = tell();
|
||||
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
else
|
||||
chunk.padding = 1;
|
||||
}
|
||||
d->childChunks[DIINChunk].push_back(chunk);
|
||||
}
|
||||
}
|
||||
else if(d->chunks[i].name == "ID3 " || d->chunks[i].name == "id3 ") {
|
||||
d->id3v2TagChunkID = d->chunks[i].name;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->chunks[i].offset,
|
||||
d->ID3v2FrameFactory));
|
||||
d->isID3InPropChunk = false;
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
if(d->childChunkIndex[PROPChunk] < 0) {
|
||||
debug("DSDIFF::File::read() -- no PROP chunk found");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read properties
|
||||
|
||||
unsigned int sampleRate = 0;
|
||||
unsigned short channels = 0;
|
||||
|
||||
for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) {
|
||||
if(d->childChunks[PROPChunk][i].name == "ID3 " ||
|
||||
d->childChunks[PROPChunk][i].name == "id3 ") {
|
||||
if(d->hasID3v2) {
|
||||
d->duplicateID3V2chunkIndex = i;
|
||||
// ID3V2 tag has already been found at root level
|
||||
continue;
|
||||
}
|
||||
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset,
|
||||
d->ID3v2FrameFactory));
|
||||
d->isID3InPropChunk = true;
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else if(d->childChunks[PROPChunk][i].name == "FS ") {
|
||||
// Sample rate
|
||||
seek(d->childChunks[PROPChunk][i].offset);
|
||||
sampleRate = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
}
|
||||
else if(d->childChunks[PROPChunk][i].name == "CHNL") {
|
||||
// Channels
|
||||
seek(d->childChunks[PROPChunk][i].offset);
|
||||
channels = readBlock(2).toShort(0, bigEndian);
|
||||
}
|
||||
}
|
||||
|
||||
// Read title & artist from DIIN chunk
|
||||
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, true);
|
||||
|
||||
if(d->hasDiin) {
|
||||
for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) {
|
||||
if(d->childChunks[DIINChunk][i].name == "DITI") {
|
||||
seek(d->childChunks[DIINChunk][i].offset);
|
||||
unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
if(titleStrLength <= d->childChunks[DIINChunk][i].size) {
|
||||
ByteVector titleStr = readBlock(titleStrLength);
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setTitle(titleStr);
|
||||
}
|
||||
}
|
||||
else if(d->childChunks[DIINChunk][i].name == "DIAR") {
|
||||
seek(d->childChunks[DIINChunk][i].offset);
|
||||
unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
if(artistStrLength <= d->childChunks[DIINChunk][i].size) {
|
||||
ByteVector artistStr = readBlock(artistStrLength);
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setArtist(artistStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(readProperties) {
|
||||
if(lengthDSDSamplesTimeChannels == 0) {
|
||||
// DST compressed signal : need to compute length of DSD uncompressed frames
|
||||
if(dstFrameRate > 0)
|
||||
lengthDSDSamplesTimeChannels = static_cast<unsigned long long>(dstNumFrames) *
|
||||
static_cast<unsigned long long>(sampleRate) /
|
||||
static_cast<unsigned long long>(dstFrameRate);
|
||||
else
|
||||
lengthDSDSamplesTimeChannels = 0;
|
||||
}
|
||||
else {
|
||||
// In DSD uncompressed files, the read number of samples is the total for each channel
|
||||
if(channels > 0)
|
||||
lengthDSDSamplesTimeChannels /= channels;
|
||||
}
|
||||
int bitrate = 0;
|
||||
if(lengthDSDSamplesTimeChannels > 0)
|
||||
bitrate = static_cast<int>(
|
||||
(audioDataSizeinBytes * 8 * sampleRate) / lengthDSDSamplesTimeChannels / 1000);
|
||||
|
||||
d->properties = std::make_unique<Properties>(sampleRate, channels,
|
||||
lengthDSDSamplesTimeChannels, bitrate, propertiesStyle);
|
||||
}
|
||||
|
||||
if(!ID3v2Tag()) {
|
||||
d->tag.access<ID3v2::Tag>(ID3v2Index, true, d->ID3v2FrameFactory);
|
||||
// By default, ID3 chunk is at root level
|
||||
d->isID3InPropChunk = false;
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
|
||||
unsigned long long offset, unsigned long replace,
|
||||
unsigned int leadingPadding)
|
||||
{
|
||||
ByteVector combined;
|
||||
if(leadingPadding)
|
||||
combined.append(ByteVector(leadingPadding, '\x00'));
|
||||
|
||||
combined.append(name);
|
||||
combined.append(ByteVector::fromLongLong(data.size(), d->endianness == BigEndian));
|
||||
combined.append(data);
|
||||
if((data.size() & 0x01) != 0)
|
||||
combined.append('\x00');
|
||||
|
||||
insert(combined, offset, replace);
|
||||
}
|
||||
292
taglib/dsdiff/dsdifffile.h
Normal file
292
taglib/dsdiff/dsdifffile.h
Normal file
@@ -0,0 +1,292 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSDIFFFILE_H
|
||||
#define TAGLIB_DSDIFFFILE_H
|
||||
|
||||
#include "rifffile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "dsdiffproperties.h"
|
||||
#include "dsdiffdiintag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of DSDIFF metadata
|
||||
|
||||
/*!
|
||||
* This is implementation of DSDIFF metadata.
|
||||
*
|
||||
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
|
||||
* chunk as well as properties from the file.
|
||||
* Description of the DSDIFF format is available
|
||||
* at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
|
||||
* DSDIFF standard does not explicitly specify the ID3V2 chunk
|
||||
* It can be found at the root level, but also sometimes inside the PROP chunk
|
||||
* In addition, title and artist info are stored as part of the standard
|
||||
*/
|
||||
|
||||
namespace DSDIFF {
|
||||
|
||||
//! An implementation of TagLib::File with DSDIFF specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for DSDIFF files to the
|
||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
||||
* the abstract TagLib::File API as well as providing some additional
|
||||
* information specific to DSDIFF files.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
* 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
|
||||
* 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 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);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN
|
||||
* tags. The ID3v2 tag is given priority in reading the information -- if
|
||||
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
|
||||
* the information from the ID3v2 tag will be returned.
|
||||
*
|
||||
* If you would like more granular control over the content of the tags,
|
||||
* with the concession of generality, use the tag-type specific calls.
|
||||
*
|
||||
* \note As this tag is not implemented as an ID3v2 tag or a DIIN tag,
|
||||
* but a union of the two this pointer may not be cast to the specific
|
||||
* tag types.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
* \see DIINTag()
|
||||
*/
|
||||
Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Returns the ID3V2 Tag for this file.
|
||||
*
|
||||
* \note This always returns a valid pointer regardless of whether or not
|
||||
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the
|
||||
* file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see hasID3v2Tag()
|
||||
*/
|
||||
ID3v2::Tag *ID3v2Tag(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Returns the DSDIFF DIIN Tag for this file
|
||||
*
|
||||
*/
|
||||
DSDIFF::DIIN::Tag *DIINTag(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This method forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the AIFF::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
|
||||
* will duplicate its content into the other tag. This returns true
|
||||
* if saving was successful.
|
||||
*
|
||||
* If neither exists or if both tags are empty, this will strip the tags
|
||||
* from the file.
|
||||
*
|
||||
* This is the same as calling save(AllTags);
|
||||
*
|
||||
* If you would like more granular control over the content of the tags,
|
||||
* with the concession of generality, use paramaterized save call below.
|
||||
*
|
||||
* \see save(int tags)
|
||||
*/
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Save the file. If \a strip is specified, it is possible to choose if
|
||||
* tags not specified in \a tags should be stripped from the file or
|
||||
* retained. With \a version, it is possible to specify whether ID3v2.4
|
||||
* or ID3v2.3 should be used.
|
||||
*/
|
||||
bool save(int tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4);
|
||||
|
||||
/*!
|
||||
* This will strip the tags that match the OR-ed together TagTypes from the
|
||||
* file. By default it strips all tags. It returns true if the tags are
|
||||
* successfully stripped.
|
||||
*
|
||||
* \note This will update the file immediately.
|
||||
*/
|
||||
void strip(int tags = AllTags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has the DSDIFF
|
||||
* title and artist tags.
|
||||
*
|
||||
* \see DIINTag()
|
||||
*/
|
||||
bool hasDIINTag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a DSDIFF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
protected:
|
||||
enum Endianness { BigEndian, LittleEndian };
|
||||
|
||||
File(FileName file, Endianness endianness);
|
||||
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);
|
||||
|
||||
/*!
|
||||
* Sets the data for the the specified chunk at root level to \a data.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setRootChunkData(unsigned int i, const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Sets the data for the root-level chunk \a name to \a data.
|
||||
* If a root-level chunk with the given name already exists
|
||||
* it will be overwritten, otherwise it will be
|
||||
* created after the existing chunks.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setRootChunkData(const ByteVector &name, const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Sets the data for the the specified child chunk to \a data.
|
||||
*
|
||||
* If data is null, then remove the chunk
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setChildChunkData(unsigned int i, const ByteVector &data,
|
||||
unsigned int childChunkNum);
|
||||
|
||||
/*!
|
||||
* Sets the data for the child chunk \a name to \a data. If a chunk with
|
||||
* the given name already exists it will be overwritten, otherwise it will
|
||||
* be created after the existing chunks inside child chunk.
|
||||
*
|
||||
* If data is null, then remove the chunks with \a name name
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setChildChunkData(const ByteVector &name, const ByteVector &data,
|
||||
unsigned int childChunkNum);
|
||||
|
||||
void updateRootChunksStructure(unsigned int startingChunk);
|
||||
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
void writeChunk(const ByteVector &name, const ByteVector &data,
|
||||
unsigned long long offset, unsigned long replace = 0,
|
||||
unsigned int leadingPadding = 0);
|
||||
|
||||
class FilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace DSDIFF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
101
taglib/dsdiff/dsdiffproperties.cpp
Normal file
101
taglib/dsdiff/dsdiffproperties.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsdiffproperties.h"
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSDIFF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int sampleWidth { 0 };
|
||||
unsigned long long sampleCount { 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSDIFF::Properties::Properties(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>())
|
||||
{
|
||||
d->channels = channels;
|
||||
d->sampleCount = samplesCount;
|
||||
d->sampleWidth = 1;
|
||||
d->sampleRate = sampleRate;
|
||||
d->bitrate = bitrate;
|
||||
d->length = d->sampleRate > 0
|
||||
? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5)
|
||||
: 0;
|
||||
}
|
||||
|
||||
DSDIFF::Properties::~Properties() = default;
|
||||
|
||||
int DSDIFF::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::bitsPerSample() const
|
||||
{
|
||||
return d->sampleWidth;
|
||||
}
|
||||
|
||||
long long DSDIFF::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
79
taglib/dsdiff/dsdiffproperties.h
Normal file
79
taglib/dsdiff/dsdiffproperties.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSDIFFPROPERTIES_H
|
||||
#define TAGLIB_DSDIFFPROPERTIES_H
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSDIFF {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for DSDIFF
|
||||
|
||||
/*!
|
||||
* This reads the data from an DSDIFF stream found in the AudioProperties
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of DSDIFF::Properties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
Properties(const unsigned int sampleRate, const unsigned short channels,
|
||||
const unsigned long long samplesCount, const int bitrate,
|
||||
ReadStyle style);
|
||||
|
||||
/*!
|
||||
* Destroys this DSDIFF::Properties instance.
|
||||
*/
|
||||
~Properties() override;
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
int lengthInSeconds() const override;
|
||||
int lengthInMilliseconds() const override;
|
||||
int bitrate() const override;
|
||||
int sampleRate() const override;
|
||||
int channels() const override;
|
||||
|
||||
int bitsPerSample() const;
|
||||
long long sampleCount() const;
|
||||
|
||||
private:
|
||||
class PropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace DSDIFF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
232
taglib/dsf/dsffile.cpp
Normal file
232
taglib/dsf/dsffile.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsffile.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tagutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
||||
|
||||
class DSF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(ID3v2::FrameFactory *frameFactory)
|
||||
: ID3v2FrameFactory(frameFactory ? frameFactory
|
||||
: ID3v2::FrameFactory::instance())
|
||||
{
|
||||
}
|
||||
|
||||
~FilePrivate() = default;
|
||||
|
||||
FilePrivate(const FilePrivate &) = delete;
|
||||
FilePrivate &operator=(const FilePrivate &) = delete;
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
long long fileSize = 0;
|
||||
long long metadataOffset = 0;
|
||||
std::unique_ptr<Properties> properties;
|
||||
std::unique_ptr<ID3v2::Tag> tag;
|
||||
};
|
||||
|
||||
bool DSF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A DSF file has to start with "DSD "
|
||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
||||
return id.startsWith("DSD ");
|
||||
}
|
||||
|
||||
DSF::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::~File() = default;
|
||||
|
||||
ID3v2::Tag *DSF::File::tag() const
|
||||
{
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap DSF::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
PropertyMap DSF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
DSF::Properties *DSF::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool DSF::File::save()
|
||||
{
|
||||
return save(ID3v2::v4);
|
||||
}
|
||||
|
||||
bool DSF::File::save(ID3v2::Version version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("DSF::File::save() - Cannot save to a read only file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Three things must be updated: the file size, the tag data, and the metadata offset
|
||||
|
||||
if(d->tag->isEmpty()) {
|
||||
long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize;
|
||||
|
||||
// Update the file size
|
||||
if(d->fileSize != newFileSize) {
|
||||
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
|
||||
d->fileSize = newFileSize;
|
||||
}
|
||||
|
||||
// Update the metadata offset to 0 since there is no longer a tag
|
||||
if(d->metadataOffset) {
|
||||
insert(ByteVector::fromLongLong(0ULL, false), 20, 8);
|
||||
d->metadataOffset = 0;
|
||||
}
|
||||
|
||||
// Delete the old tag
|
||||
truncate(newFileSize);
|
||||
}
|
||||
else {
|
||||
ByteVector tagData = d->tag->render(version);
|
||||
|
||||
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
|
||||
long long newFileSize = newMetadataOffset + tagData.size();
|
||||
long long oldTagSize = d->fileSize - newMetadataOffset;
|
||||
|
||||
// Update the file size
|
||||
if(d->fileSize != newFileSize) {
|
||||
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
|
||||
d->fileSize = newFileSize;
|
||||
}
|
||||
|
||||
// Update the metadata offset
|
||||
if(d->metadataOffset != newMetadataOffset) {
|
||||
insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8);
|
||||
d->metadataOffset = newMetadataOffset;
|
||||
}
|
||||
|
||||
// Delete the old tag and write the new one
|
||||
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DSF::File::read(AudioProperties::ReadStyle propertiesStyle)
|
||||
{
|
||||
if(!isOpen())
|
||||
return;
|
||||
|
||||
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
|
||||
// The file format is not chunked in the sense of a RIFF File, though
|
||||
|
||||
// DSD chunk
|
||||
ByteVector chunkName = readBlock(4);
|
||||
if(chunkName != "DSD ") {
|
||||
debug("DSF::File::read() -- Not a DSF file.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
long long dsdHeaderSize = readBlock(8).toLongLong(false);
|
||||
|
||||
// Integrity check
|
||||
if(dsdHeaderSize != 28) {
|
||||
debug("DSF::File::read() -- File is corrupted, wrong DSD header size");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->fileSize = readBlock(8).toLongLong(false);
|
||||
|
||||
// File is malformed or corrupted, allow trailing garbage
|
||||
if(d->fileSize > length()) {
|
||||
debug("DSF::File::read() -- File is corrupted wrong length");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->metadataOffset = readBlock(8).toLongLong(false);
|
||||
|
||||
// File is malformed or corrupted
|
||||
if(d->metadataOffset > d->fileSize) {
|
||||
debug("DSF::File::read() -- Invalid metadata offset.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Format chunk
|
||||
chunkName = readBlock(4);
|
||||
if(chunkName != "fmt ") {
|
||||
debug("DSF::File::read() -- Missing 'fmt ' chunk.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
long long fmtHeaderSize = readBlock(8).toLongLong(false);
|
||||
if(fmtHeaderSize != 52) {
|
||||
debug("DSF::File::read() -- File is corrupted, wrong FMT header size");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->properties = std::make_unique<Properties>(readBlock(fmtHeaderSize), propertiesStyle);
|
||||
|
||||
// Skip the data chunk
|
||||
|
||||
// A metadata offset of 0 indicates the absence of an ID3v2 tag
|
||||
if(d->metadataOffset == 0)
|
||||
d->tag = std::make_unique<ID3v2::Tag>(nullptr, 0, d->ID3v2FrameFactory);
|
||||
else
|
||||
d->tag = std::make_unique<ID3v2::Tag>(this, d->metadataOffset,
|
||||
d->ID3v2FrameFactory);
|
||||
}
|
||||
136
taglib/dsf/dsffile.h
Normal file
136
taglib/dsf/dsffile.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSFFILE_H
|
||||
#define TAGLIB_DSFFILE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
|
||||
#include "dsfproperties.h"
|
||||
|
||||
#include "id3v2tag.h"
|
||||
|
||||
namespace TagLib {
|
||||
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);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
|
||||
/*!
|
||||
* Returns the ID3v2 Tag for this file.
|
||||
*/
|
||||
ID3v2::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the DSF::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*/
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* \a version specifies the ID3v2 version to be used for writing tags.
|
||||
*/
|
||||
bool save(ID3v2::Version version);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a DSF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
void read(AudioProperties::ReadStyle propertiesStyle);
|
||||
|
||||
class FilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace DSF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
134
taglib/dsf/dsfproperties.cpp
Normal file
134
taglib/dsf/dsfproperties.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "dsfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() = default;
|
||||
~PropertiesPrivate() = default;
|
||||
|
||||
PropertiesPrivate(const PropertiesPrivate &) = delete;
|
||||
PropertiesPrivate &operator=(const PropertiesPrivate &) = delete;
|
||||
|
||||
// Nomenclature is from DSF file format specification
|
||||
unsigned int formatVersion = 0;
|
||||
unsigned int formatID = 0;
|
||||
unsigned int channelType = 0;
|
||||
unsigned int channelNum = 0;
|
||||
unsigned int samplingFrequency = 0;
|
||||
unsigned int bitsPerSample = 0;
|
||||
long long sampleCount = 0;
|
||||
unsigned int blockSizePerChannel = 0;
|
||||
|
||||
// Computed
|
||||
unsigned int bitrate = 0;
|
||||
unsigned int length = 0;
|
||||
};
|
||||
|
||||
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
read(data);
|
||||
}
|
||||
|
||||
DSF::Properties::~Properties() = default;
|
||||
|
||||
int DSF::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int DSF::Properties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int DSF::Properties::sampleRate() const
|
||||
{
|
||||
return d->samplingFrequency;
|
||||
}
|
||||
|
||||
int DSF::Properties::channels() const
|
||||
{
|
||||
return d->channelNum;
|
||||
}
|
||||
|
||||
int DSF::Properties::formatVersion() const
|
||||
{
|
||||
return d->formatVersion;
|
||||
}
|
||||
|
||||
int DSF::Properties::formatID() const
|
||||
{
|
||||
return d->formatID;
|
||||
}
|
||||
|
||||
int DSF::Properties::channelType() const
|
||||
{
|
||||
return d->channelType;
|
||||
}
|
||||
|
||||
int DSF::Properties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
long long DSF::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
int DSF::Properties::blockSizePerChannel() const
|
||||
{
|
||||
return d->blockSizePerChannel;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSF::Properties::read(const ByteVector &data)
|
||||
{
|
||||
d->formatVersion = data.toUInt(0U,false);
|
||||
d->formatID = data.toUInt(4U,false);
|
||||
d->channelType = data.toUInt(8U,false);
|
||||
d->channelNum = data.toUInt(12U,false);
|
||||
d->samplingFrequency = data.toUInt(16U,false);
|
||||
d->bitsPerSample = data.toUInt(20U,false);
|
||||
d->sampleCount = data.toLongLong(24U,false);
|
||||
d->blockSizePerChannel = data.toUInt(32U,false);
|
||||
|
||||
d->bitrate = static_cast<unsigned int>(
|
||||
(d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0 + 0.5);
|
||||
d->length = d->samplingFrequency > 0
|
||||
? static_cast<unsigned int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5)
|
||||
: 0;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Tsuda Kageyu
|
||||
email : tsuda.kageyu@gmail.com
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@@ -23,74 +23,50 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#ifndef TAGLIB_DSFPROPERTIES_H
|
||||
#define TAGLIB_DSFPROPERTIES_H
|
||||
|
||||
#include "trefcounter.h"
|
||||
#include <memory>
|
||||
|
||||
#if defined(HAVE_GCC_ATOMIC)
|
||||
# define ATOMIC_INT int
|
||||
# define ATOMIC_INC(x) __sync_add_and_fetch(&(x), 1)
|
||||
# define ATOMIC_DEC(x) __sync_sub_and_fetch(&(x), 1)
|
||||
#elif defined(HAVE_WIN_ATOMIC)
|
||||
# if !defined(NOMINMAX)
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# define ATOMIC_INT long
|
||||
# define ATOMIC_INC(x) InterlockedIncrement(&x)
|
||||
# define ATOMIC_DEC(x) InterlockedDecrement(&x)
|
||||
#elif defined(HAVE_MAC_ATOMIC)
|
||||
# include <libkern/OSAtomic.h>
|
||||
# define ATOMIC_INT int32_t
|
||||
# define ATOMIC_INC(x) OSAtomicIncrement32Barrier(&x)
|
||||
# define ATOMIC_DEC(x) OSAtomicDecrement32Barrier(&x)
|
||||
#elif defined(HAVE_IA64_ATOMIC)
|
||||
# include <ia64intrin.h>
|
||||
# define ATOMIC_INT int
|
||||
# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
|
||||
# define ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
|
||||
#else
|
||||
# define ATOMIC_INT int
|
||||
# define ATOMIC_INC(x) (++x)
|
||||
# define ATOMIC_DEC(x) (--x)
|
||||
#endif
|
||||
#include "taglib_export.h"
|
||||
#include "tbytevector.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
namespace TagLib {
|
||||
namespace DSF {
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties {
|
||||
public:
|
||||
Properties(const ByteVector &data, ReadStyle style);
|
||||
~Properties() override;
|
||||
|
||||
class RefCounter::RefCounterPrivate
|
||||
{
|
||||
public:
|
||||
RefCounterPrivate() :
|
||||
refCount(1) {}
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
volatile ATOMIC_INT refCount;
|
||||
};
|
||||
int lengthInMilliseconds() const override;
|
||||
int bitrate() const override;
|
||||
int sampleRate() const override;
|
||||
int channels() const override;
|
||||
|
||||
RefCounter::RefCounter() :
|
||||
d(new RefCounterPrivate())
|
||||
{
|
||||
}
|
||||
int formatVersion() const;
|
||||
int formatID() const;
|
||||
|
||||
RefCounter::~RefCounter()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
/*!
|
||||
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels,
|
||||
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels
|
||||
*/
|
||||
int channelType() const;
|
||||
int bitsPerSample() const;
|
||||
long long sampleCount() const;
|
||||
int blockSizePerChannel() const;
|
||||
|
||||
void RefCounter::ref()
|
||||
{
|
||||
ATOMIC_INC(d->refCount);
|
||||
}
|
||||
private:
|
||||
void read(const ByteVector &data);
|
||||
|
||||
bool RefCounter::deref()
|
||||
{
|
||||
return (ATOMIC_DEC(d->refCount) == 0);
|
||||
}
|
||||
|
||||
int RefCounter::count() const
|
||||
{
|
||||
return static_cast<int>(d->refCount);
|
||||
}
|
||||
class PropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace DSF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
@@ -27,40 +27,50 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tfilestream.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <trefcounter.h>
|
||||
|
||||
#include "fileref.h"
|
||||
#include "asffile.h"
|
||||
#include "mpegfile.h"
|
||||
#include "vorbisfile.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 <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "tfilestream.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tvariant.h"
|
||||
#include "tdebug.h"
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#include "apefile.h"
|
||||
#include "modfile.h"
|
||||
#include "s3mfile.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 "wavpackfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FileRef::FileTypeResolver::FileTypeResolverPrivate
|
||||
{
|
||||
};
|
||||
|
||||
class FileRef::StreamTypeResolver::StreamTypeResolverPrivate
|
||||
{
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
typedef List<const FileRef::FileTypeResolver *> ResolverList;
|
||||
ResolverList fileTypeResolvers;
|
||||
List<const FileRef::FileTypeResolver *> fileTypeResolvers;
|
||||
|
||||
// Detect the file type by user-defined resolvers.
|
||||
|
||||
@@ -68,36 +78,33 @@ namespace
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(::strlen(fileName) == 0 && ::wcslen(fileName) == 0)
|
||||
return 0;
|
||||
if(::wcslen(fileName) == 0)
|
||||
return nullptr;
|
||||
#else
|
||||
if(::strlen(fileName) == 0)
|
||||
return 0;
|
||||
return nullptr;
|
||||
#endif
|
||||
ResolverList::ConstIterator it = fileTypeResolvers.begin();
|
||||
for(; it != fileTypeResolvers.end(); ++it) {
|
||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
|
||||
File *file = resolver->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
File *detectByResolvers(IOStream* stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
for(ResolverList::ConstIterator it = fileTypeResolvers.begin();
|
||||
it != fileTypeResolvers.end(); ++it) {
|
||||
if(const FileRef::StreamTypeResolver *streamResolver =
|
||||
dynamic_cast<const FileRef::StreamTypeResolver*>(*it)) {
|
||||
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 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Detect the file type based on the file extension.
|
||||
@@ -121,18 +128,26 @@ namespace
|
||||
// that a default file type resolver is created.
|
||||
|
||||
if(ext.isEmpty())
|
||||
return 0;
|
||||
return nullptr;
|
||||
|
||||
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
|
||||
|
||||
File *file = 0;
|
||||
File *file = nullptr;
|
||||
|
||||
if(ext == "MP3")
|
||||
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
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, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "MPC")
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WV")
|
||||
@@ -162,6 +177,10 @@ namespace
|
||||
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.
|
||||
|
||||
@@ -171,7 +190,7 @@ namespace
|
||||
delete file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Detect the file type based on the actual content of the stream.
|
||||
@@ -179,16 +198,16 @@ namespace
|
||||
File *detectByContent(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = 0;
|
||||
File *file = nullptr;
|
||||
|
||||
if(MPEG::File::isSupported(stream))
|
||||
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Vorbis::File::isSupported(stream))
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::FLAC::File::isSupported(stream))
|
||||
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(FLAC::File::isSupported(stream))
|
||||
file = new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(MPC::File::isSupported(stream))
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(WavPack::File::isSupported(stream))
|
||||
@@ -209,6 +228,10 @@ 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);
|
||||
|
||||
// isSupported() only does a quick check, so double check the file here.
|
||||
|
||||
@@ -218,96 +241,40 @@ namespace
|
||||
delete file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Internal function that supports FileRef::create().
|
||||
// This looks redundant, but necessary in order not to change the previous
|
||||
// behavior of FileRef::create().
|
||||
|
||||
File* createInternal(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
|
||||
#ifdef _WIN32
|
||||
const String s = fileName.toString();
|
||||
#else
|
||||
const String s(fileName);
|
||||
#endif
|
||||
|
||||
String ext;
|
||||
const int pos = s.rfind(".");
|
||||
if(pos != -1)
|
||||
ext = s.substr(pos + 1).upper();
|
||||
|
||||
if(ext.isEmpty())
|
||||
return 0;
|
||||
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGA") {
|
||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
||||
File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
if(ext == "FLAC")
|
||||
return new FLAC::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "MPC")
|
||||
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WV")
|
||||
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "SPX")
|
||||
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OPUS")
|
||||
return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "TTA")
|
||||
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
||||
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WMA" || ext == "ASF")
|
||||
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
||||
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WAV")
|
||||
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "APE")
|
||||
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "S3M")
|
||||
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "IT")
|
||||
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class FileRef::FileRefPrivate : public RefCounter
|
||||
class FileRef::FileRefPrivate
|
||||
{
|
||||
public:
|
||||
FileRefPrivate() :
|
||||
file(0),
|
||||
stream(0) {}
|
||||
|
||||
~FileRefPrivate() {
|
||||
FileRefPrivate() = default;
|
||||
~FileRefPrivate()
|
||||
{
|
||||
delete file;
|
||||
delete stream;
|
||||
}
|
||||
|
||||
File *file;
|
||||
IOStream *stream;
|
||||
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 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -315,55 +282,93 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FileRef::FileRef() :
|
||||
d(new FileRefPrivate())
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
FileRef::FileRef(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(new FileRefPrivate())
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
{
|
||||
parse(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(new FileRefPrivate())
|
||||
FileRef::FileRef(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
{
|
||||
parse(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(File *file) :
|
||||
d(new FileRefPrivate())
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
{
|
||||
d->file = file;
|
||||
}
|
||||
|
||||
FileRef::FileRef(const FileRef &ref) :
|
||||
d(ref.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
FileRef::FileRef(const FileRef &) = default;
|
||||
|
||||
FileRef::~FileRef()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
}
|
||||
FileRef::~FileRef() = default;
|
||||
|
||||
Tag *FileRef::tag() const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::tag() - Called without a valid file.");
|
||||
return 0;
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return nullptr;
|
||||
}
|
||||
return d->file->tag();
|
||||
}
|
||||
|
||||
PropertyMap FileRef::properties() const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return PropertyMap();
|
||||
}
|
||||
return d->file->properties();
|
||||
}
|
||||
|
||||
void FileRef::removeUnsupportedProperties(const StringList& properties)
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return;
|
||||
}
|
||||
return d->file->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap FileRef::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return PropertyMap();
|
||||
}
|
||||
return d->file->setProperties(properties);
|
||||
}
|
||||
|
||||
StringList FileRef::complexPropertyKeys() const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return StringList();
|
||||
}
|
||||
return d->file->complexPropertyKeys();
|
||||
}
|
||||
|
||||
List<VariantMap> FileRef::complexProperties(const String &key) const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return List<VariantMap>();
|
||||
}
|
||||
return d->file->complexProperties(key);
|
||||
}
|
||||
|
||||
bool FileRef::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return false;
|
||||
}
|
||||
return d->file->setComplexProperties(key, value);
|
||||
}
|
||||
|
||||
AudioProperties *FileRef::audioProperties() const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::audioProperties() - Called without a valid file.");
|
||||
return 0;
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return nullptr;
|
||||
}
|
||||
return d->file->audioProperties();
|
||||
}
|
||||
@@ -375,8 +380,7 @@ File *FileRef::file() const
|
||||
|
||||
bool FileRef::save()
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::save() - Called without a valid file.");
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return false;
|
||||
}
|
||||
return d->file->save();
|
||||
@@ -388,6 +392,11 @@ const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::Fil
|
||||
return resolver;
|
||||
}
|
||||
|
||||
void FileRef::clearFileTypeResolvers() // static
|
||||
{
|
||||
fileTypeResolvers.clear();
|
||||
}
|
||||
|
||||
StringList FileRef::defaultFileExtensions()
|
||||
{
|
||||
StringList l;
|
||||
@@ -397,10 +406,12 @@ StringList FileRef::defaultFileExtensions()
|
||||
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,22 +434,21 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("s3m");
|
||||
l.append("it");
|
||||
l.append("xm");
|
||||
l.append("dsf");
|
||||
l.append("dff");
|
||||
l.append("dsdiff"); // alias for "dff"
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
bool FileRef::isNull() const
|
||||
{
|
||||
return (!d->file || !d->file->isValid());
|
||||
return d->isNull();
|
||||
}
|
||||
|
||||
FileRef &FileRef::operator=(const FileRef &ref)
|
||||
{
|
||||
FileRef(ref).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
FileRef &FileRef::operator=(const FileRef &) = default;
|
||||
|
||||
void FileRef::swap(FileRef &ref)
|
||||
void FileRef::swap(FileRef &ref) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
@@ -455,12 +465,6 @@ bool FileRef::operator!=(const FileRef &ref) const
|
||||
return (ref.d->file != d->file);
|
||||
}
|
||||
|
||||
File *FileRef::create(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) // static
|
||||
{
|
||||
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -490,7 +494,7 @@ void FileRef::parse(FileName fileName, bool readAudioProperties,
|
||||
// Stream have to be closed here if failed to resolve file types.
|
||||
|
||||
delete d->stream;
|
||||
d->stream = 0;
|
||||
d->stream = nullptr;
|
||||
}
|
||||
|
||||
void FileRef::parse(IOStream *stream, bool readAudioProperties,
|
||||
@@ -518,3 +522,9 @@ void FileRef::parse(IOStream *stream, bool readAudioProperties,
|
||||
|
||||
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileTypeResolver::FileTypeResolver() = default;
|
||||
FileRef::FileTypeResolver::~FileTypeResolver() = default;
|
||||
|
||||
FileRef::StreamTypeResolver::StreamTypeResolver() = default;
|
||||
FileRef::StreamTypeResolver::~StreamTypeResolver() = default;
|
||||
|
||||
137
taglib/fileref.h
137
taglib/fileref.h
@@ -63,7 +63,7 @@ namespace TagLib {
|
||||
//! A class for pluggable file type resolution.
|
||||
|
||||
/*!
|
||||
* This class is used to add extend TagLib's very basic file name based file
|
||||
* This class is used to extend TagLib's very basic file name based file
|
||||
* type resolution.
|
||||
*
|
||||
* This can be accomplished with:
|
||||
@@ -91,12 +91,20 @@ namespace TagLib {
|
||||
|
||||
class TAGLIB_EXPORT FileTypeResolver
|
||||
{
|
||||
TAGLIB_IGNORE_MISSING_DESTRUCTOR
|
||||
public:
|
||||
FileTypeResolver();
|
||||
/*!
|
||||
* Destroys this FileTypeResolver instance.
|
||||
*/
|
||||
virtual ~FileTypeResolver() = 0;
|
||||
|
||||
FileTypeResolver(const FileTypeResolver &) = delete;
|
||||
FileTypeResolver &operator=(const FileTypeResolver &) = delete;
|
||||
|
||||
/*!
|
||||
* This method must be overridden to provide an additional file type
|
||||
* resolver. If the resolver is able to determine the file type it should
|
||||
* return a valid File object; if not it should return 0.
|
||||
* return a valid File object; if not it should return nullptr.
|
||||
*
|
||||
* \note The created file is then owned by the FileRef and should not be
|
||||
* deleted. Deletion will happen automatically when the FileRef passes
|
||||
@@ -106,16 +114,32 @@ 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
|
||||
{
|
||||
TAGLIB_IGNORE_MISSING_DESTRUCTOR
|
||||
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;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -168,7 +192,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this FileRef instance.
|
||||
*/
|
||||
virtual ~FileRef();
|
||||
~FileRef();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to represented file's tag.
|
||||
@@ -183,6 +207,84 @@ namespace TagLib {
|
||||
*/
|
||||
Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Exports the tags of the file as dictionary mapping (human readable) tag
|
||||
* names (uppercase Strings) to StringLists of tag values. Calls this
|
||||
* method on the wrapped File instance.
|
||||
* For each metadata object of the file that could not be parsed into the PropertyMap
|
||||
* format, the returned map's unsupportedData() list will contain one entry identifying
|
||||
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
|
||||
* to remove (a subset of) them.
|
||||
* For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2
|
||||
* tag) only the most "modern" one will be exported (ID3v2 in this case).
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties, or a subset of them, from the file's metadata.
|
||||
* The parameter \a properties must contain only entries from
|
||||
* properties().unsupportedData().
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
|
||||
/*!
|
||||
* Sets the tags of the wrapped File to those specified in \a properties.
|
||||
* If some value(s) could not be written to the specific metadata format,
|
||||
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
|
||||
* indicating that no problems occurred.
|
||||
* With file types that support several tag formats (for instance, MP3 files can have
|
||||
* ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one
|
||||
* (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't
|
||||
* be taken into account for the return value of this function.
|
||||
* See the documentation of the subclass implementations for detailed descriptions.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Get the keys of complex properties, i.e. properties which cannot be
|
||||
* represented simply by a string.
|
||||
* Because such properties might be expensive to fetch, there are separate
|
||||
* operations to get the available keys - which is expected to be cheap -
|
||||
* and getting and setting the property values.
|
||||
* Calls the method on the wrapped File, which collects the keys from one
|
||||
* or more of its tags.
|
||||
*/
|
||||
StringList complexPropertyKeys() const;
|
||||
|
||||
/*!
|
||||
* Get the complex properties for a given \a key.
|
||||
* In order to be flexible for different metadata formats, the properties
|
||||
* are represented as variant maps. Despite this dynamic nature, some
|
||||
* degree of standardization should be achieved between formats:
|
||||
*
|
||||
* - PICTURE
|
||||
* - data: ByteVector with picture data
|
||||
* - description: String with description
|
||||
* - pictureType: String with type as specified for ID3v2,
|
||||
* e.g. "Front Cover", "Back Cover", "Band"
|
||||
* - mimeType: String with image format, e.g. "image/jpeg"
|
||||
* - optionally more information found in the tag, such as
|
||||
* "width", "height", "numColors", "colorDepth" int values
|
||||
* in FLAC pictures
|
||||
* - GENERALOBJECT
|
||||
* - data: ByteVector with object data
|
||||
* - description: String with description
|
||||
* - fileName: String with file name
|
||||
* - mimeType: String with MIME type
|
||||
* - this is currently only implemented for ID3v2 GEOB frames
|
||||
*
|
||||
* Calls the method on the wrapped File, which gets the properties from one
|
||||
* or more of its tags.
|
||||
*/
|
||||
List<VariantMap> complexProperties(const String &key) const;
|
||||
|
||||
/*!
|
||||
* Set all complex properties for a given \a key using variant maps as
|
||||
* \a value with the same format as returned by complexProperties().
|
||||
* An empty list as \a value removes all complex properties for \a key.
|
||||
*/
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value);
|
||||
|
||||
/*!
|
||||
* Returns the audio properties for this FileRef. If no audio properties
|
||||
* were read then this will returns a null pointer.
|
||||
@@ -225,6 +327,11 @@ 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
|
||||
@@ -256,7 +363,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Exchanges the content of the FileRef by the content of \a ref.
|
||||
*/
|
||||
void swap(FileRef &ref);
|
||||
void swap(FileRef &ref) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns true if this FileRef and \a ref point to the same File object.
|
||||
@@ -269,27 +376,13 @@ namespace TagLib {
|
||||
*/
|
||||
bool operator!=(const FileRef &ref) const;
|
||||
|
||||
/*!
|
||||
* A simple implementation of file type guessing. If \a readAudioProperties
|
||||
* is true then the audio properties will be read using
|
||||
* \a audioPropertiesStyle. If \a readAudioProperties is false then
|
||||
* \a audioPropertiesStyle will be ignored.
|
||||
*
|
||||
* \note You generally shouldn't use this method, but instead the constructor
|
||||
* directly.
|
||||
*
|
||||
* \deprecated Use FileRef(FileName, bool, AudioProperties::ReadStyle).
|
||||
*/
|
||||
static File *create(FileName fileName,
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
private:
|
||||
void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
|
||||
void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
|
||||
|
||||
class FileRefPrivate;
|
||||
FileRefPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<FileRefPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,21 +23,19 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
#include <tstring.h>
|
||||
#include <tlist.h>
|
||||
#include <tdebug.h>
|
||||
#include <tagunion.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include <id3v2header.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <id3v1tag.h>
|
||||
#include <xiphcomment.h>
|
||||
|
||||
#include "flacpicture.h"
|
||||
#include "flacfile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#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 "flacmetadatablock.h"
|
||||
#include "flacunknownmetadatablock.h"
|
||||
|
||||
@@ -45,10 +43,6 @@ using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
typedef List<FLAC::MetadataBlock *> BlockList;
|
||||
typedef BlockList::Iterator BlockIterator;
|
||||
typedef BlockList::Iterator BlockConstIterator;
|
||||
|
||||
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
|
||||
|
||||
const long MinPaddingLength = 4096;
|
||||
@@ -61,38 +55,26 @@ class FLAC::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
|
||||
ID3v2FrameFactory(frameFactory),
|
||||
ID3v2Location(-1),
|
||||
ID3v2OriginalSize(0),
|
||||
ID3v1Location(-1),
|
||||
properties(0),
|
||||
flacStart(0),
|
||||
streamStart(0),
|
||||
scanned(false)
|
||||
ID3v2FrameFactory(frameFactory)
|
||||
{
|
||||
blocks.setAutoDelete(true);
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete properties;
|
||||
}
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
long ID3v2Location;
|
||||
long ID3v2OriginalSize;
|
||||
offset_t ID3v2Location { -1 };
|
||||
long ID3v2OriginalSize { 0 };
|
||||
|
||||
long ID3v1Location;
|
||||
offset_t ID3v1Location { -1 };
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
Properties *properties;
|
||||
std::unique_ptr<Properties> properties;
|
||||
ByteVector xiphCommentData;
|
||||
BlockList blocks;
|
||||
List<FLAC::MetadataBlock *> blocks;
|
||||
|
||||
long flacStart;
|
||||
long streamStart;
|
||||
bool scanned;
|
||||
offset_t flacStart { 0 };
|
||||
offset_t streamStart { 0 };
|
||||
bool scanned { false };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -111,9 +93,12 @@ bool FLAC::File::isSupported(IOStream *stream)
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
FLAC::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>(
|
||||
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -122,7 +107,18 @@ FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate(frameFactory))
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(
|
||||
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -131,16 +127,13 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate(frameFactory))
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
FLAC::File::~File() = default;
|
||||
|
||||
TagLib::Tag *FLAC::File::tag() const
|
||||
{
|
||||
@@ -162,9 +155,74 @@ 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
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool FLAC::File::save()
|
||||
@@ -189,7 +247,7 @@ bool FLAC::File::save()
|
||||
|
||||
MetadataBlock *commentBlock =
|
||||
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end();) {
|
||||
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
|
||||
if((*it)->code() == MetadataBlock::VorbisComment) {
|
||||
// Remove the old Vorbis Comment block
|
||||
delete *it;
|
||||
@@ -199,7 +257,7 @@ bool FLAC::File::save()
|
||||
if(commentBlock && (*it)->code() == MetadataBlock::Picture) {
|
||||
// Set the new Vorbis Comment block before the first picture block
|
||||
d->blocks.insert(it, commentBlock);
|
||||
commentBlock = 0;
|
||||
commentBlock = nullptr;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
@@ -209,18 +267,18 @@ bool FLAC::File::save()
|
||||
// Render data for the metadata blocks
|
||||
|
||||
ByteVector data;
|
||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
ByteVector blockData = (*it)->render();
|
||||
for(const auto &block : std::as_const(d->blocks)) {
|
||||
ByteVector blockData = block->render();
|
||||
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
|
||||
blockHeader[0] = (*it)->code();
|
||||
blockHeader[0] = block->code();
|
||||
data.append(blockHeader);
|
||||
data.append(blockData);
|
||||
}
|
||||
|
||||
// Compute the amount of padding, and append that to data.
|
||||
|
||||
long originalLength = d->streamStart - d->flacStart;
|
||||
long paddingLength = originalLength - data.size() - 4;
|
||||
offset_t originalLength = d->streamStart - d->flacStart;
|
||||
offset_t paddingLength = originalLength - data.size() - 4;
|
||||
|
||||
if(paddingLength <= 0) {
|
||||
paddingLength = MinPaddingLength;
|
||||
@@ -228,15 +286,15 @@ bool FLAC::File::save()
|
||||
else {
|
||||
// Padding won't increase beyond 1% of the file size or 1MB.
|
||||
|
||||
long threshold = length() / 100;
|
||||
threshold = std::max(threshold, MinPaddingLength);
|
||||
threshold = std::min(threshold, MaxPaddingLegnth);
|
||||
offset_t threshold = length() / 100;
|
||||
threshold = std::max<offset_t>(threshold, MinPaddingLength);
|
||||
threshold = std::min<offset_t>(threshold, MaxPaddingLegnth);
|
||||
|
||||
if(paddingLength > threshold)
|
||||
paddingLength = MinPaddingLength;
|
||||
}
|
||||
|
||||
ByteVector paddingHeader = ByteVector::fromUInt(paddingLength);
|
||||
ByteVector paddingHeader = ByteVector::fromUInt(static_cast<unsigned int>(paddingLength));
|
||||
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
|
||||
data.append(paddingHeader);
|
||||
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
|
||||
@@ -317,7 +375,8 @@ bool FLAC::File::save()
|
||||
|
||||
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create);
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create,
|
||||
d->ID3v2FrameFactory);
|
||||
}
|
||||
|
||||
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
|
||||
@@ -330,29 +389,11 @@ Ogg::XiphComment *FLAC::File::xiphComment(bool create)
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create);
|
||||
}
|
||||
|
||||
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
|
||||
{
|
||||
d->ID3v2FrameFactory = factory;
|
||||
}
|
||||
|
||||
ByteVector FLAC::File::streamInfoData()
|
||||
{
|
||||
debug("FLAC::File::streamInfoData() -- This function is obsolete. Returning an empty ByteVector.");
|
||||
return ByteVector();
|
||||
}
|
||||
|
||||
long FLAC::File::streamLength()
|
||||
{
|
||||
debug("FLAC::File::streamLength() -- This function is obsolete. Returning zero.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<FLAC::Picture *> FLAC::File::pictureList()
|
||||
{
|
||||
List<Picture *> pictures;
|
||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
Picture *picture = dynamic_cast<Picture *>(*it);
|
||||
if(picture) {
|
||||
for(const auto &block : std::as_const(d->blocks)) {
|
||||
if(auto picture = dynamic_cast<Picture *>(block)) {
|
||||
pictures.append(picture);
|
||||
}
|
||||
}
|
||||
@@ -366,7 +407,7 @@ void FLAC::File::addPicture(Picture *picture)
|
||||
|
||||
void FLAC::File::removePicture(Picture *picture, bool del)
|
||||
{
|
||||
BlockIterator it = d->blocks.find(picture);
|
||||
auto it = d->blocks.find(picture);
|
||||
if(it != d->blocks.end())
|
||||
d->blocks.erase(it);
|
||||
|
||||
@@ -376,7 +417,7 @@ void FLAC::File::removePicture(Picture *picture, bool del)
|
||||
|
||||
void FLAC::File::removePictures()
|
||||
{
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
|
||||
for(auto it = d->blocks.begin(); it != d->blocks.end(); ) {
|
||||
if(dynamic_cast<Picture *>(*it)) {
|
||||
delete *it;
|
||||
it = d->blocks.erase(it);
|
||||
@@ -390,10 +431,10 @@ void FLAC::File::removePictures()
|
||||
void FLAC::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v1)
|
||||
d->tag.set(FlacID3v1Index, 0);
|
||||
d->tag.set(FlacID3v1Index, nullptr);
|
||||
|
||||
if(tags & ID3v2)
|
||||
d->tag.set(FlacID3v2Index, 0);
|
||||
d->tag.set(FlacID3v2Index, nullptr);
|
||||
|
||||
if(tags & XiphComment) {
|
||||
xiphComment()->removeAllFields();
|
||||
@@ -456,14 +497,14 @@ void FLAC::File::read(bool readProperties)
|
||||
|
||||
const ByteVector infoData = d->blocks.front()->render();
|
||||
|
||||
long streamLength;
|
||||
offset_t streamLength;
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location - d->streamStart;
|
||||
else
|
||||
streamLength = length() - d->streamStart;
|
||||
|
||||
d->properties = new Properties(infoData, streamLength);
|
||||
d->properties = std::make_unique<Properties>(infoData, streamLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +518,7 @@ void FLAC::File::scan()
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
long nextBlockOffset;
|
||||
offset_t nextBlockOffset;
|
||||
|
||||
if(d->ID3v2Location >= 0)
|
||||
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
|
||||
@@ -542,7 +583,7 @@ void FLAC::File::scan()
|
||||
return;
|
||||
}
|
||||
|
||||
MetadataBlock *block = 0;
|
||||
MetadataBlock *block = nullptr;
|
||||
|
||||
// Found the vorbis-comment
|
||||
if(blockType == MetadataBlock::VorbisComment) {
|
||||
@@ -555,7 +596,7 @@ void FLAC::File::scan()
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Picture) {
|
||||
FLAC::Picture *picture = new FLAC::Picture();
|
||||
auto picture = new FLAC::Picture();
|
||||
if(picture->parse(data)) {
|
||||
block = picture;
|
||||
}
|
||||
|
||||
@@ -26,11 +26,10 @@
|
||||
#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"
|
||||
|
||||
@@ -89,22 +88,23 @@ namespace TagLib {
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \deprecated This constructor will be dropped in favor of the one below
|
||||
* in a future version.
|
||||
* 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);
|
||||
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 and ID3v2 tag the frames will be created using
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
TAGLIB_DEPRECATED
|
||||
File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -116,12 +116,28 @@ namespace TagLib {
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* 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
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
TAGLIB_DEPRECATED
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -129,7 +145,10 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file. This will be a union of XiphComment,
|
||||
@@ -139,7 +158,7 @@ namespace TagLib {
|
||||
* \see ID3v1Tag()
|
||||
* \see XiphComment()
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
TagLib::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
@@ -147,9 +166,9 @@ namespace TagLib {
|
||||
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
|
||||
* converted to the PropertyMap.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &);
|
||||
void removeUnsupportedProperties(const StringList &) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
@@ -158,13 +177,30 @@ namespace TagLib {
|
||||
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
|
||||
* in the FLAC specification.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
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;
|
||||
|
||||
/*!
|
||||
* Returns the FLAC::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file. This will primarily save the XiphComment, but
|
||||
@@ -173,7 +209,7 @@ namespace TagLib {
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*/
|
||||
virtual bool save();
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v2 tag of the file.
|
||||
@@ -232,32 +268,6 @@ namespace TagLib {
|
||||
*/
|
||||
Ogg::XiphComment *xiphComment(bool create = false);
|
||||
|
||||
/*!
|
||||
* Set the ID3v2::FrameFactory to something other than the default. This
|
||||
* can be used to specify the way that ID3v2 frames will be interpreted
|
||||
* when
|
||||
*
|
||||
* \see ID3v2FrameFactory
|
||||
* \deprecated This value should be passed in via the constructor.
|
||||
*/
|
||||
TAGLIB_DEPRECATED void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
|
||||
/*!
|
||||
* Returns the block of data used by FLAC::Properties for parsing the
|
||||
* stream properties.
|
||||
*
|
||||
* \deprecated Always returns an empty vector.
|
||||
*/
|
||||
TAGLIB_DEPRECATED ByteVector streamInfoData(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns the length of the audio-stream, used by FLAC::Properties for
|
||||
* calculating the bitrate.
|
||||
*
|
||||
* \deprecated Always returns zero.
|
||||
*/
|
||||
TAGLIB_DEPRECATED long streamLength(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns a list of pictures attached to the FLAC file.
|
||||
*/
|
||||
@@ -328,14 +338,12 @@ namespace TagLib {
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties);
|
||||
void scan();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,25 +23,14 @@
|
||||
* 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()
|
||||
: d(0)
|
||||
{
|
||||
}
|
||||
|
||||
FLAC::MetadataBlock::~MetadataBlock()
|
||||
{
|
||||
}
|
||||
FLAC::MetadataBlock::MetadataBlock() = default;
|
||||
|
||||
FLAC::MetadataBlock::~MetadataBlock() = default;
|
||||
|
||||
@@ -38,6 +38,9 @@ namespace TagLib {
|
||||
MetadataBlock();
|
||||
virtual ~MetadataBlock();
|
||||
|
||||
MetadataBlock(const MetadataBlock &item) = delete;
|
||||
MetadataBlock &operator=(const MetadataBlock &item) = delete;
|
||||
|
||||
enum BlockType {
|
||||
StreamInfo = 0,
|
||||
Padding,
|
||||
@@ -59,11 +62,9 @@ namespace TagLib {
|
||||
virtual ByteVector render() const = 0;
|
||||
|
||||
private:
|
||||
MetadataBlock(const MetadataBlock &item);
|
||||
MetadataBlock &operator=(const MetadataBlock &item);
|
||||
|
||||
class MetadataBlockPrivate;
|
||||
MetadataBlockPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<MetadataBlockPrivate> d;
|
||||
};
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,48 +23,37 @@
|
||||
* 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:
|
||||
PicturePrivate() :
|
||||
type(FLAC::Picture::Other),
|
||||
width(0),
|
||||
height(0),
|
||||
colorDepth(0),
|
||||
numColors(0)
|
||||
{}
|
||||
|
||||
Type type;
|
||||
Type type { FLAC::Picture::Other };
|
||||
String mimeType;
|
||||
String description;
|
||||
int width;
|
||||
int height;
|
||||
int colorDepth;
|
||||
int numColors;
|
||||
int width { 0 };
|
||||
int height { 0 };
|
||||
int colorDepth { 0 };
|
||||
int numColors { 0 };
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::Picture::Picture() :
|
||||
d(new PicturePrivate())
|
||||
d(std::make_unique<PicturePrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
FLAC::Picture::Picture(const ByteVector &data) :
|
||||
d(new PicturePrivate())
|
||||
d(std::make_unique<PicturePrivate>())
|
||||
{
|
||||
parse(data);
|
||||
}
|
||||
|
||||
FLAC::Picture::~Picture()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
FLAC::Picture::~Picture() = default;
|
||||
|
||||
int FLAC::Picture::code() const
|
||||
{
|
||||
@@ -214,4 +203,3 @@ void FLAC::Picture::setData(const ByteVector &data)
|
||||
{
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "tlist.h"
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tpicturetype.h"
|
||||
#include "taglib_export.h"
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
@@ -41,54 +42,14 @@ namespace TagLib {
|
||||
/*!
|
||||
* This describes the function or content of the picture.
|
||||
*/
|
||||
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
|
||||
};
|
||||
DECLARE_PICTURE_TYPE_ENUM(Type)
|
||||
|
||||
Picture();
|
||||
Picture(const ByteVector &data);
|
||||
~Picture();
|
||||
~Picture() override;
|
||||
|
||||
Picture(const Picture &item) = delete;
|
||||
Picture &operator=(const Picture &item) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the type of the image.
|
||||
@@ -177,12 +138,12 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the FLAC metadata block type.
|
||||
*/
|
||||
int code() const;
|
||||
int code() const override;
|
||||
|
||||
/*!
|
||||
* Render the content to the FLAC picture block format.
|
||||
*/
|
||||
ByteVector render() const;
|
||||
ByteVector render() const override;
|
||||
|
||||
/*!
|
||||
* Parse the picture data in the FLAC picture block format.
|
||||
@@ -190,14 +151,12 @@ namespace TagLib {
|
||||
bool parse(const ByteVector &rawData);
|
||||
|
||||
private:
|
||||
Picture(const Picture &item);
|
||||
Picture &operator=(const Picture &item);
|
||||
|
||||
class PicturePrivate;
|
||||
PicturePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PicturePrivate> d;
|
||||
};
|
||||
|
||||
typedef List<Picture> PictureList;
|
||||
using PictureList = List<Picture>;
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
* 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;
|
||||
@@ -34,20 +35,12 @@ using namespace TagLib;
|
||||
class FLAC::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
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;
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
int channels { 0 };
|
||||
unsigned long long sampleFrames { 0 };
|
||||
ByteVector signature;
|
||||
};
|
||||
|
||||
@@ -55,34 +48,14 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) :
|
||||
FLAC::Properties::Properties(const ByteVector &data, offset_t streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
read(data, streamLength);
|
||||
}
|
||||
|
||||
FLAC::Properties::Properties(File *, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
debug("FLAC::Properties::Properties() - This constructor is no longer used.");
|
||||
}
|
||||
|
||||
FLAC::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int FLAC::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int FLAC::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
FLAC::Properties::~Properties() = default;
|
||||
|
||||
int FLAC::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
@@ -104,11 +77,6 @@ int FLAC::Properties::bitsPerSample() const
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
int FLAC::Properties::sampleWidth() const
|
||||
{
|
||||
return bitsPerSample();
|
||||
}
|
||||
|
||||
int FLAC::Properties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
@@ -128,7 +96,7 @@ ByteVector FLAC::Properties::signature() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
|
||||
{
|
||||
if(data.size() < 18) {
|
||||
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#ifndef TAGLIB_FLACPROPERTIES_H
|
||||
#define TAGLIB_FLACPROPERTIES_H
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
@@ -33,8 +34,6 @@ namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for FLAC
|
||||
|
||||
/*!
|
||||
@@ -49,62 +48,37 @@ namespace TagLib {
|
||||
* Create an instance of FLAC::Properties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
// BIC: switch to const reference
|
||||
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Create an instance of FLAC::Properties with the data read from the
|
||||
* FLAC::File \a file.
|
||||
*/
|
||||
// BIC: remove
|
||||
Properties(File *file, ReadStyle style = Average);
|
||||
Properties(const ByteVector &data, offset_t streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this FLAC::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated Use lengthInSeconds().
|
||||
*/
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample as read from the FLAC
|
||||
@@ -112,16 +86,6 @@ 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 Use bitsPerSample().
|
||||
*/
|
||||
TAGLIB_DEPRECATED int sampleWidth() const;
|
||||
|
||||
/*!
|
||||
* Return the number of sample frames.
|
||||
*/
|
||||
@@ -134,13 +98,11 @@ namespace TagLib {
|
||||
ByteVector signature() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read(const ByteVector &data, long streamLength);
|
||||
void read(const ByteVector &data, offset_t streamLength);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "flacunknownmetadatablock.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -33,23 +30,18 @@ using namespace TagLib;
|
||||
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
|
||||
{
|
||||
public:
|
||||
UnknownMetadataBlockPrivate() : code(0) {}
|
||||
|
||||
int code;
|
||||
int code { 0 };
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) :
|
||||
d(new UnknownMetadataBlockPrivate())
|
||||
d(std::make_unique<UnknownMetadataBlockPrivate>())
|
||||
{
|
||||
d->code = code;
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock() = default;
|
||||
|
||||
int FLAC::UnknownMetadataBlock::code() const
|
||||
{
|
||||
@@ -75,4 +67,3 @@ ByteVector FLAC::UnknownMetadataBlock::render() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,13 +36,16 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock
|
||||
{
|
||||
public:
|
||||
UnknownMetadataBlock(int blockType, const ByteVector &data);
|
||||
~UnknownMetadataBlock();
|
||||
UnknownMetadataBlock(int code, const ByteVector &data);
|
||||
~UnknownMetadataBlock() override;
|
||||
|
||||
UnknownMetadataBlock(const UnknownMetadataBlock &item) = delete;
|
||||
UnknownMetadataBlock &operator=(const UnknownMetadataBlock &item) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the FLAC metadata block type.
|
||||
*/
|
||||
int code() const;
|
||||
int code() const override;
|
||||
|
||||
/*!
|
||||
* Sets the FLAC metadata block type.
|
||||
@@ -62,14 +65,12 @@ namespace TagLib {
|
||||
/*!
|
||||
* Render the content of the block.
|
||||
*/
|
||||
ByteVector render() const;
|
||||
ByteVector render() const override;
|
||||
|
||||
private:
|
||||
UnknownMetadataBlock(const MetadataBlock &item);
|
||||
UnknownMetadataBlock &operator=(const MetadataBlock &item);
|
||||
|
||||
class UnknownMetadataBlockPrivate;
|
||||
UnknownMetadataBlockPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<UnknownMetadataBlockPrivate> d;
|
||||
};
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "itfile.h"
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "itfile.h"
|
||||
#include "tdebug.h"
|
||||
#include "modfileprivate.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "modfileprivate.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace IT;
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
IT::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(file),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -57,32 +57,19 @@ IT::File::File(FileName file, bool readProperties,
|
||||
IT::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(stream),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
IT::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
IT::File::~File() = default;
|
||||
|
||||
Mod::Tag *IT::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
PropertyMap IT::File::properties() const
|
||||
{
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
PropertyMap IT::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag.setProperties(properties);
|
||||
}
|
||||
|
||||
IT::Properties *IT::File::audioProperties() const
|
||||
{
|
||||
return &d->properties;
|
||||
@@ -162,7 +149,7 @@ bool IT::File::save()
|
||||
if(!readU16L(special))
|
||||
return false;
|
||||
|
||||
unsigned long fileSize = File::length();
|
||||
auto fileSize = static_cast<unsigned long>(File::length());
|
||||
if(special & Properties::MessageAttached) {
|
||||
seek(54);
|
||||
if(!readU16L(messageLength) || !readU32L(messageOffset))
|
||||
@@ -257,7 +244,7 @@ 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 it's values.
|
||||
// gets its values.
|
||||
if(static_cast<unsigned char>(pannings[i]) < 128 && volumes[i] > 0)
|
||||
++channels;
|
||||
}
|
||||
@@ -328,7 +315,7 @@ void IT::File::read(bool)
|
||||
comment.append(sampleName);
|
||||
}
|
||||
|
||||
if(message.size() > 0)
|
||||
if(!message.isEmpty())
|
||||
comment.append(message);
|
||||
d->tag.setComment(comment.toString("\n"));
|
||||
d->tag.setTrackerName("Impulse Tracker");
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#define TAGLIB_ITFILE_H
|
||||
|
||||
#include "tfile.h"
|
||||
#include "audioproperties.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
#include "modfilebase.h"
|
||||
#include "modtag.h"
|
||||
#include "itproperties.h"
|
||||
@@ -63,27 +63,18 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
Mod::Tag *tag() const;
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Forwards to Mod::Tag::properties().
|
||||
* BIC: will be removed once File::toDict() is made virtual
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Forwards to Mod::Tag::setProperties().
|
||||
* BIC: will be removed once File::setProperties() is made virtual
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
Mod::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Returns the IT::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
IT::Properties *audioProperties() const;
|
||||
IT::Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
@@ -91,17 +82,14 @@ namespace TagLib {
|
||||
*
|
||||
* \note Saving Impulse Tracker tags is not supported.
|
||||
*/
|
||||
bool save();
|
||||
|
||||
bool save() override;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace IT
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "itproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -32,77 +31,30 @@ using namespace IT;
|
||||
class IT::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
channels(0),
|
||||
lengthInPatterns(0),
|
||||
instrumentCount(0),
|
||||
sampleCount(0),
|
||||
patternCount(0),
|
||||
version(0),
|
||||
compatibleVersion(0),
|
||||
flags(0),
|
||||
special(0),
|
||||
globalVolume(0),
|
||||
mixVolume(0),
|
||||
tempo(0),
|
||||
bpmSpeed(0),
|
||||
panningSeparation(0),
|
||||
pitchWheelDepth(0)
|
||||
{
|
||||
}
|
||||
|
||||
int channels;
|
||||
unsigned short lengthInPatterns;
|
||||
unsigned short instrumentCount;
|
||||
unsigned short sampleCount;
|
||||
unsigned short patternCount;
|
||||
unsigned short version;
|
||||
unsigned short compatibleVersion;
|
||||
unsigned short flags;
|
||||
unsigned short special;
|
||||
unsigned char globalVolume;
|
||||
unsigned char mixVolume;
|
||||
unsigned char tempo;
|
||||
unsigned char bpmSpeed;
|
||||
unsigned char panningSeparation;
|
||||
unsigned char pitchWheelDepth;
|
||||
int channels { 0 };
|
||||
unsigned short lengthInPatterns { 0 };
|
||||
unsigned short instrumentCount { 0 };
|
||||
unsigned short sampleCount { 0 };
|
||||
unsigned short patternCount { 0 };
|
||||
unsigned short version { 0 };
|
||||
unsigned short compatibleVersion { 0 };
|
||||
unsigned short flags { 0 };
|
||||
unsigned short special { 0 };
|
||||
unsigned char globalVolume { 0 };
|
||||
unsigned char mixVolume { 0 };
|
||||
unsigned char tempo { 0 };
|
||||
unsigned char bpmSpeed { 0 };
|
||||
unsigned char panningSeparation { 0 };
|
||||
unsigned char pitchWheelDepth { 0 };
|
||||
};
|
||||
|
||||
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
|
||||
AudioProperties(propertiesStyle),
|
||||
d(new PropertiesPrivate())
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
IT::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int IT::Properties::length() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::lengthInSeconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::sampleRate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
IT::Properties::~Properties() = default;
|
||||
|
||||
int IT::Properties::channels() const
|
||||
{
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
namespace TagLib {
|
||||
namespace IT {
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties {
|
||||
friend class File;
|
||||
public:
|
||||
/*! Flag bits. */
|
||||
enum {
|
||||
@@ -53,53 +52,49 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
Properties(AudioProperties::ReadStyle propertiesStyle);
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
int length() const;
|
||||
int lengthInSeconds() const;
|
||||
int lengthInMilliseconds() const;
|
||||
int bitrate() const;
|
||||
int sampleRate() const;
|
||||
int channels() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
unsigned short lengthInPatterns() const;
|
||||
bool stereo() const;
|
||||
unsigned short instrumentCount() const;
|
||||
unsigned short sampleCount() const;
|
||||
unsigned short patternCount() const;
|
||||
unsigned short version() const;
|
||||
int channels() const override;
|
||||
|
||||
unsigned short lengthInPatterns() const;
|
||||
bool stereo() const;
|
||||
unsigned short instrumentCount() const;
|
||||
unsigned short sampleCount() const;
|
||||
unsigned short patternCount() const;
|
||||
unsigned short version() const;
|
||||
unsigned short compatibleVersion() const;
|
||||
unsigned short flags() const;
|
||||
unsigned short special() const;
|
||||
unsigned char globalVolume() const;
|
||||
unsigned char mixVolume() const;
|
||||
unsigned char tempo() const;
|
||||
unsigned char bpmSpeed() const;
|
||||
unsigned char panningSeparation() const;
|
||||
unsigned char pitchWheelDepth() const;
|
||||
unsigned short flags() const;
|
||||
unsigned short special() const;
|
||||
unsigned char globalVolume() const;
|
||||
unsigned char mixVolume() const;
|
||||
unsigned char tempo() const;
|
||||
unsigned char bpmSpeed() const;
|
||||
unsigned char panningSeparation() const;
|
||||
unsigned char pitchWheelDepth() const;
|
||||
|
||||
void setChannels(int channels);
|
||||
void setLengthInPatterns(unsigned short lengthInPatterns);
|
||||
void setInstrumentCount(unsigned short instrumentCount);
|
||||
void setSampleCount (unsigned short sampleCount);
|
||||
void setSampleCount(unsigned short sampleCount);
|
||||
void setPatternCount(unsigned short patternCount);
|
||||
void setVersion (unsigned short version);
|
||||
void setVersion(unsigned short version);
|
||||
void setCompatibleVersion(unsigned short compatibleVersion);
|
||||
void setFlags (unsigned short flags);
|
||||
void setSpecial (unsigned short special);
|
||||
void setFlags(unsigned short flags);
|
||||
void setSpecial(unsigned short special);
|
||||
void setGlobalVolume(unsigned char globalVolume);
|
||||
void setMixVolume (unsigned char mixVolume);
|
||||
void setTempo (unsigned char tempo);
|
||||
void setBpmSpeed (unsigned char bpmSpeed);
|
||||
void setMixVolume(unsigned char mixVolume);
|
||||
void setTempo(unsigned char tempo);
|
||||
void setBpmSpeed(unsigned char bpmSpeed);
|
||||
void setPanningSeparation(unsigned char panningSeparation);
|
||||
void setPitchWheelDepth (unsigned char pitchWheelDepth);
|
||||
void setPitchWheelDepth(unsigned char pitchWheelDepth);
|
||||
|
||||
private:
|
||||
Properties(const Properties&);
|
||||
Properties &operator=(const Properties&);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace IT
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "modfile.h"
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "tdebug.h"
|
||||
#include "modfileprivate.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "modfileprivate.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Mod;
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
Mod::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(file),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -57,16 +57,13 @@ Mod::File::File(FileName file, bool readProperties,
|
||||
Mod::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(stream),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
Mod::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
Mod::File::~File() = default;
|
||||
|
||||
Mod::Tag *Mod::File::tag() const
|
||||
{
|
||||
@@ -163,25 +160,30 @@ void Mod::File::read(bool)
|
||||
seek(0);
|
||||
READ_STRING(d->tag.setTitle, 20);
|
||||
|
||||
offset_t pos = 20;
|
||||
StringList comment;
|
||||
for(unsigned int i = 0; i < instruments; ++ i) {
|
||||
READ_STRING_AS(instrumentName, 22);
|
||||
// value in words, * 2 (<< 1) for bytes:
|
||||
READ_U16B_AS(sampleLength);
|
||||
// skip unused data
|
||||
pos += 22 + 2 + 1 + 1 + 2 + 2;
|
||||
seek(pos);
|
||||
|
||||
READ_BYTE_AS(fineTuneByte);
|
||||
int fineTune = fineTuneByte & 0xF;
|
||||
// > 7 means negative value
|
||||
if(fineTune > 7) fineTune -= 16;
|
||||
// // value in words, * 2 (<< 1) for bytes:
|
||||
// READ_U16B_AS(sampleLength);
|
||||
|
||||
READ_BYTE_AS(volume);
|
||||
if(volume > 64) volume = 64;
|
||||
// volume in decibels: 20 * log10(volume / 64)
|
||||
// READ_BYTE_AS(fineTuneByte);
|
||||
// int fineTune = fineTuneByte & 0xF;
|
||||
// // > 7 means negative value
|
||||
// if(fineTune > 7) fineTune -= 16;
|
||||
|
||||
// value in words, * 2 (<< 1) for bytes:
|
||||
READ_U16B_AS(repeatStart);
|
||||
// value in words, * 2 (<< 1) for bytes:
|
||||
READ_U16B_AS(repatLength);
|
||||
// READ_BYTE_AS(volume);
|
||||
// if(volume > 64) volume = 64;
|
||||
// // volume in decibels: 20 * log10(volume / 64)
|
||||
|
||||
// // value in words, * 2 (<< 1) for bytes:
|
||||
// READ_U16B_AS(repeatStart);
|
||||
// // value in words, * 2 (<< 1) for bytes:
|
||||
// READ_U16B_AS(repeatLength);
|
||||
|
||||
comment.append(instrumentName);
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#define TAGLIB_MODFILE_H
|
||||
|
||||
#include "tfile.h"
|
||||
#include "audioproperties.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
#include "modfilebase.h"
|
||||
#include "modtag.h"
|
||||
#include "modproperties.h"
|
||||
@@ -66,26 +66,29 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
Mod::Tag *tag() const;
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
Mod::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Forwards to Mod::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Forwards to Mod::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
/*!
|
||||
* Returns the Mod::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Mod::Properties *audioProperties() const;
|
||||
Mod::Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
@@ -93,16 +96,14 @@ namespace TagLib {
|
||||
*
|
||||
* \note Saving Protracker tags is not supported.
|
||||
*/
|
||||
bool save();
|
||||
bool save() override;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace Mod
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,13 +23,17 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "modfilebase.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Mod;
|
||||
|
||||
class Mod::FileBase::FileBasePrivate
|
||||
{
|
||||
};
|
||||
|
||||
Mod::FileBase::~FileBase() = default;
|
||||
|
||||
Mod::FileBase::FileBase(FileName file) : TagLib::File(file)
|
||||
{
|
||||
}
|
||||
@@ -41,7 +45,7 @@ Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
|
||||
void Mod::FileBase::writeString(const String &s, unsigned long size, char padding)
|
||||
{
|
||||
ByteVector data(s.data(String::Latin1));
|
||||
data.resize(size, padding);
|
||||
data.resize(static_cast<unsigned int>(size), padding);
|
||||
writeBlock(data);
|
||||
}
|
||||
|
||||
@@ -73,7 +77,7 @@ void Mod::FileBase::writeU16L(unsigned short number)
|
||||
|
||||
void Mod::FileBase::writeU32L(unsigned long number)
|
||||
{
|
||||
writeBlock(ByteVector::fromUInt(number, false));
|
||||
writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(number), false));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU16B(unsigned short number)
|
||||
@@ -83,7 +87,7 @@ void Mod::FileBase::writeU16B(unsigned short number)
|
||||
|
||||
void Mod::FileBase::writeU32B(unsigned long number)
|
||||
{
|
||||
writeBlock(ByteVector::fromUInt(number, true));
|
||||
writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(number), true));
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readByte(unsigned char &byte)
|
||||
|
||||
@@ -38,6 +38,12 @@ namespace TagLib {
|
||||
namespace Mod {
|
||||
class TAGLIB_EXPORT FileBase : public TagLib::File
|
||||
{
|
||||
public:
|
||||
~FileBase() override;
|
||||
|
||||
FileBase(const FileBase &) = delete;
|
||||
FileBase& operator=(const FileBase &) = delete;
|
||||
|
||||
protected:
|
||||
FileBase(FileName file);
|
||||
FileBase(IOStream *stream);
|
||||
@@ -55,6 +61,10 @@ namespace TagLib {
|
||||
bool readU32L(unsigned long &number);
|
||||
bool readU16B(unsigned short &number);
|
||||
bool readU32B(unsigned long &number);
|
||||
private:
|
||||
class FileBasePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FileBasePrivate> d;
|
||||
};
|
||||
} // namespace Mod
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,45 +23,45 @@
|
||||
#define TAGLIB_MODFILEPRIVATE_H
|
||||
|
||||
// some helper-macros only used internally by (s3m|it|xm)file.cpp
|
||||
#define READ_ASSERT(cond) \
|
||||
if(!(cond)) \
|
||||
{ \
|
||||
setValid(false); \
|
||||
return; \
|
||||
}
|
||||
#define READ_ASSERT(cond) \
|
||||
do { \
|
||||
if(!(cond)) { \
|
||||
setValid(false); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define READ(setter,type,read) \
|
||||
{ \
|
||||
type number; \
|
||||
READ_ASSERT(read(number)); \
|
||||
setter(number); \
|
||||
}
|
||||
#define READ(setter, type, read) \
|
||||
do { \
|
||||
type number; \
|
||||
READ_ASSERT(read(number)); \
|
||||
setter(number); \
|
||||
} while(0)
|
||||
|
||||
#define READ_BYTE(setter) READ(setter,unsigned char,readByte)
|
||||
#define READ_U16L(setter) READ(setter,unsigned short,readU16L)
|
||||
#define READ_U32L(setter) READ(setter,unsigned long,readU32L)
|
||||
#define READ_U16B(setter) READ(setter,unsigned short,readU16B)
|
||||
#define READ_U32B(setter) READ(setter,unsigned long,readU32B)
|
||||
#define READ_BYTE(setter) READ(setter, unsigned char, readByte)
|
||||
#define READ_U16L(setter) READ(setter, unsigned short, readU16L)
|
||||
#define READ_U32L(setter) READ(setter, unsigned long, readU32L)
|
||||
#define READ_U16B(setter) READ(setter, unsigned short, readU16B)
|
||||
#define READ_U32B(setter) READ(setter, unsigned long, readU32B)
|
||||
|
||||
#define READ_STRING(setter,size) \
|
||||
{ \
|
||||
String s; \
|
||||
READ_ASSERT(readString(s, size)); \
|
||||
setter(s); \
|
||||
}
|
||||
#define READ_STRING(setter, size) \
|
||||
do { \
|
||||
String s; \
|
||||
READ_ASSERT(readString(s, size)); \
|
||||
setter(s); \
|
||||
} while(0)
|
||||
|
||||
#define READ_AS(type,name,read) \
|
||||
type name = 0; \
|
||||
READ_ASSERT(read(name));
|
||||
#define READ_AS(type, name, read) \
|
||||
type name = 0; \
|
||||
READ_ASSERT(read(name))
|
||||
|
||||
#define READ_BYTE_AS(name) READ_AS(unsigned char,name,readByte)
|
||||
#define READ_U16L_AS(name) READ_AS(unsigned short,name,readU16L)
|
||||
#define READ_U32L_AS(name) READ_AS(unsigned long,name,readU32L)
|
||||
#define READ_U16B_AS(name) READ_AS(unsigned short,name,readU16B)
|
||||
#define READ_U32B_AS(name) READ_AS(unsigned long,name,readU32B)
|
||||
|
||||
#define READ_STRING_AS(name,size) \
|
||||
String name; \
|
||||
READ_ASSERT(readString(name, size));
|
||||
#define READ_BYTE_AS(name) READ_AS(unsigned char, name, readByte)
|
||||
#define READ_U16L_AS(name) READ_AS(unsigned short, name, readU16L)
|
||||
#define READ_U32L_AS(name) READ_AS(unsigned long, name, readU32L)
|
||||
#define READ_U16B_AS(name) READ_AS(unsigned short, name, readU16B)
|
||||
#define READ_U32B_AS(name) READ_AS(unsigned long, name, readU32B)
|
||||
|
||||
#define READ_STRING_AS(name, size) \
|
||||
String name; \
|
||||
READ_ASSERT(readString(name, size))
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "modproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -32,43 +31,18 @@ using namespace Mod;
|
||||
class Mod::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
channels(0),
|
||||
instrumentCount(0),
|
||||
lengthInPatterns(0)
|
||||
{
|
||||
}
|
||||
|
||||
int channels;
|
||||
unsigned int instrumentCount;
|
||||
unsigned char lengthInPatterns;
|
||||
int channels { 0 };
|
||||
unsigned int instrumentCount { 0 };
|
||||
unsigned char lengthInPatterns { 0 };
|
||||
};
|
||||
|
||||
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
|
||||
AudioProperties(propertiesStyle),
|
||||
d(new PropertiesPrivate())
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
Mod::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int Mod::Properties::length() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mod::Properties::lengthInSeconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mod::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
Mod::Properties::~Properties() = default;
|
||||
|
||||
int Mod::Properties::bitrate() const
|
||||
{
|
||||
|
||||
@@ -35,31 +35,27 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
Properties(AudioProperties::ReadStyle propertiesStyle);
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
int length() const;
|
||||
int lengthInSeconds() const;
|
||||
int lengthInMilliseconds() const;
|
||||
int bitrate() const;
|
||||
int sampleRate() const;
|
||||
int channels() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
unsigned int instrumentCount() const;
|
||||
int bitrate() const override;
|
||||
int sampleRate() const override;
|
||||
int channels() const override;
|
||||
|
||||
unsigned int instrumentCount() const;
|
||||
unsigned char lengthInPatterns() const;
|
||||
|
||||
void setChannels(int channels);
|
||||
|
||||
void setInstrumentCount(unsigned int sampleCount);
|
||||
void setInstrumentCount(unsigned int instrumentCount);
|
||||
void setLengthInPatterns(unsigned char lengthInPatterns);
|
||||
|
||||
private:
|
||||
friend class File;
|
||||
|
||||
Properties(const Properties&);
|
||||
Properties &operator=(const Properties&);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace Mod
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "modtag.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
@@ -34,24 +36,17 @@ using namespace Mod;
|
||||
class Mod::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
String title;
|
||||
String comment;
|
||||
String trackerName;
|
||||
};
|
||||
|
||||
Mod::Tag::Tag() :
|
||||
d(new TagPrivate())
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
Mod::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
Mod::Tag::~Tag() = default;
|
||||
|
||||
String Mod::Tag::title() const
|
||||
{
|
||||
@@ -140,34 +135,34 @@ PropertyMap Mod::Tag::properties() const
|
||||
|
||||
PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap properties(origProps);
|
||||
properties.removeEmpty();
|
||||
PropertyMap props(origProps);
|
||||
props.removeEmpty();
|
||||
StringList oneValueSet;
|
||||
if(properties.contains("TITLE")) {
|
||||
d->title = properties["TITLE"].front();
|
||||
if(props.contains("TITLE")) {
|
||||
d->title = props["TITLE"].front();
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
d->title.clear();
|
||||
|
||||
if(properties.contains("COMMENT")) {
|
||||
d->comment = properties["COMMENT"].front();
|
||||
if(props.contains("COMMENT")) {
|
||||
d->comment = props["COMMENT"].front();
|
||||
oneValueSet.append("COMMENT");
|
||||
} else
|
||||
d->comment.clear();
|
||||
|
||||
if(properties.contains("TRACKERNAME")) {
|
||||
d->trackerName = properties["TRACKERNAME"].front();
|
||||
if(props.contains("TRACKERNAME")) {
|
||||
d->trackerName = props["TRACKERNAME"].front();
|
||||
oneValueSet.append("TRACKERNAME");
|
||||
} else
|
||||
d->trackerName.clear();
|
||||
|
||||
// for each tag that has been set above, remove the first entry in the corresponding
|
||||
// value list. The others will be returned as unsupported by this format.
|
||||
for(StringList::ConstIterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
|
||||
if(properties[*it].size() == 1)
|
||||
properties.erase(*it);
|
||||
for(const auto &entry : std::as_const(oneValueSet)) {
|
||||
if(props[entry].size() == 1)
|
||||
props.erase(entry);
|
||||
else
|
||||
properties[*it].erase( properties[*it].begin() );
|
||||
props[entry].erase(props[entry].begin());
|
||||
}
|
||||
return properties;
|
||||
return props;
|
||||
}
|
||||
|
||||
@@ -46,45 +46,48 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
Tag();
|
||||
virtual ~Tag();
|
||||
~Tag() override;
|
||||
|
||||
Tag(const Tag &) = delete;
|
||||
Tag &operator=(const Tag &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String title() const;
|
||||
String title() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns String::null.
|
||||
*/
|
||||
virtual String artist() const;
|
||||
String artist() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns String::null.
|
||||
*/
|
||||
virtual String album() const;
|
||||
String album() const override;
|
||||
|
||||
/*!
|
||||
* Returns the track comment derived from the instrument/sample/pattern
|
||||
* names; if no comment is present in the tag String::null will be
|
||||
* returned.
|
||||
*/
|
||||
virtual String comment() const;
|
||||
String comment() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns String::null.
|
||||
*/
|
||||
virtual String genre() const;
|
||||
String genre() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns 0.
|
||||
*/
|
||||
virtual unsigned int year() const;
|
||||
unsigned int year() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns 0.
|
||||
*/
|
||||
virtual unsigned int track() const;
|
||||
unsigned int track() const override;
|
||||
|
||||
/*!
|
||||
* Returns the name of the tracker used to create/edit the module file.
|
||||
@@ -103,17 +106,17 @@ namespace TagLib {
|
||||
* Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20
|
||||
* characters.
|
||||
*/
|
||||
virtual void setTitle(const String &title);
|
||||
void setTitle(const String &title) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
virtual void setArtist(const String &artist);
|
||||
void setArtist(const String &artist) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
virtual void setAlbum(const String &album);
|
||||
void setAlbum(const String &album) override;
|
||||
|
||||
/*!
|
||||
* Sets the comment to \a comment. If \a comment is String::null then
|
||||
@@ -132,22 +135,22 @@ namespace TagLib {
|
||||
* Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22
|
||||
* characters.
|
||||
*/
|
||||
virtual void setComment(const String &comment);
|
||||
void setComment(const String &comment) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
virtual void setGenre(const String &genre);
|
||||
void setGenre(const String &genre) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
virtual void setYear(unsigned int year);
|
||||
void setYear(unsigned int year) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
virtual void setTrack(unsigned int track);
|
||||
void setTrack(unsigned int track) override;
|
||||
|
||||
/*!
|
||||
* Sets the tracker name to \a trackerName. If \a trackerName is
|
||||
@@ -165,7 +168,7 @@ namespace TagLib {
|
||||
* Implements the unified property interface -- export function.
|
||||
* Since the module tag is very limited, the exported map is as well.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
@@ -175,14 +178,12 @@ namespace TagLib {
|
||||
* all but the first will be contained in the returned map of unsupported
|
||||
* properties.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
};
|
||||
} // namespace Mod
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,140 +23,140 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "mp4atom.h"
|
||||
|
||||
#include <climits>
|
||||
#include <utility>
|
||||
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
const char *const MP4::Atom::containers[11] = {
|
||||
namespace {
|
||||
constexpr std::array containers {
|
||||
"moov", "udta", "mdia", "meta", "ilst",
|
||||
"stbl", "minf", "moof", "traf", "trak",
|
||||
"stsd"
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class MP4::Atom::AtomPrivate
|
||||
{
|
||||
public:
|
||||
explicit AtomPrivate(offset_t ofs) : offset(ofs) {}
|
||||
offset_t offset;
|
||||
offset_t length { 0 };
|
||||
TagLib::ByteVector name;
|
||||
AtomList children;
|
||||
};
|
||||
|
||||
MP4::Atom::Atom(File *file)
|
||||
: offset(file->tell())
|
||||
: d(std::make_unique<AtomPrivate>(file->tell()))
|
||||
{
|
||||
children.setAutoDelete(true);
|
||||
d->children.setAutoDelete(true);
|
||||
|
||||
ByteVector header = file->readBlock(8);
|
||||
if(header.size() != 8) {
|
||||
// The atom header must be 8 bytes long, otherwise there is either
|
||||
// trailing garbage or the file is truncated
|
||||
debug("MP4: Couldn't read 8 bytes of data for atom header");
|
||||
length = 0;
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
|
||||
length = header.toUInt();
|
||||
d->length = header.toUInt();
|
||||
|
||||
if(length == 0) {
|
||||
if(d->length == 0) {
|
||||
// The last atom which extends to the end of the file.
|
||||
length = file->length() - offset;
|
||||
d->length = file->length() - d->offset;
|
||||
}
|
||||
else if(length == 1) {
|
||||
else if(d->length == 1) {
|
||||
// The atom has a 64-bit length.
|
||||
const long long longLength = file->readBlock(8).toLongLong();
|
||||
if(longLength <= LONG_MAX) {
|
||||
// The actual length fits in long. That's always the case if long is 64-bit.
|
||||
length = static_cast<long>(longLength);
|
||||
d->length = static_cast<long>(longLength);
|
||||
}
|
||||
else {
|
||||
debug("MP4: 64-bit atoms are not supported");
|
||||
length = 0;
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(length < 8 || length > file->length() - offset) {
|
||||
if(d->length < 8 || d->length > file->length() - d->offset) {
|
||||
debug("MP4: Invalid atom size");
|
||||
length = 0;
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
|
||||
name = header.mid(4, 4);
|
||||
d->name = header.mid(4, 4);
|
||||
for(int i = 0; i < 4; ++i) {
|
||||
const char ch = name.at(i);
|
||||
const char ch = d->name.at(i);
|
||||
if((ch < ' ' || ch > '~') && ch != '\251') {
|
||||
debug("MP4: Invalid atom type");
|
||||
length = 0;
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < numContainers; i++) {
|
||||
if(name == containers[i]) {
|
||||
if(name == "meta") {
|
||||
long posAfterMeta = file->tell();
|
||||
ByteVector nextSize = file->readBlock(8).mid(4, 4);
|
||||
static const char *const metaChildrenNames[] = {
|
||||
"hdlr", "ilst", "mhdr", "ctry", "lang"
|
||||
for(auto c : containers) {
|
||||
if(d->name == c) {
|
||||
if(d->name == "meta") {
|
||||
offset_t posAfterMeta = file->tell();
|
||||
static constexpr std::array metaChildrenNames {
|
||||
"hdlr", "ilst", "mhdr", "ctry", "lang"
|
||||
};
|
||||
bool metaIsFullAtom = true;
|
||||
for(size_t j = 0;
|
||||
j < sizeof(metaChildrenNames) / sizeof(metaChildrenNames[0]);
|
||||
++j) {
|
||||
if(nextSize == metaChildrenNames[j]) {
|
||||
// meta is not a full atom (i.e. not followed by version, flags). It
|
||||
// is followed by the size and type of the first child atom.
|
||||
metaIsFullAtom = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// meta is not a full atom (i.e. not followed by version, flags). It
|
||||
// is followed by the size and type of the first child atom.
|
||||
auto metaIsFullAtom = std::none_of(metaChildrenNames.begin(), metaChildrenNames.end(),
|
||||
[nextSize = file->readBlock(8).mid(4, 4)](const auto &child) { return nextSize == child; });
|
||||
// Only skip next four bytes, which contain version and flags, if meta
|
||||
// is a full atom.
|
||||
file->seek(posAfterMeta + (metaIsFullAtom ? 4 : 0));
|
||||
}
|
||||
else if(name == "stsd") {
|
||||
else if(d->name == "stsd") {
|
||||
file->seek(8, File::Current);
|
||||
}
|
||||
while(file->tell() < offset + length) {
|
||||
MP4::Atom *child = new MP4::Atom(file);
|
||||
children.append(child);
|
||||
if(child->length == 0)
|
||||
while(file->tell() < d->offset + d->length) {
|
||||
auto child = new MP4::Atom(file);
|
||||
d->children.append(child);
|
||||
if(child->d->length == 0)
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
file->seek(offset + length);
|
||||
file->seek(d->offset + d->length);
|
||||
}
|
||||
|
||||
MP4::Atom::~Atom()
|
||||
{
|
||||
}
|
||||
MP4::Atom::~Atom() = default;
|
||||
|
||||
MP4::Atom *
|
||||
MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4)
|
||||
{
|
||||
if(name1 == 0) {
|
||||
if(name1 == nullptr) {
|
||||
return this;
|
||||
}
|
||||
for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
|
||||
if((*it)->name == name1) {
|
||||
return (*it)->find(name2, name3, name4);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
auto it = std::find_if(d->children.cbegin(), d->children.cend(),
|
||||
[&name1](Atom *child) { return child->d->name == name1; });
|
||||
return it != d->children.cend() ? (*it)->find(name2, name3, name4) : nullptr;
|
||||
}
|
||||
|
||||
MP4::AtomList
|
||||
MP4::Atom::findall(const char *name, bool recursive)
|
||||
{
|
||||
MP4::AtomList result;
|
||||
for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
|
||||
if((*it)->name == name) {
|
||||
result.append(*it);
|
||||
for(const auto &child : std::as_const(d->children)) {
|
||||
if(child->d->name == name) {
|
||||
result.append(child);
|
||||
}
|
||||
if(recursive) {
|
||||
result.append((*it)->findall(name, recursive));
|
||||
result.append(child->findall(name, recursive));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -166,58 +166,140 @@ bool
|
||||
MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3)
|
||||
{
|
||||
path.append(this);
|
||||
if(name1 == 0) {
|
||||
if(name1 == nullptr) {
|
||||
return true;
|
||||
}
|
||||
for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
|
||||
if((*it)->name == name1) {
|
||||
return (*it)->path(path, name2, name3);
|
||||
}
|
||||
auto it = std::find_if(d->children.cbegin(), d->children.cend(),
|
||||
[&name1](Atom *child) { return child->d->name == name1; });
|
||||
return it != d->children.cend() ? (*it)->path(path, name2, name3) : false;
|
||||
}
|
||||
|
||||
void MP4::Atom::addToOffset(offset_t delta)
|
||||
{
|
||||
d->offset += delta;
|
||||
}
|
||||
|
||||
void MP4::Atom::prependChild(Atom *atom)
|
||||
{
|
||||
d->children.prepend(atom);
|
||||
}
|
||||
|
||||
bool MP4::Atom::removeChild(Atom *meta)
|
||||
{
|
||||
auto it = d->children.find(meta);
|
||||
if(it != d->children.end()) {
|
||||
d->children.erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MP4::Atoms::Atoms(File *file)
|
||||
offset_t MP4::Atom::offset() const
|
||||
{
|
||||
atoms.setAutoDelete(true);
|
||||
return d->offset;
|
||||
}
|
||||
|
||||
offset_t MP4::Atom::length() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
const ByteVector &MP4::Atom::name() const
|
||||
{
|
||||
return d->name;
|
||||
}
|
||||
|
||||
const MP4::AtomList &MP4::Atom::children() const
|
||||
{
|
||||
return d->children;
|
||||
}
|
||||
|
||||
|
||||
class MP4::Atoms::AtomsPrivate
|
||||
{
|
||||
public:
|
||||
AtomList atoms;
|
||||
};
|
||||
|
||||
MP4::Atoms::Atoms(File *file) :
|
||||
d(std::make_unique<AtomsPrivate>())
|
||||
{
|
||||
d->atoms.setAutoDelete(true);
|
||||
|
||||
file->seek(0, File::End);
|
||||
long end = file->tell();
|
||||
offset_t end = file->tell();
|
||||
file->seek(0);
|
||||
while(file->tell() + 8 <= end) {
|
||||
MP4::Atom *atom = new MP4::Atom(file);
|
||||
atoms.append(atom);
|
||||
if (atom->length == 0)
|
||||
auto atom = new MP4::Atom(file);
|
||||
d->atoms.append(atom);
|
||||
if (atom->length() == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MP4::Atoms::~Atoms()
|
||||
{
|
||||
}
|
||||
MP4::Atoms::~Atoms() = default;
|
||||
|
||||
MP4::Atom *
|
||||
MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4)
|
||||
{
|
||||
for(AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
|
||||
if((*it)->name == name1) {
|
||||
return (*it)->find(name2, name3, name4);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(),
|
||||
[&name1](Atom *atom) { return atom->name() == name1; });
|
||||
return it != d->atoms.cend() ? (*it)->find(name2, name3, name4) : nullptr;
|
||||
}
|
||||
|
||||
MP4::AtomList
|
||||
MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4)
|
||||
{
|
||||
MP4::AtomList path;
|
||||
for(AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
|
||||
if((*it)->name == name1) {
|
||||
if(!(*it)->path(path, name2, name3, name4)) {
|
||||
path.clear();
|
||||
}
|
||||
return path;
|
||||
auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(),
|
||||
[&name1](Atom *atom) { return atom->name() == name1; });
|
||||
if(it != d->atoms.cend()) {
|
||||
if(!(*it)->path(path, name2, name3, name4)) {
|
||||
path.clear();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool checkValid(const MP4::AtomList &list)
|
||||
{
|
||||
return std::none_of(list.begin(), list.end(),
|
||||
[](const auto &a) { return a->length() == 0 || !checkValid(a->children()); });
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool MP4::Atoms::checkRootLevelAtoms()
|
||||
{
|
||||
bool moovValid = false;
|
||||
for(auto it = d->atoms.begin(); it != d->atoms.end(); ++it) {
|
||||
bool invalid = (*it)->length() == 0 || !checkValid((*it)->children());
|
||||
if(!moovValid && !invalid && (*it)->name() == "moov") {
|
||||
moovValid = true;
|
||||
}
|
||||
if(invalid) {
|
||||
if(moovValid && (*it)->name() != "moof") {
|
||||
// Only the root level atoms "moov" and (if present) "moof" are
|
||||
// modified. If they are valid, ignore following invalid root level
|
||||
// atoms as trailing garbage.
|
||||
while(it != d->atoms.end()) {
|
||||
delete *it;
|
||||
it = d->atoms.erase(it);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const MP4::AtomList &MP4::Atoms::atoms() const
|
||||
{
|
||||
return d->atoms;
|
||||
}
|
||||
|
||||
@@ -25,21 +25,22 @@
|
||||
|
||||
// This file is not part of the public API!
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#ifndef TAGLIB_MP4ATOM_H
|
||||
#define TAGLIB_MP4ATOM_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "tfile.h"
|
||||
#include "tlist.h"
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
namespace TagLib {
|
||||
namespace MP4 {
|
||||
class Atom;
|
||||
typedef TagLib::List<Atom *> AtomList;
|
||||
using AtomList = TagLib::List<Atom *>;
|
||||
|
||||
enum AtomDataType
|
||||
{
|
||||
enum AtomDataType {
|
||||
TypeImplicit = 0, // for use with tags for which no type needs to be indicated because only one type is allowed
|
||||
TypeUTF8 = 1, // without any count or null terminator
|
||||
TypeUTF16 = 2, // also known as UTF-16BE
|
||||
@@ -64,29 +65,37 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
struct AtomData {
|
||||
AtomData(AtomDataType type, ByteVector data) : type(type), locale(0), data(data) {}
|
||||
AtomData(AtomDataType type, const ByteVector &data) :
|
||||
type(type), data(data) { }
|
||||
AtomDataType type;
|
||||
int locale;
|
||||
int locale { 0 };
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
typedef TagLib::List<AtomData> AtomDataList;
|
||||
using AtomDataList = TagLib::List<AtomData>;
|
||||
|
||||
class TAGLIB_EXPORT Atom
|
||||
{
|
||||
public:
|
||||
Atom(File *file);
|
||||
~Atom();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
|
||||
Atom(const Atom &) = delete;
|
||||
Atom &operator=(const Atom &) = delete;
|
||||
Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr);
|
||||
bool path(AtomList &path, const char *name1, const char *name2 = nullptr, const char *name3 = nullptr);
|
||||
AtomList findall(const char *name, bool recursive = false);
|
||||
long offset;
|
||||
long length;
|
||||
TagLib::ByteVector name;
|
||||
AtomList children;
|
||||
void addToOffset(offset_t delta);
|
||||
void prependChild(Atom *atom);
|
||||
bool removeChild(Atom *meta);
|
||||
offset_t offset() const;
|
||||
offset_t length() const;
|
||||
const ByteVector &name() const;
|
||||
const AtomList &children() const;
|
||||
|
||||
private:
|
||||
static const int numContainers = 11;
|
||||
static const char *const containers[11];
|
||||
class AtomPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AtomPrivate> d;
|
||||
};
|
||||
|
||||
//! Root-level atoms
|
||||
@@ -95,12 +104,20 @@ namespace TagLib {
|
||||
public:
|
||||
Atoms(File *file);
|
||||
~Atoms();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
AtomList atoms;
|
||||
Atoms(const Atoms &) = delete;
|
||||
Atoms &operator=(const Atoms &) = delete;
|
||||
Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr);
|
||||
AtomList path(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr);
|
||||
bool checkRootLevelAtoms();
|
||||
const AtomList &atoms() const;
|
||||
|
||||
private:
|
||||
class AtomsPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AtomsPrivate> d;
|
||||
};
|
||||
} // namespace MP4
|
||||
} // namespace TagLib
|
||||
} // namespace MP4
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -23,20 +23,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include "mp4coverart.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class MP4::CoverArt::CoverArtPrivate : public RefCounter
|
||||
class MP4::CoverArt::CoverArtPrivate
|
||||
{
|
||||
public:
|
||||
CoverArtPrivate() :
|
||||
format(MP4::CoverArt::JPEG) {}
|
||||
|
||||
Format format;
|
||||
Format format { MP4::CoverArt::JPEG };
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
@@ -45,39 +39,24 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MP4::CoverArt::CoverArt(Format format, const ByteVector &data) :
|
||||
d(new CoverArtPrivate())
|
||||
d(std::make_shared<CoverArtPrivate>())
|
||||
{
|
||||
d->format = format;
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
MP4::CoverArt::CoverArt(const CoverArt &item) :
|
||||
d(item.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
MP4::CoverArt &
|
||||
MP4::CoverArt::operator=(const CoverArt &item)
|
||||
{
|
||||
CoverArt(item).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
MP4::CoverArt::CoverArt(const CoverArt &) = default;
|
||||
MP4::CoverArt &MP4::CoverArt::operator=(const CoverArt &) = default;
|
||||
|
||||
void
|
||||
MP4::CoverArt::swap(CoverArt &item)
|
||||
MP4::CoverArt::swap(CoverArt &item) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, item.d);
|
||||
}
|
||||
|
||||
MP4::CoverArt::~CoverArt()
|
||||
{
|
||||
if(d->deref()) {
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
MP4::CoverArt::~CoverArt() = default;
|
||||
|
||||
MP4::CoverArt::Format
|
||||
MP4::CoverArt::format() const
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
CoverArt(Format format, const ByteVector &data);
|
||||
~CoverArt();
|
||||
virtual ~CoverArt();
|
||||
|
||||
CoverArt(const CoverArt &item);
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Exchanges the content of the CoverArt by the content of \a item.
|
||||
*/
|
||||
void swap(CoverArt &item);
|
||||
void swap(CoverArt &item) noexcept;
|
||||
|
||||
//! Format of the image
|
||||
Format format() const;
|
||||
@@ -70,10 +70,11 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class CoverArtPrivate;
|
||||
CoverArtPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<CoverArtPrivate> d;
|
||||
};
|
||||
|
||||
typedef List<CoverArt> CoverArtList;
|
||||
using CoverArtList = List<CoverArt>;
|
||||
} // namespace MP4
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -23,52 +23,31 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "mp4atom.h"
|
||||
#include "mp4tag.h"
|
||||
#include "mp4file.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tagutils.h"
|
||||
|
||||
#include "mp4itemfactory.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool checkValid(const MP4::AtomList &list)
|
||||
{
|
||||
for(MP4::AtomList::ConstIterator it = list.begin(); it != list.end(); ++it) {
|
||||
|
||||
if((*it)->length == 0)
|
||||
return false;
|
||||
|
||||
if(!checkValid((*it)->children))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class MP4::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
tag(0),
|
||||
atoms(0),
|
||||
properties(0) {}
|
||||
|
||||
~FilePrivate()
|
||||
FilePrivate(MP4::ItemFactory *mp4ItemFactory)
|
||||
: itemFactory(mp4ItemFactory ? mp4ItemFactory
|
||||
: MP4::ItemFactory::instance())
|
||||
{
|
||||
delete atoms;
|
||||
delete tag;
|
||||
delete properties;
|
||||
}
|
||||
|
||||
MP4::Tag *tag;
|
||||
MP4::Atoms *atoms;
|
||||
MP4::Properties *properties;
|
||||
~FilePrivate() = default;
|
||||
|
||||
const ItemFactory *itemFactory;
|
||||
std::unique_ptr<MP4::Tag> tag;
|
||||
std::unique_ptr<MP4::Atoms> atoms;
|
||||
std::unique_ptr<MP4::Properties> properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -87,31 +66,29 @@ bool MP4::File::isSupported(IOStream *stream)
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
|
||||
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle,
|
||||
ItemFactory *itemFactory) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>(itemFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) :
|
||||
MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle,
|
||||
ItemFactory *itemFactory) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>(itemFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
MP4::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
MP4::File::~File() = default;
|
||||
|
||||
MP4::Tag *
|
||||
MP4::File::tag() const
|
||||
MP4::Tag *MP4::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap MP4::File::properties() const
|
||||
@@ -129,10 +106,9 @@ PropertyMap MP4::File::setProperties(const PropertyMap &properties)
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
MP4::Properties *
|
||||
MP4::File::audioProperties() const
|
||||
MP4::Properties *MP4::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -141,8 +117,8 @@ MP4::File::read(bool readProperties)
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
d->atoms = new Atoms(this);
|
||||
if(!checkValid(d->atoms->atoms)) {
|
||||
d->atoms = std::make_unique<Atoms>(this);
|
||||
if(!d->atoms->checkRootLevelAtoms()) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
@@ -153,9 +129,9 @@ MP4::File::read(bool readProperties)
|
||||
return;
|
||||
}
|
||||
|
||||
d->tag = new Tag(this, d->atoms);
|
||||
d->tag = std::make_unique<Tag>(this, d->atoms.get(), d->itemFactory);
|
||||
if(readProperties) {
|
||||
d->properties = new Properties(this, d->atoms);
|
||||
d->properties = std::make_unique<Properties>(this, d->atoms.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,5 +174,5 @@ MP4::File::strip(int tags)
|
||||
bool
|
||||
MP4::File::hasMP4Tag() const
|
||||
{
|
||||
return (d->atoms->find("moov", "udta", "meta", "ilst") != 0);
|
||||
return (d->atoms->find("moov", "udta", "meta", "ilst") != nullptr);
|
||||
}
|
||||
|
||||
@@ -26,16 +26,18 @@
|
||||
#ifndef TAGLIB_MP4FILE_H
|
||||
#define TAGLIB_MP4FILE_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tfile.h"
|
||||
#include "taglib_export.h"
|
||||
#include "mp4properties.h"
|
||||
#include "mp4tag.h"
|
||||
#include "tag.h"
|
||||
#include "mp4properties.h"
|
||||
|
||||
namespace TagLib {
|
||||
//! An implementation of MP4 (AAC, ALAC, ...) metadata
|
||||
namespace MP4 {
|
||||
class Atoms;
|
||||
class ItemFactory;
|
||||
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for MP4 files to the
|
||||
@@ -64,9 +66,12 @@ namespace TagLib {
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* The items will be created using \a itemFactory (default if null).
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
|
||||
Properties::ReadStyle audioPropertiesStyle = Properties::Average,
|
||||
ItemFactory *itemFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Constructs an MP4 file from \a stream. If \a readProperties is true the
|
||||
@@ -76,14 +81,20 @@ namespace TagLib {
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* The items will be created using \a itemFactory (default if null).
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
|
||||
Properties::ReadStyle audioPropertiesStyle = Properties::Average,
|
||||
ItemFactory *itemFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the MP4 tag of the file.
|
||||
@@ -95,35 +106,35 @@ namespace TagLib {
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*/
|
||||
Tag *tag() const;
|
||||
Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the MP4 audio properties for this file.
|
||||
*/
|
||||
Properties *audioProperties() const;
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*/
|
||||
bool save();
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* This will strip the tags that match the OR-ed together TagTypes from the
|
||||
@@ -153,7 +164,8 @@ namespace TagLib {
|
||||
void read(bool readProperties);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace MP4
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,22 +23,15 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include "mp4item.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class MP4::Item::ItemPrivate : public RefCounter
|
||||
class MP4::Item::ItemPrivate
|
||||
{
|
||||
public:
|
||||
ItemPrivate() :
|
||||
valid(true),
|
||||
atomDataType(TypeUndefined) {}
|
||||
|
||||
bool valid;
|
||||
AtomDataType atomDataType;
|
||||
bool valid { true };
|
||||
AtomDataType atomDataType { TypeUndefined };
|
||||
union {
|
||||
bool m_bool;
|
||||
int m_int;
|
||||
@@ -53,89 +46,75 @@ public:
|
||||
};
|
||||
|
||||
MP4::Item::Item() :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->valid = false;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const Item &item) :
|
||||
d(item.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
MP4::Item &
|
||||
MP4::Item::operator=(const Item &item)
|
||||
{
|
||||
Item(item).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
MP4::Item::Item(const Item &) = default;
|
||||
MP4::Item &MP4::Item::operator=(const Item &) = default;
|
||||
|
||||
void
|
||||
MP4::Item::swap(Item &item)
|
||||
MP4::Item::swap(Item &item) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, item.d);
|
||||
}
|
||||
|
||||
MP4::Item::~Item()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
}
|
||||
MP4::Item::~Item() = default;
|
||||
|
||||
MP4::Item::Item(bool value) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->m_bool = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(int value) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->m_int = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(unsigned char value) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->m_byte = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(unsigned int value) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->m_uint = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(long long value) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->m_longlong = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(int value1, int value2) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->m_intPair.first = value1;
|
||||
d->m_intPair.second = value2;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const ByteVectorList &value) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->m_byteVectorList = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const StringList &value) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->m_stringList = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const MP4::CoverArtList &value) :
|
||||
d(new ItemPrivate())
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->m_coverArtList = value;
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#define TAGLIB_MP4ITEM_H
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "mp4coverart.h"
|
||||
#include "taglib_export.h"
|
||||
#include "mp4coverart.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace MP4 {
|
||||
@@ -50,16 +50,16 @@ namespace TagLib {
|
||||
/*!
|
||||
* Exchanges the content of the Item by the content of \a item.
|
||||
*/
|
||||
void swap(Item &item);
|
||||
void swap(Item &item) noexcept;
|
||||
|
||||
~Item();
|
||||
virtual ~Item();
|
||||
|
||||
Item(int value);
|
||||
Item(unsigned char value);
|
||||
Item(unsigned int value);
|
||||
Item(long long value);
|
||||
Item(bool value);
|
||||
Item(int first, int second);
|
||||
Item(int value1, int value2);
|
||||
Item(const StringList &value);
|
||||
Item(const ByteVectorList &value);
|
||||
Item(const CoverArtList &value);
|
||||
@@ -81,8 +81,11 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class ItemPrivate;
|
||||
ItemPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<ItemPrivate> d;
|
||||
};
|
||||
|
||||
using ItemMap = TagLib::Map<String, Item>;
|
||||
} // namespace MP4
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
779
taglib/mp4/mp4itemfactory.cpp
Normal file
779
taglib/mp4/mp4itemfactory.cpp
Normal file
@@ -0,0 +1,779 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2023 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "mp4itemfactory.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
#include "id3v1genres.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace MP4;
|
||||
|
||||
class ItemFactory::ItemFactoryPrivate
|
||||
{
|
||||
public:
|
||||
NameHandlerMap handlerTypeForName;
|
||||
Map<ByteVector, String> propertyKeyForName;
|
||||
Map<String, ByteVector> nameForPropertyKey;
|
||||
};
|
||||
|
||||
ItemFactory ItemFactory::factory;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ItemFactory *ItemFactory::instance()
|
||||
{
|
||||
return &factory;
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseItem(
|
||||
const Atom *atom, const ByteVector &data) const
|
||||
{
|
||||
auto handlerType = handlerTypeForName(atom->name());
|
||||
switch(handlerType) {
|
||||
case ItemHandlerType::Unknown:
|
||||
break;
|
||||
case ItemHandlerType::FreeForm:
|
||||
return parseFreeForm(atom, data);
|
||||
case ItemHandlerType::IntPair:
|
||||
case ItemHandlerType::IntPairNoTrailing:
|
||||
return parseIntPair(atom, data);
|
||||
case ItemHandlerType::Bool:
|
||||
return parseBool(atom, data);
|
||||
case ItemHandlerType::Int:
|
||||
return parseInt(atom, data);
|
||||
case ItemHandlerType::TextOrInt:
|
||||
return parseTextOrInt(atom, data);
|
||||
case ItemHandlerType::UInt:
|
||||
return parseUInt(atom, data);
|
||||
case ItemHandlerType::LongLong:
|
||||
return parseLongLong(atom, data);
|
||||
case ItemHandlerType::Byte:
|
||||
return parseByte(atom, data);
|
||||
case ItemHandlerType::Gnre:
|
||||
return parseGnre(atom, data);
|
||||
case ItemHandlerType::Covr:
|
||||
return parseCovr(atom, data);
|
||||
case ItemHandlerType::TextImplicit:
|
||||
return parseText(atom, data, -1);
|
||||
case ItemHandlerType::Text:
|
||||
return parseText(atom, data);
|
||||
}
|
||||
return {atom->name(), Item()};
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderItem(
|
||||
const String &itemName, const Item &item) const
|
||||
{
|
||||
if(itemName.startsWith("----")) {
|
||||
return renderFreeForm(itemName, item);
|
||||
}
|
||||
else {
|
||||
const ByteVector name = itemName.data(String::Latin1);
|
||||
auto handlerType = handlerTypeForName(name);
|
||||
switch(handlerType) {
|
||||
case ItemHandlerType::Unknown:
|
||||
debug("MP4: Unknown item name \"" + name + "\"");
|
||||
break;
|
||||
case ItemHandlerType::FreeForm:
|
||||
return renderFreeForm(name, item);
|
||||
case ItemHandlerType::IntPair:
|
||||
return renderIntPair(name, item);
|
||||
case ItemHandlerType::IntPairNoTrailing:
|
||||
return renderIntPairNoTrailing(name, item);
|
||||
case ItemHandlerType::Bool:
|
||||
return renderBool(name, item);
|
||||
case ItemHandlerType::Int:
|
||||
return renderInt(name, item);
|
||||
case ItemHandlerType::TextOrInt:
|
||||
return renderTextOrInt(name, item);
|
||||
case ItemHandlerType::UInt:
|
||||
return renderUInt(name, item);
|
||||
case ItemHandlerType::LongLong:
|
||||
return renderLongLong(name, item);
|
||||
case ItemHandlerType::Byte:
|
||||
return renderByte(name, item);
|
||||
case ItemHandlerType::Gnre:
|
||||
return renderInt(name, item);
|
||||
case ItemHandlerType::Covr:
|
||||
return renderCovr(name, item);
|
||||
case ItemHandlerType::TextImplicit:
|
||||
return renderText(name, item, TypeImplicit);
|
||||
case ItemHandlerType::Text:
|
||||
return renderText(name, item);
|
||||
}
|
||||
}
|
||||
return ByteVector();
|
||||
}
|
||||
|
||||
std::pair<ByteVector, Item> ItemFactory::itemFromProperty(
|
||||
const String &key, const StringList &values) const
|
||||
{
|
||||
ByteVector name = nameForPropertyKey(key);
|
||||
if(!name.isEmpty()) {
|
||||
if(values.isEmpty()) {
|
||||
return {name, values};
|
||||
}
|
||||
auto handlerType = name.startsWith("----")
|
||||
? ItemHandlerType::FreeForm
|
||||
: handlerTypeForName(name);
|
||||
switch(handlerType) {
|
||||
case ItemHandlerType::IntPair:
|
||||
case ItemHandlerType::IntPairNoTrailing:
|
||||
if(StringList parts = StringList::split(values.front(), "/");
|
||||
!parts.isEmpty()) {
|
||||
int first = parts[0].toInt();
|
||||
int second = 0;
|
||||
if(parts.size() > 1) {
|
||||
second = parts[1].toInt();
|
||||
}
|
||||
return {name, Item(first, second)};
|
||||
}
|
||||
break;
|
||||
case ItemHandlerType::Int:
|
||||
case ItemHandlerType::Gnre:
|
||||
return {name, Item(values.front().toInt())};
|
||||
case ItemHandlerType::UInt:
|
||||
return {name, Item(static_cast<unsigned int>(values.front().toInt()))};
|
||||
case ItemHandlerType::LongLong:
|
||||
return {name, Item(static_cast<long long>(values.front().toInt()))};
|
||||
case ItemHandlerType::Byte:
|
||||
return {name, Item(static_cast<unsigned char>(values.front().toInt()))};
|
||||
case ItemHandlerType::Bool:
|
||||
return {name, Item(values.front().toInt() != 0)};
|
||||
case ItemHandlerType::FreeForm:
|
||||
case ItemHandlerType::TextOrInt:
|
||||
case ItemHandlerType::TextImplicit:
|
||||
case ItemHandlerType::Text:
|
||||
return {name, values};
|
||||
|
||||
case ItemHandlerType::Covr:
|
||||
debug("MP4: Invalid item \"" + name + "\" for property");
|
||||
break;
|
||||
case ItemHandlerType::Unknown:
|
||||
debug("MP4: Unknown item name \"" + name + "\" for property");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {name, Item()};
|
||||
}
|
||||
|
||||
std::pair<String, StringList> ItemFactory::itemToProperty(
|
||||
const ByteVector &itemName, const Item &item) const
|
||||
{
|
||||
const String key = propertyKeyForName(itemName);
|
||||
if(!key.isEmpty()) {
|
||||
auto handlerType = itemName.startsWith("----")
|
||||
? ItemHandlerType::FreeForm
|
||||
: handlerTypeForName(itemName);
|
||||
switch(handlerType) {
|
||||
case ItemHandlerType::IntPair:
|
||||
case ItemHandlerType::IntPairNoTrailing:
|
||||
{
|
||||
auto [vn, tn] = item.toIntPair();
|
||||
String value = String::number(vn);
|
||||
if(tn) {
|
||||
value += "/" + String::number(tn);
|
||||
}
|
||||
return {key, value};
|
||||
}
|
||||
case ItemHandlerType::Int:
|
||||
case ItemHandlerType::Gnre:
|
||||
return {key, String::number(item.toInt())};
|
||||
case ItemHandlerType::UInt:
|
||||
return {key, String::number(item.toUInt())};
|
||||
case ItemHandlerType::LongLong:
|
||||
return {key, String::fromLongLong(item.toLongLong())};
|
||||
case ItemHandlerType::Byte:
|
||||
return {key, String::number(item.toByte())};
|
||||
case ItemHandlerType::Bool:
|
||||
return {key, String::number(item.toBool())};
|
||||
case ItemHandlerType::FreeForm:
|
||||
case ItemHandlerType::TextOrInt:
|
||||
case ItemHandlerType::TextImplicit:
|
||||
case ItemHandlerType::Text:
|
||||
return {key, item.toStringList()};
|
||||
|
||||
case ItemHandlerType::Covr:
|
||||
debug("MP4: Invalid item \"" + itemName + "\" for property");
|
||||
break;
|
||||
case ItemHandlerType::Unknown:
|
||||
debug("MP4: Unknown item name \"" + itemName + "\" for property");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {String(), StringList()};
|
||||
}
|
||||
|
||||
String ItemFactory::propertyKeyForName(const ByteVector &name) const
|
||||
{
|
||||
if(d->propertyKeyForName.isEmpty()) {
|
||||
d->propertyKeyForName = namePropertyMap();
|
||||
}
|
||||
return d->propertyKeyForName.value(name);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::nameForPropertyKey(const String &key) const
|
||||
{
|
||||
if(d->nameForPropertyKey.isEmpty()) {
|
||||
if(d->propertyKeyForName.isEmpty()) {
|
||||
d->propertyKeyForName = namePropertyMap();
|
||||
}
|
||||
for(const auto &[k, t] : std::as_const(d->propertyKeyForName)) {
|
||||
d->nameForPropertyKey[t] = k;
|
||||
}
|
||||
}
|
||||
return d->nameForPropertyKey.value(key);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ItemFactory::ItemFactory() :
|
||||
d(std::make_unique<ItemFactoryPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
ItemFactory::~ItemFactory() = default;
|
||||
|
||||
ItemFactory::NameHandlerMap ItemFactory::nameHandlerMap() const
|
||||
{
|
||||
return {
|
||||
{"----", ItemHandlerType::FreeForm},
|
||||
{"trkn", ItemHandlerType::IntPair},
|
||||
{"disk", ItemHandlerType::IntPairNoTrailing},
|
||||
{"cpil", ItemHandlerType::Bool},
|
||||
{"pgap", ItemHandlerType::Bool},
|
||||
{"pcst", ItemHandlerType::Bool},
|
||||
{"shwm", ItemHandlerType::Bool},
|
||||
{"tmpo", ItemHandlerType::Int},
|
||||
{"\251mvi", ItemHandlerType::Int},
|
||||
{"\251mvc", ItemHandlerType::Int},
|
||||
{"hdvd", ItemHandlerType::Int},
|
||||
{"rate", ItemHandlerType::TextOrInt},
|
||||
{"tvsn", ItemHandlerType::UInt},
|
||||
{"tves", ItemHandlerType::UInt},
|
||||
{"cnID", ItemHandlerType::UInt},
|
||||
{"sfID", ItemHandlerType::UInt},
|
||||
{"atID", ItemHandlerType::UInt},
|
||||
{"geID", ItemHandlerType::UInt},
|
||||
{"cmID", ItemHandlerType::UInt},
|
||||
{"plID", ItemHandlerType::LongLong},
|
||||
{"stik", ItemHandlerType::Byte},
|
||||
{"rtng", ItemHandlerType::Byte},
|
||||
{"akID", ItemHandlerType::Byte},
|
||||
{"gnre", ItemHandlerType::Gnre},
|
||||
{"covr", ItemHandlerType::Covr},
|
||||
{"purl", ItemHandlerType::TextImplicit},
|
||||
{"egid", ItemHandlerType::TextImplicit},
|
||||
};
|
||||
}
|
||||
|
||||
ItemFactory::ItemHandlerType ItemFactory::handlerTypeForName(
|
||||
const ByteVector &name) const
|
||||
{
|
||||
if(d->handlerTypeForName.isEmpty()) {
|
||||
d->handlerTypeForName = nameHandlerMap();
|
||||
}
|
||||
auto type = d->handlerTypeForName.value(name, ItemHandlerType::Unknown);
|
||||
if (type == ItemHandlerType::Unknown && name.size() == 4) {
|
||||
type = ItemHandlerType::Text;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
Map<ByteVector, String> ItemFactory::namePropertyMap() const
|
||||
{
|
||||
return {
|
||||
{"\251nam", "TITLE"},
|
||||
{"\251ART", "ARTIST"},
|
||||
{"\251alb", "ALBUM"},
|
||||
{"\251cmt", "COMMENT"},
|
||||
{"\251gen", "GENRE"},
|
||||
{"\251day", "DATE"},
|
||||
{"\251wrt", "COMPOSER"},
|
||||
{"\251grp", "GROUPING"},
|
||||
{"aART", "ALBUMARTIST"},
|
||||
{"trkn", "TRACKNUMBER"},
|
||||
{"disk", "DISCNUMBER"},
|
||||
{"cpil", "COMPILATION"},
|
||||
{"tmpo", "BPM"},
|
||||
{"cprt", "COPYRIGHT"},
|
||||
{"\251lyr", "LYRICS"},
|
||||
{"\251too", "ENCODING"},
|
||||
{"\251enc", "ENCODEDBY"},
|
||||
{"soal", "ALBUMSORT"},
|
||||
{"soaa", "ALBUMARTISTSORT"},
|
||||
{"soar", "ARTISTSORT"},
|
||||
{"sonm", "TITLESORT"},
|
||||
{"soco", "COMPOSERSORT"},
|
||||
{"sosn", "SHOWSORT"},
|
||||
{"shwm", "SHOWWORKMOVEMENT"},
|
||||
{"pgap", "GAPLESSPLAYBACK"},
|
||||
{"pcst", "PODCAST"},
|
||||
{"catg", "PODCASTCATEGORY"},
|
||||
{"desc", "PODCASTDESC"},
|
||||
{"egid", "PODCASTID"},
|
||||
{"purl", "PODCASTURL"},
|
||||
{"tves", "TVEPISODE"},
|
||||
{"tven", "TVEPISODEID"},
|
||||
{"tvnn", "TVNETWORK"},
|
||||
{"tvsn", "TVSEASON"},
|
||||
{"tvsh", "TVSHOW"},
|
||||
{"\251wrk", "WORK"},
|
||||
{"\251mvn", "MOVEMENTNAME"},
|
||||
{"\251mvi", "MOVEMENTNUMBER"},
|
||||
{"\251mvc", "MOVEMENTCOUNT"},
|
||||
{"ownr", "OWNER"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Release Track Id", "MUSICBRAINZ_RELEASETRACKID"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Album Release Country", "RELEASECOUNTRY"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Album Status", "RELEASESTATUS"},
|
||||
{"----:com.apple.iTunes:MusicBrainz Album Type", "RELEASETYPE"},
|
||||
{"----:com.apple.iTunes:ARTISTS", "ARTISTS"},
|
||||
{"----:com.apple.iTunes:ORIGINALDATE", "ORIGINALDATE"},
|
||||
{"----:com.apple.iTunes:RELEASEDATE", "RELEASEDATE"},
|
||||
{"----:com.apple.iTunes:ASIN", "ASIN"},
|
||||
{"----:com.apple.iTunes:LABEL", "LABEL"},
|
||||
{"----:com.apple.iTunes:LYRICIST", "LYRICIST"},
|
||||
{"----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR"},
|
||||
{"----:com.apple.iTunes:REMIXER", "REMIXER"},
|
||||
{"----:com.apple.iTunes:ENGINEER", "ENGINEER"},
|
||||
{"----:com.apple.iTunes:PRODUCER", "PRODUCER"},
|
||||
{"----:com.apple.iTunes:DJMIXER", "DJMIXER"},
|
||||
{"----:com.apple.iTunes:MIXER", "MIXER"},
|
||||
{"----:com.apple.iTunes:SUBTITLE", "SUBTITLE"},
|
||||
{"----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE"},
|
||||
{"----:com.apple.iTunes:MOOD", "MOOD"},
|
||||
{"----:com.apple.iTunes:ISRC", "ISRC"},
|
||||
{"----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER"},
|
||||
{"----:com.apple.iTunes:BARCODE", "BARCODE"},
|
||||
{"----:com.apple.iTunes:SCRIPT", "SCRIPT"},
|
||||
{"----:com.apple.iTunes:LANGUAGE", "LANGUAGE"},
|
||||
{"----:com.apple.iTunes:LICENSE", "LICENSE"},
|
||||
{"----:com.apple.iTunes:MEDIA", "MEDIA"}
|
||||
};
|
||||
}
|
||||
|
||||
MP4::AtomDataList ItemFactory::parseData2(
|
||||
const MP4::Atom *atom, const ByteVector &data, int expectedFlags,
|
||||
bool freeForm)
|
||||
{
|
||||
AtomDataList result;
|
||||
int i = 0;
|
||||
unsigned int pos = 0;
|
||||
while(pos < data.size()) {
|
||||
const auto length = static_cast<int>(data.toUInt(pos));
|
||||
if(length < 12) {
|
||||
debug("MP4: Too short atom");
|
||||
return result;
|
||||
}
|
||||
|
||||
const ByteVector name = data.mid(pos + 4, 4);
|
||||
const auto flags = static_cast<int>(data.toUInt(pos + 8));
|
||||
if(freeForm && i < 2) {
|
||||
if(i == 0 && name != "mean") {
|
||||
debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
|
||||
return result;
|
||||
}
|
||||
if(i == 1 && name != "name") {
|
||||
debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
|
||||
return result;
|
||||
}
|
||||
result.append(AtomData(static_cast<AtomDataType>(flags),
|
||||
data.mid(pos + 12, length - 12)));
|
||||
}
|
||||
else {
|
||||
if(name != "data") {
|
||||
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
|
||||
return result;
|
||||
}
|
||||
if(expectedFlags == -1 || flags == expectedFlags) {
|
||||
result.append(AtomData(static_cast<AtomDataType>(flags),
|
||||
data.mid(pos + 16, length - 16)));
|
||||
}
|
||||
}
|
||||
pos += length;
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ByteVectorList ItemFactory::parseData(
|
||||
const MP4::Atom *atom, const ByteVector &bytes, int expectedFlags,
|
||||
bool freeForm)
|
||||
{
|
||||
const AtomDataList data = parseData2(atom, bytes, expectedFlags, freeForm);
|
||||
ByteVectorList result;
|
||||
for(const auto &atom : data) {
|
||||
result.append(atom.data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseInt(
|
||||
const MP4::Atom *atom, const ByteVector &bytes)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, bytes);
|
||||
return {
|
||||
atom->name(),
|
||||
!data.isEmpty() ? Item(static_cast<int>(data[0].toShort())) : Item()
|
||||
};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseTextOrInt(
|
||||
const MP4::Atom *atom, const ByteVector &bytes)
|
||||
{
|
||||
AtomDataList data = parseData2(atom, bytes);
|
||||
if(!data.isEmpty()) {
|
||||
AtomData val = data[0];
|
||||
return {
|
||||
atom->name(),
|
||||
val.type == TypeUTF8 ? Item(StringList(String(val.data, String::UTF8)))
|
||||
: Item(static_cast<int>(val.data.toShort()))
|
||||
};
|
||||
}
|
||||
return {atom->name(), Item()};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseUInt(
|
||||
const MP4::Atom *atom, const ByteVector &bytes)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, bytes);
|
||||
return {
|
||||
atom->name(),
|
||||
!data.isEmpty() ? Item(data[0].toUInt()) : Item()
|
||||
};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseLongLong(
|
||||
const MP4::Atom *atom, const ByteVector &bytes)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, bytes);
|
||||
return {
|
||||
atom->name(),
|
||||
!data.isEmpty() ?Item (data[0].toLongLong()) : Item()
|
||||
};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseByte(
|
||||
const MP4::Atom *atom, const ByteVector &bytes)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, bytes);
|
||||
return {
|
||||
atom->name(),
|
||||
!data.isEmpty() ? Item(static_cast<unsigned char>(data[0].at(0))) : Item()
|
||||
};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseGnre(
|
||||
const MP4::Atom *atom, const ByteVector &bytes)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, bytes);
|
||||
if(!data.isEmpty()) {
|
||||
int idx = static_cast<int>(data[0].toShort());
|
||||
if(idx > 0) {
|
||||
return {
|
||||
"\251gen",
|
||||
Item(StringList(ID3v1::genre(idx - 1)))
|
||||
};
|
||||
}
|
||||
}
|
||||
return {"\251gen", Item()};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseIntPair(
|
||||
const MP4::Atom *atom, const ByteVector &bytes)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, bytes);
|
||||
if(!data.isEmpty()) {
|
||||
const int a = data[0].toShort(2U);
|
||||
const int b = data[0].toShort(4U);
|
||||
return {atom->name(), Item(a, b)};
|
||||
}
|
||||
return {atom->name(), Item()};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseBool(
|
||||
const MP4::Atom *atom, const ByteVector &bytes)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, bytes);
|
||||
if(!data.isEmpty()) {
|
||||
bool value = !data[0].isEmpty() && data[0][0] != '\0';
|
||||
return {atom->name(), Item(value)};
|
||||
}
|
||||
return {atom->name(), Item()};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseText(
|
||||
const MP4::Atom *atom, const ByteVector &bytes, int expectedFlags)
|
||||
{
|
||||
const ByteVectorList data = parseData(atom, bytes, expectedFlags);
|
||||
if(!data.isEmpty()) {
|
||||
StringList value;
|
||||
for(const auto &byte : data) {
|
||||
value.append(String(byte, String::UTF8));
|
||||
}
|
||||
return {atom->name(), Item(value)};
|
||||
}
|
||||
return {atom->name(), Item()};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseFreeForm(
|
||||
const MP4::Atom *atom, const ByteVector &bytes)
|
||||
{
|
||||
const AtomDataList data = parseData2(atom, bytes, -1, true);
|
||||
if(data.size() > 2) {
|
||||
auto itBegin = data.begin();
|
||||
|
||||
String name = "----:";
|
||||
name += String((itBegin++)->data, String::UTF8); // data[0].data
|
||||
name += ':';
|
||||
name += String((itBegin++)->data, String::UTF8); // data[1].data
|
||||
|
||||
AtomDataType type = itBegin->type; // data[2].type
|
||||
|
||||
for(auto it = itBegin; it != data.end(); ++it) {
|
||||
if(it->type != type) {
|
||||
debug("MP4: We currently don't support values with multiple types");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(type == TypeUTF8) {
|
||||
StringList value;
|
||||
for(auto it = itBegin; it != data.end(); ++it) {
|
||||
value.append(String(it->data, String::UTF8));
|
||||
}
|
||||
Item item(value);
|
||||
item.setAtomDataType(type);
|
||||
return {name, item};
|
||||
}
|
||||
else {
|
||||
ByteVectorList value;
|
||||
for(auto it = itBegin; it != data.end(); ++it) {
|
||||
value.append(it->data);
|
||||
}
|
||||
Item item(value);
|
||||
item.setAtomDataType(type);
|
||||
return {name, item};
|
||||
}
|
||||
}
|
||||
return {atom->name(), Item()};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseCovr(
|
||||
const MP4::Atom *atom, const ByteVector &data)
|
||||
{
|
||||
MP4::CoverArtList value;
|
||||
unsigned int pos = 0;
|
||||
while(pos < data.size()) {
|
||||
const int length = static_cast<int>(data.toUInt(pos));
|
||||
if(length < 12) {
|
||||
debug("MP4: Too short atom");
|
||||
break;
|
||||
}
|
||||
|
||||
const ByteVector name = data.mid(pos + 4, 4);
|
||||
const int flags = static_cast<int>(data.toUInt(pos + 8));
|
||||
if(name != "data") {
|
||||
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
|
||||
break;
|
||||
}
|
||||
if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP ||
|
||||
flags == TypeGIF || flags == TypeImplicit) {
|
||||
value.append(MP4::CoverArt(static_cast<MP4::CoverArt::Format>(flags),
|
||||
data.mid(pos + 16, length - 16)));
|
||||
}
|
||||
else {
|
||||
debug("MP4: Unknown covr format " + String::number(flags));
|
||||
}
|
||||
pos += length;
|
||||
}
|
||||
return {
|
||||
atom->name(),
|
||||
!value.isEmpty() ? Item(value) : Item()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
ByteVector ItemFactory::renderAtom(
|
||||
const ByteVector &name, const ByteVector &data)
|
||||
{
|
||||
return ByteVector::fromUInt(data.size() + 8) + name + data;
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderData(
|
||||
const ByteVector &name, int flags, const ByteVectorList &data)
|
||||
{
|
||||
ByteVector result;
|
||||
for(const auto &byte : data) {
|
||||
result.append(renderAtom("data", ByteVector::fromUInt(flags) +
|
||||
ByteVector(4, '\0') + byte));
|
||||
}
|
||||
return renderAtom(name, result);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderBool(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector(1, item.toBool() ? '\1' : '\0'));
|
||||
return renderData(name, TypeInteger, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderInt(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector::fromShort(item.toInt()));
|
||||
return renderData(name, TypeInteger, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderTextOrInt(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
StringList value = item.toStringList();
|
||||
return value.isEmpty() ? renderInt(name, item) : renderText(name, item);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderUInt(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector::fromUInt(item.toUInt()));
|
||||
return renderData(name, TypeInteger, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderLongLong(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector::fromLongLong(item.toLongLong()));
|
||||
return renderData(name, TypeInteger, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderByte(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector(1, item.toByte()));
|
||||
return renderData(name, TypeInteger, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderIntPair(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector(2, '\0') +
|
||||
ByteVector::fromShort(item.toIntPair().first) +
|
||||
ByteVector::fromShort(item.toIntPair().second) +
|
||||
ByteVector(2, '\0'));
|
||||
return renderData(name, TypeImplicit, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderIntPairNoTrailing(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector(2, '\0') +
|
||||
ByteVector::fromShort(item.toIntPair().first) +
|
||||
ByteVector::fromShort(item.toIntPair().second));
|
||||
return renderData(name, TypeImplicit, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderText(
|
||||
const ByteVector &name, const MP4::Item &item, int flags)
|
||||
{
|
||||
ByteVectorList data;
|
||||
const StringList values = item.toStringList();
|
||||
for(const auto &value : values) {
|
||||
data.append(value.data(String::UTF8));
|
||||
}
|
||||
return renderData(name, flags, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderCovr(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVector data;
|
||||
const MP4::CoverArtList values = item.toCoverArtList();
|
||||
for(const auto &value : values) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(value.format()) +
|
||||
ByteVector(4, '\0') + value.data()));
|
||||
}
|
||||
return renderAtom(name, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderFreeForm(
|
||||
const String &name, const MP4::Item &item)
|
||||
{
|
||||
StringList header = StringList::split(name, ":");
|
||||
if(header.size() != 3) {
|
||||
debug("MP4: Invalid free-form item name \"" + name + "\"");
|
||||
return ByteVector();
|
||||
}
|
||||
ByteVector data;
|
||||
data.append(renderAtom("mean", ByteVector::fromUInt(0) +
|
||||
header[1].data(String::UTF8)));
|
||||
data.append(renderAtom("name", ByteVector::fromUInt(0) +
|
||||
header[2].data(String::UTF8)));
|
||||
AtomDataType type = item.atomDataType();
|
||||
if(type == TypeUndefined) {
|
||||
if(!item.toStringList().isEmpty()) {
|
||||
type = TypeUTF8;
|
||||
}
|
||||
else {
|
||||
type = TypeImplicit;
|
||||
}
|
||||
}
|
||||
if(type == TypeUTF8) {
|
||||
const StringList values = item.toStringList();
|
||||
for(const auto &value : values) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(type) +
|
||||
ByteVector(4, '\0') +
|
||||
value.data(String::UTF8)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const ByteVectorList values = item.toByteVectorList();
|
||||
for(const auto &value : values) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(type) +
|
||||
ByteVector(4, '\0') + value));
|
||||
}
|
||||
}
|
||||
return renderAtom("----", data);
|
||||
}
|
||||
257
taglib/mp4/mp4itemfactory.h
Normal file
257
taglib/mp4/mp4itemfactory.h
Normal file
@@ -0,0 +1,257 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2023 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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_MP4ITEMFACTORY_H
|
||||
#define TAGLIB_MP4ITEMFACTORY_H
|
||||
|
||||
#include <memory>
|
||||
#include "taglib_export.h"
|
||||
#include "mp4item.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace MP4 {
|
||||
|
||||
//! A factory for creating MP4 items during parsing
|
||||
|
||||
/*!
|
||||
* This factory abstracts away the parsing and rendering between atom data
|
||||
* and MP4 items.
|
||||
*
|
||||
* Reimplementing this factory is the key to adding support for atom types
|
||||
* not directly supported by TagLib to your application. To do so you would
|
||||
* subclass this factory and reimplement nameHandlerMap() to add support
|
||||
* for new atoms. If the new atoms do not have the same behavior as
|
||||
* other supported atoms, it may be necessary to reimplement parseItem() and
|
||||
* renderItem(). Then by setting your factory in the MP4::Tag constructor
|
||||
* you can implement behavior that will allow for new atom types to be used.
|
||||
*
|
||||
* A custom item factory adding support for a "tsti" integer atom and a
|
||||
* "tstt" text atom can be implemented like this:
|
||||
*
|
||||
* \code
|
||||
* class CustomItemFactory : public MP4::ItemFactory {
|
||||
* protected:
|
||||
* NameHandlerMap nameHandlerMap() const override
|
||||
* {
|
||||
* return MP4::ItemFactory::nameHandlerMap()
|
||||
* .insert("tsti", ItemHandlerType::Int)
|
||||
* .insert("tstt", ItemHandlerType::Text);
|
||||
* }
|
||||
* };
|
||||
* \endcode
|
||||
*
|
||||
* If the custom item shall also be accessible via a property,
|
||||
* namePropertyMap() can be overridden in the same way.
|
||||
*/
|
||||
class TAGLIB_EXPORT ItemFactory
|
||||
{
|
||||
public:
|
||||
ItemFactory(const ItemFactory &) = delete;
|
||||
ItemFactory &operator=(const ItemFactory &) = delete;
|
||||
|
||||
static ItemFactory *instance();
|
||||
|
||||
/*!
|
||||
* Create an MP4 item from the \a data bytes of an \a atom.
|
||||
* Returns the name (in most cases atom->name) and an item,
|
||||
* an invalid item on failure.
|
||||
* The default implementation uses the map returned by nameHandlerMap().
|
||||
*/
|
||||
virtual std::pair<String, Item> parseItem(
|
||||
const Atom *atom, const ByteVector &data) const;
|
||||
|
||||
/*!
|
||||
* Render an MP4 \a item to the data bytes of an atom \a itemName.
|
||||
* An empty byte vector is returned if the item is invalid or unknown.
|
||||
* The default implementation uses the map returned by nameHandlerMap().
|
||||
*/
|
||||
virtual ByteVector renderItem(
|
||||
const String &itemName, const Item &item) const;
|
||||
|
||||
/*!
|
||||
* Create an MP4 item from a property with \a key and \a values.
|
||||
* If the property is not supported, an invalid item is returned.
|
||||
* The default implementation uses the map returned by namePropertyMap().
|
||||
*/
|
||||
virtual std::pair<ByteVector, Item> itemFromProperty(
|
||||
const String &key, const StringList &values) const;
|
||||
|
||||
/*!
|
||||
* Get an MP4 item as a property.
|
||||
* If no property exists for \a itemName, an empty string is returned as
|
||||
* the property key.
|
||||
* The default implementation uses the map returned by namePropertyMap().
|
||||
*/
|
||||
virtual std::pair<String, StringList> itemToProperty(
|
||||
const ByteVector &itemName, const Item &item) const;
|
||||
|
||||
/*!
|
||||
* Returns property key for atom \a name, empty if no property exists for
|
||||
* this atom.
|
||||
* The default method looks up the map created by namePropertyMap() and
|
||||
* should be enough for most uses.
|
||||
*/
|
||||
virtual String propertyKeyForName(const ByteVector &name) const;
|
||||
|
||||
/*!
|
||||
* Returns atom name for property \a key, empty if no property is
|
||||
* supported for this key.
|
||||
* The default method uses the reverse mapping of propertyKeyForName()
|
||||
* and should be enough for most uses.
|
||||
*/
|
||||
virtual ByteVector nameForPropertyKey(const String &key) const;
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Type that determines the parsing and rendering between the data and
|
||||
* the item representation of an atom.
|
||||
*/
|
||||
enum class ItemHandlerType {
|
||||
Unknown,
|
||||
FreeForm,
|
||||
IntPair,
|
||||
IntPairNoTrailing,
|
||||
Bool,
|
||||
Int,
|
||||
TextOrInt,
|
||||
UInt,
|
||||
LongLong,
|
||||
Byte,
|
||||
Gnre,
|
||||
Covr,
|
||||
TextImplicit,
|
||||
Text
|
||||
};
|
||||
|
||||
/*! Mapping of atom name to handler type. */
|
||||
using NameHandlerMap = Map<ByteVector, ItemHandlerType>;
|
||||
|
||||
/*!
|
||||
* Constructs an item factory. Because this is a singleton this method is
|
||||
* protected, but may be used for subclasses.
|
||||
*/
|
||||
ItemFactory();
|
||||
|
||||
/*!
|
||||
* Destroys the frame factory.
|
||||
*/
|
||||
virtual ~ItemFactory();
|
||||
|
||||
/*!
|
||||
* Returns mapping between atom names and handler types.
|
||||
* This method is called once by handlerTypeForName() to initialize its
|
||||
* internal cache.
|
||||
* To add support for a new atom, it is sufficient in most cases to just
|
||||
* reimplement this method and add new entries.
|
||||
*/
|
||||
virtual NameHandlerMap nameHandlerMap() const;
|
||||
|
||||
/*!
|
||||
* Returns handler type for atom \a name.
|
||||
* The default method looks up the map created by nameHandlerMap() and
|
||||
* should be enough for most uses.
|
||||
*/
|
||||
virtual ItemHandlerType handlerTypeForName(const ByteVector &name) const;
|
||||
|
||||
/*!
|
||||
* Returns mapping between atom names and property keys.
|
||||
* This method is called once by propertyKeyForName() to initialize its
|
||||
* internal cache.
|
||||
* To add support for a new atom with a property, it is sufficient in most
|
||||
* cases to just reimplement this method and add new entries.
|
||||
*/
|
||||
virtual Map<ByteVector, String> namePropertyMap() const;
|
||||
|
||||
// Functions used by parseItem() to create items from atom data.
|
||||
static MP4::AtomDataList parseData2(
|
||||
const MP4::Atom *atom, const ByteVector &data, int expectedFlags = -1,
|
||||
bool freeForm = false);
|
||||
static ByteVectorList parseData(
|
||||
const MP4::Atom *atom, const ByteVector &bytes, int expectedFlags = -1,
|
||||
bool freeForm = false);
|
||||
static std::pair<String, Item> parseText(
|
||||
const MP4::Atom *atom, const ByteVector &bytes, int expectedFlags = 1);
|
||||
static std::pair<String, Item> parseFreeForm(
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseInt(
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseByte(
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseTextOrInt(
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseUInt(
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseLongLong(
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseGnre(
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseIntPair(
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseBool(
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseCovr(
|
||||
const MP4::Atom *atom, const ByteVector &data);
|
||||
|
||||
// Functions used by renderItem() to render atom data for items.
|
||||
static ByteVector renderAtom(
|
||||
const ByteVector &name, const ByteVector &data);
|
||||
static ByteVector renderData(
|
||||
const ByteVector &name, int flags, const ByteVectorList &data);
|
||||
static ByteVector renderText(
|
||||
const ByteVector &name, const MP4::Item &item, int flags = TypeUTF8);
|
||||
static ByteVector renderFreeForm(
|
||||
const String &name, const MP4::Item &item);
|
||||
static ByteVector renderBool(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderInt(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderTextOrInt(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderByte(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderUInt(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderLongLong(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderIntPair(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderIntPairNoTrailing(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderCovr(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
|
||||
private:
|
||||
static ItemFactory factory;
|
||||
|
||||
class ItemFactoryPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ItemFactoryPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace MP4
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
@@ -23,11 +23,12 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "mp4properties.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tstring.h"
|
||||
#include "mp4file.h"
|
||||
#include "mp4atom.h"
|
||||
#include "mp4properties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -37,15 +38,15 @@ namespace
|
||||
long long calculateMdatLength(const MP4::AtomList &list)
|
||||
{
|
||||
long long totalLength = 0;
|
||||
for(MP4::AtomList::ConstIterator it = list.begin(); it != list.end(); ++it) {
|
||||
long length = (*it)->length;
|
||||
for(const auto &atom : list) {
|
||||
offset_t length = atom->length();
|
||||
if(length == 0)
|
||||
return 0; // for safety, see checkValid() in mp4file.cpp
|
||||
|
||||
if((*it)->name == "mdat")
|
||||
if(atom->name() == "mdat")
|
||||
totalLength += length;
|
||||
|
||||
totalLength += calculateMdatLength((*it)->children);
|
||||
totalLength += calculateMdatLength(atom->children());
|
||||
}
|
||||
|
||||
return totalLength;
|
||||
@@ -55,22 +56,13 @@ namespace
|
||||
class MP4::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
bitsPerSample(0),
|
||||
encrypted(false),
|
||||
codec(MP4::Properties::Unknown) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int bitsPerSample;
|
||||
bool encrypted;
|
||||
Codec codec;
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
bool encrypted { false };
|
||||
Codec codec { MP4::Properties::Unknown };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -79,15 +71,12 @@ public:
|
||||
|
||||
MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
read(file, atoms);
|
||||
}
|
||||
|
||||
MP4::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
MP4::Properties::~Properties() = default;
|
||||
|
||||
int
|
||||
MP4::Properties::channels() const
|
||||
@@ -101,18 +90,6 @@ MP4::Properties::sampleRate() const
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
@@ -156,23 +133,23 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
return;
|
||||
}
|
||||
|
||||
MP4::Atom *trak = 0;
|
||||
MP4::Atom *trak = nullptr;
|
||||
ByteVector data;
|
||||
|
||||
const MP4::AtomList trakList = moov->findall("trak");
|
||||
for(MP4::AtomList::ConstIterator it = trakList.begin(); it != trakList.end(); ++it) {
|
||||
trak = *it;
|
||||
MP4::Atom *hdlr = trak->find("mdia", "hdlr");
|
||||
for(const auto &track : trakList) {
|
||||
MP4::Atom *hdlr = track->find("mdia", "hdlr");
|
||||
if(!hdlr) {
|
||||
debug("MP4: Atom 'trak.mdia.hdlr' not found");
|
||||
return;
|
||||
}
|
||||
file->seek(hdlr->offset);
|
||||
data = file->readBlock(hdlr->length);
|
||||
trak = track;
|
||||
file->seek(hdlr->offset());
|
||||
data = file->readBlock(hdlr->length());
|
||||
if(data.containsAt("soun", 16)) {
|
||||
break;
|
||||
}
|
||||
trak = 0;
|
||||
trak = nullptr;
|
||||
}
|
||||
if(!trak) {
|
||||
debug("MP4: No audio tracks");
|
||||
@@ -185,8 +162,8 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
return;
|
||||
}
|
||||
|
||||
file->seek(mdhd->offset);
|
||||
data = file->readBlock(mdhd->length);
|
||||
file->seek(mdhd->offset());
|
||||
data = file->readBlock(mdhd->length());
|
||||
|
||||
const unsigned int version = data.at(8);
|
||||
long long unit;
|
||||
@@ -207,6 +184,17 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
unit = data.toUInt(20U);
|
||||
length = data.toUInt(24U);
|
||||
}
|
||||
if(length == 0) {
|
||||
// No length found in the media header (mdhd), try the movie header (mvhd)
|
||||
if(MP4::Atom *mvhd = moov->find("mvhd")) {
|
||||
file->seek(mvhd->offset());
|
||||
data = file->readBlock(mvhd->length());
|
||||
if(data.size() >= 24 + 4) {
|
||||
unit = data.toUInt(20U);
|
||||
length = data.toUInt(24U);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(unit > 0 && length > 0)
|
||||
d->length = static_cast<int>(length * 1000.0 / unit + 0.5);
|
||||
|
||||
@@ -215,8 +203,8 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
return;
|
||||
}
|
||||
|
||||
file->seek(atom->offset);
|
||||
data = file->readBlock(atom->length);
|
||||
file->seek(atom->offset());
|
||||
data = file->readBlock(atom->length());
|
||||
if(data.containsAt("mp4a", 20)) {
|
||||
d->codec = AAC;
|
||||
d->channels = data.toShort(40U);
|
||||
@@ -240,13 +228,13 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
}
|
||||
else {
|
||||
d->bitrate = static_cast<int>(
|
||||
(calculateMdatLength(atoms->atoms) * 8) / d->length);
|
||||
(calculateMdatLength(atoms->atoms()) * 8) / d->length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(data.containsAt("alac", 20)) {
|
||||
if(atom->length == 88 && data.containsAt("alac", 56)) {
|
||||
if(atom->length() == 88 && data.containsAt("alac", 56)) {
|
||||
d->codec = ALAC;
|
||||
d->bitsPerSample = data.at(69);
|
||||
d->channels = data.at(73);
|
||||
@@ -257,13 +245,12 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
// There are files which do not contain a nominal bitrate, e.g. those
|
||||
// generated by refalac64.exe. Calculate the bitrate from the audio
|
||||
// data size (mdat atoms) and the duration.
|
||||
d->bitrate = (calculateMdatLength(atoms->atoms) * 8) / d->length;
|
||||
d->bitrate = static_cast<int>((calculateMdatLength(atoms->atoms()) * 8) / d->length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MP4::Atom *drms = atom->find("drms");
|
||||
if(drms) {
|
||||
if(atom->find("drms")) {
|
||||
d->encrypted = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,49 +45,32 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
Properties(File *file, Atoms *atoms, ReadStyle style = Average);
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated Use lengthInSeconds().
|
||||
*/
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
@@ -108,7 +91,8 @@ namespace TagLib {
|
||||
void read(File *file, Atoms *atoms);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace MP4
|
||||
} // namespace TagLib
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user