31 Commits

Author SHA1 Message Date
Scott Wheeler
de25bc6111 StripAll should be treated as equivalent to StripOthers in save() 2019-09-12 07:57:16 +02:00
Scott Wheeler
074f30e3fa Remove DSF and DSDIFF from master to a feature branch
These can be merged back into master once they're in a more mature state.
2019-09-12 07:55:34 +02:00
Scott Wheeler
f1b40de66b Unify File::save(...) APIs between file formats that support ID3v2
Closes #922
2019-09-11 06:48:27 +02:00
Scott Wheeler
0b99cd9bac Revert switch to other static size method
This was based on a misread of the header:  at present there is no
non-static size() method, so removing the argument makes the behavior
incorrect.
2019-09-11 00:58:18 +02:00
Scott Wheeler
4668cf0f93 Missing header that should have been added in b8dc105 2019-09-11 00:42:15 +02:00
Scott Wheeler
c05fa78406 Mark deprected methods and remove internal usage
This does not put the deprecated marker on methods that will or could resolve
to the same overload, e.g.:

void foo(bool bar = true); // <-- not marked
void foo(Bar bar) // <-- since this will have a default argument in the new version
2019-09-11 00:39:37 +02:00
Scott Wheeler
b8dc105ae3 Deprecate calls to MPEG::File::save(...) that use boolean params
This uses explicit enums for e.g. the ID3v2 version, making calls more
readable:

  file.save(ID3v1 | ID3v2, StripOthers, ID3v2::v4, Duplicate);

Instead of:

  file.save(ID3v1 | ID3v2, true, 4, true);

Needs to be ported to other types, per #922
2019-09-10 22:59:07 +02:00
Scott Wheeler
fced0f46e9 Add docs for this method 2019-09-10 13:31:44 +02:00
Scott Wheeler
dc0f667a4c No tabs in TagLib 2019-09-10 13:08:11 +02:00
Scott Wheeler
085180e9a4 Require at least CMake 3 2019-09-10 12:45:36 +02:00
Scott Wheeler
ef1312d622 Add -lz to taglib.pc and taglib-config when built with zlib
Closes #872
2019-09-10 12:41:11 +02:00
StefanBruens
86c0428475 Clear valid flag for invalid Speex files
This matches the corresponding code in vorbisfile.cpp, opusfile.cpp and flagfile.cpp, and fixes taglib/taglib#902.
2019-09-10 11:44:21 +02:00
Scott Wheeler
3c78c4cfc9 Merge pull request #883 from ufleisch/riff-padding
Do not ignore non zero RIFF padding if leading to parse error (#882)
2019-09-10 11:25:46 +02:00
Scott Wheeler
c8bb6271e5 Merge pull request #917 from ufleisch/ogg-bitrate
Calculate Ogg bitrate without comment size (#874)
2019-09-10 11:14:15 +02:00
Scott Wheeler
84f7462526 Tests need C++11 2019-09-05 17:29:40 +02:00
Urs Fleisch
2f23892182 Calculate Ogg bitrate without overhead size (#874) 2019-09-02 22:14:41 +02:00
Scott Wheeler
2918602ad0 Merge pull request #910 from joerg-krause/patch-1
Drop CMAKE_SYSROOT from taglib-config
2019-08-26 23:37:24 +02:00
Scott Wheeler
c146cd7e92 Merge pull request #909 from williamjcm/patch-1
Make TagLib seach in its source dir for utf8-cpp.
2019-08-26 23:34:47 +02:00
Scott Wheeler
b124e621fe Merge pull request #912 from whatdoineed2do/m4a-track-year-equal0-bugfix
MP4 - setTrack()/setYear() accepts 0 to remove the tag
2019-08-26 23:32:44 +02:00
Scott Wheeler
044fba2921 Merge pull request #919 from jonaski/spelling
Fix spelling and typos
2019-08-26 23:26:55 +02:00
Jonas Kvinge
e72a98903f Fix spelling and typos 2019-08-26 23:23:33 +02:00
whatdoineed2do
79bc9ccf8e Call fflush() before ftruncate() to drop all buffered data (#914)
This avoids stale data presented to caller via a fread()

Current bug due to the buffered data can be seen in stripping mp3s of tags

    f.strip(ID3v1);
    f.strip(APE);

The ID3v1 tag sits at the end of file (strip calls ftruncate()) and the APE
strip performs a readFile() that would return the stream buffered/truncated data
and reinsert
2019-07-25 07:27:49 +04:30
whatdoineed2do/Ray
850a3565a4 setTrack()/setYear() accepts 0 to remove the tag as per
documentation/functionality across other tpyes (mp3/flac/...); m4a do not honour
this and instead sets the underlying value to 0.

This commit fixes this issue (#911)
2019-07-13 16:03:51 +01:00
Jörg Krause
18d424995f Drop CMAKE_SYSROOT from taglib-config
Commit d4c938cbc7 is about fixing taglib-config for proper cross-compilation. The fix is right in principle, but wrong about adding `CMAKE_SYSROOT`. The correct prefix path should be set outside of the config file as some embedded Linux distros like OpenWrt or OpenEmbedded install with a different DESTDIR, and dependent packages see a different sysroot.
2019-06-16 15:33:52 +02:00
Guillaume Jacquemin
d04db70bc6 Make TagLib seach in its source dir for utf8-cpp.
Without this change, TagLib is unusable as part of CMake projects that rely on `add_subdirectory()` for dependencies.
2019-06-01 10:19:27 +02:00
StefanBruens
ba7adc2bc2 Respect atom type when converting rate tag (#896)
* Respect atom type when converting rate tag

TagLib prior to #818 (commit ff28cf276c) read and wrote the "rate" tag as
text, and switched to reading it as integer value even when the atom class
is a text type. This breaks reading existing files, and can be avoided by
taking the atom type into account.

This fixes issue #885.

* Respect MP4::Item type when writing the rate tag

TagLib prior to #818 (commit ff28cf276c) read and wrote the "rate" tag
as text, and switched to writing the integer value of the MP4::Item.
This breaks writing from applications which supply the value as
StringList, which was the previously implemented API. Applications using
an MP4::Item(UInt) are still supported without changes on the application
side.

This is the complementary writing part for issue #885.
2019-05-31 15:51:16 +04:30
Urs Fleisch
79bb1428c0 Support ID3v2 GRP1 frame introduced with iTunes 12.5.4.42, #903. (#904) 2019-05-17 15:45:48 +04:30
Jörg Krause
7470f92a67 fix taglib-config file for cross compiling (#906) 2019-05-17 15:43:35 +04:30
Tim Malseed
6455671ece Update mp4properties.cpp (#893)
When parsing mp4 media header version 1 (mdhd) atoms, the timescale (unit) is parsed as a `LongLong` (8 bytes), but instead should be a `UInt` (4 bytes). This results in an incorrect timescale, and also pushes the offset of the duration (length) off by 4 bytes.

The end result being that the AudioProperties track length for mp4's with mdhd v1 comes back as 0.

See: https://wiki.multimedia.cx/index.php/QuickTime_container

|  Entry | Bytes (v0) | Bytes (v1) |
| :---         |     :---:      | :---: |
| size  | 4  | 4 |
| type  | 4  | 4 |
| version | 1 | 1 |
| flags | 3 | 3 |
| creation time* | 4 | **8** |
| modification time* | 4 | **8** |
| time scale | 4 | 4 |
| duration* | 4 | **8** |
| language | 2 | 2 |
| quality | 2 | 2 |
2019-03-17 08:22:19 -05:00
Tim Malseed
660748210f Minor fix for mp4 media header v0 minimum size check (#895)
Mp4 media header (mdhd) v0 atoms are a minimum of 8 bytes for size & type information, plus 24 bytes for remaining entries (`24 +8`) bytes in total, rather than (`24 + 4`).

See https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25615
2019-03-17 08:20:43 -05:00
Urs Fleisch
1cf176af61 Do not ignore non zero RIFF padding if leading to parse error (#882) 2019-02-10 08:40:20 +01:00
264 changed files with 5745 additions and 11290 deletions

View File

@@ -17,7 +17,6 @@ addons:
packages:
- libcppunit-dev
- zlib1g-dev
- libboost-dev
matrix:
exclude:

View File

@@ -1,11 +1,7 @@
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(taglib)
if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
cmake_policy(SET CMP0022 OLD)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
if(DEFINED ENABLE_STATIC)
@@ -104,8 +100,12 @@ math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
include(ConfigureChecks.cmake)
if(${ZLIB_FOUND})
set(ZLIB_LIBRARIES_FLAGS -lz)
endif()
if(NOT WIN32)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" @ONLY)
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${BIN_INSTALL_DIR}")
endif()
@@ -127,11 +127,20 @@ endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
if(WITH_ASF)
set(TAGLIB_WITH_ASF TRUE)
endif()
if(WITH_MP4)
set(TAGLIB_WITH_MP4 TRUE)
endif()
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
if(TRACE_IN_RELEASE)
set(TRACE_IN_RELEASE TRUE)
endif()
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
add_subdirectory(taglib)
if(BUILD_BINDINGS)

View File

@@ -1,8 +1,6 @@
include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckCXXCompilerFlag)
include(CheckCXXSourceCompiles)
include(TestLargeFiles)
# Check if the size of numeric types are suitable.
@@ -36,22 +34,6 @@ if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
endif()
# Determine whether your compiler supports large files.
if(NOT WIN32)
test_large_files(SUPPORT_LARGE_FILES)
if(NOT SUPPORT_LARGE_FILES)
MESSAGE(FATAL_ERROR "TagLib requires large files support.")
endif()
endif()
# Enable check_cxx_source_compiles() to work with Boost "header-only" libraries.
find_package(Boost)
if(Boost_FOUND)
set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${Boost_INCLUDE_DIRS}")
endif()
# Determine which kind of atomic operations your compiler supports.
check_cxx_source_compiles("
@@ -111,17 +93,6 @@ if(NOT HAVE_STD_ATOMIC)
endif()
endif()
# Determine which kind of smart pointers your compiler supports.
check_cxx_source_compiles("
#include <memory>
int main() {
std::shared_ptr<int> x;
std::unique_ptr<int> y;
return 0;
}
" HAVE_STD_SMART_PTR)
# Determine which kind of byte swap functions your compiler supports.
check_cxx_source_compiles("
@@ -240,5 +211,5 @@ endif()
# Detect WinRT mode
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
set(PLATFORM_WINRT 1)
set(PLATFORM WINRT 1)
endif()

6
NEWS
View File

@@ -245,7 +245,7 @@ TagLib 1.6.3 (Apr 17, 2010)
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
* New method `int String::toInt(bool *ok)` which can return whether the
conversion to a number was successfull.
conversion to a number was successful.
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
compressed frames). (BUG:231075)
@@ -253,7 +253,7 @@ TagLib 1.6.2 (Apr 9, 2010)
==========================
* Read Vorbis Comments from the first FLAC metadata block, if there are
multipe ones. (BUG:211089)
multiple ones. (BUG:211089)
* Fixed a memory leak in FileRef's OGA format detection.
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
@@ -288,7 +288,7 @@ TagLib 1.6 (Sep 13, 2009)
* Added support for disabling dllimport/dllexport on Windows using the
TAGLIB_STATIC macro.
* Support for parsing the obsolete 'gnre' MP4 atom.
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determin if
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determine if
TagLib was built with MP4/ASF support.
1.6 RC1:

View File

@@ -92,32 +92,32 @@ void taglib_free(void* pointer)
TagLib_File *taglib_file_new(const char *filename)
{
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
return reinterpret_cast<TagLib_File *>(FileRef::create(filename));
}
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
{
switch(type) {
case TagLib_File_MPEG:
return reinterpret_cast<TagLib_File *>(new FileRef(new MPEG::File(filename)));
return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
case TagLib_File_OggVorbis:
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Vorbis::File(filename)));
return reinterpret_cast<TagLib_File *>(new Ogg::Vorbis::File(filename));
case TagLib_File_FLAC:
return reinterpret_cast<TagLib_File *>(new FileRef(new FLAC::File(filename)));
return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
case TagLib_File_MPC:
return reinterpret_cast<TagLib_File *>(new FileRef(new MPC::File(filename)));
return reinterpret_cast<TagLib_File *>(new MPC::File(filename));
case TagLib_File_OggFlac:
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::FLAC::File(filename)));
return reinterpret_cast<TagLib_File *>(new Ogg::FLAC::File(filename));
case TagLib_File_WavPack:
return reinterpret_cast<TagLib_File *>(new FileRef(new WavPack::File(filename)));
return reinterpret_cast<TagLib_File *>(new WavPack::File(filename));
case TagLib_File_Speex:
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Speex::File(filename)));
return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
case TagLib_File_TrueAudio:
return reinterpret_cast<TagLib_File *>(new FileRef(new TrueAudio::File(filename)));
return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
case TagLib_File_MP4:
return reinterpret_cast<TagLib_File *>(new FileRef(new MP4::File(filename)));
return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
case TagLib_File_ASF:
return reinterpret_cast<TagLib_File *>(new FileRef(new ASF::File(filename)));
return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
default:
return 0;
}
@@ -125,29 +125,29 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
void taglib_file_free(TagLib_File *file)
{
delete reinterpret_cast<FileRef *>(file);
delete reinterpret_cast<File *>(file);
}
BOOL taglib_file_is_valid(const TagLib_File *file)
{
return reinterpret_cast<const FileRef *>(file)->isValid();
return reinterpret_cast<const File *>(file)->isValid();
}
TagLib_Tag *taglib_file_tag(const TagLib_File *file)
{
const FileRef *f = reinterpret_cast<const FileRef *>(file);
const File *f = reinterpret_cast<const File *>(file);
return reinterpret_cast<TagLib_Tag *>(f->tag());
}
const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
{
const FileRef *f = reinterpret_cast<const FileRef *>(file);
const File *f = reinterpret_cast<const File *>(file);
return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
}
BOOL taglib_file_save(TagLib_File *file)
{
return reinterpret_cast<FileRef *>(file)->save();
return reinterpret_cast<File *>(file)->save();
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,9 +0,0 @@
#include <sys/types.h>
int main(int argc, char **argv)
{
/* Cause a compile-time error if off_t is smaller than 64 bits */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
return 0;
}

View File

@@ -1,23 +0,0 @@
#cmakedefine _LARGEFILE_SOURCE
#cmakedefine _LARGEFILE64_SOURCE
#cmakedefine _LARGE_FILES
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
/* Cause a compile-time error if off_t is smaller than 64 bits,
* and make sure we have ftello / fseeko.
*/
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
FILE *fp = fopen(argv[0],"r");
off_t offset = ftello( fp );
fseeko( fp, offset, SEEK_CUR );
fclose(fp);
return 0;
}

View File

@@ -1,104 +0,0 @@
# - Define macro to check large file support
#
# TEST_LARGE_FILES(VARIABLE)
#
# VARIABLE will be set to true if off_t is 64 bits, and fseeko/ftello present.
# This macro will also set defines necessary enable large file support, for instance
# _LARGE_FILES
# _LARGEFILE_SOURCE
# _FILE_OFFSET_BITS 64
# HAVE_FSEEKO
#
# However, it is YOUR job to make sure these defines are set in a cmakedefine so they
# end up in a config.h file that is included in your source if necessary!
# This macro skips the Windows specific checks. Because TagLib uses Win32 API.
MACRO(TEST_LARGE_FILES VARIABLE)
IF(NOT DEFINED ${VARIABLE})
# On most platforms it is probably overkill to first test the flags for 64-bit off_t,
# and then separately fseeko. However, in the future we might have 128-bit filesystems
# (ZFS), so it might be dangerous to indiscriminately set e.g. _FILE_OFFSET_BITS=64.
MESSAGE(STATUS "Checking for 64-bit off_t")
# First check without any special flags
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c")
if(FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - present")
endif(FILE64_OK)
if(NOT FILE64_OK)
# Test with _FILE_OFFSET_BITS=64
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
COMPILE_DEFINITIONS "-D_FILE_OFFSET_BITS=64" )
if(FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - present with _FILE_OFFSET_BITS=64")
set(_FILE_OFFSET_BITS 64)
endif(FILE64_OK)
endif(NOT FILE64_OK)
if(NOT FILE64_OK)
# Test with _LARGE_FILES
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
COMPILE_DEFINITIONS "-D_LARGE_FILES" )
if(FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGE_FILES")
set(_LARGE_FILES 1)
endif(FILE64_OK)
endif(NOT FILE64_OK)
if(NOT FILE64_OK)
# Test with _LARGEFILE_SOURCE
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
if(FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGEFILE_SOURCE")
set(_LARGEFILE_SOURCE 1)
endif(FILE64_OK)
endif(NOT FILE64_OK)
if(NOT FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - not present")
else(NOT FILE64_OK)
# Set the flags we might have determined to be required above
configure_file("${CMAKE_SOURCE_DIR}/cmake/TestLargeFiles.c.cmakein"
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
MESSAGE(STATUS "Checking for fseeko/ftello")
# Test if ftello/fseeko are available
TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}"
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
if(FSEEKO_COMPILE_OK)
MESSAGE(STATUS "Checking for fseeko/ftello - present")
endif(FSEEKO_COMPILE_OK)
if(NOT FSEEKO_COMPILE_OK)
# glibc 2.2 neds _LARGEFILE_SOURCE for fseeko (but not 64-bit off_t...)
TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}"
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c"
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
if(FSEEKO_COMPILE_OK)
MESSAGE(STATUS "Checking for fseeko/ftello - present with _LARGEFILE_SOURCE")
set(_LARGEFILE_SOURCE 1)
endif(FSEEKO_COMPILE_OK)
endif(NOT FSEEKO_COMPILE_OK)
endif(NOT FILE64_OK)
if(FSEEKO_COMPILE_OK)
SET(${VARIABLE} 1 CACHE INTERNAL "Result of test for large file support" FORCE)
set(HAVE_FSEEKO 1)
else(FSEEKO_COMPILE_OK)
MESSAGE(STATUS "Checking for fseeko/ftello - not found")
SET(${VARIABLE} 0 CACHE INTERNAL "Result of test for large file support" FORCE)
endif(FSEEKO_COMPILE_OK)
ENDIF(NOT DEFINED ${VARIABLE})
ENDMACRO(TEST_LARGE_FILES VARIABLE)

View File

@@ -3,11 +3,6 @@
#ifndef TAGLIB_CONFIG_H
#define TAGLIB_CONFIG_H
/* Defined if required for large files support */
#cmakedefine _LARGE_FILES ${_LARGE_FILES}
#cmakedefine _LARGEFILE_SOURCE ${_LARGEFILE_SOURCE}
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
/* Defined if your compiler supports some byte swap functions */
#cmakedefine HAVE_GCC_BYTESWAP 1
#cmakedefine HAVE_GLIBC_BYTESWAP 1
@@ -22,9 +17,6 @@
#cmakedefine HAVE_WIN_ATOMIC 1
#cmakedefine HAVE_IA64_ATOMIC 1
/* Defined if your compiler supports shared_ptr */
#cmakedefine HAVE_STD_SMART_PTR 1
/* Defined if your compiler supports some safer version of vsprintf */
#cmakedefine HAVE_VSNPRINTF 1
#cmakedefine HAVE_VSPRINTF_S 1

View File

@@ -37,7 +37,3 @@ target_link_libraries(framelist tag)
add_executable(strip-id3v1 strip-id3v1.cpp)
target_link_libraries(strip-id3v1 tag)
########### next target ###############
add_executable(inspect inspect.cpp)
target_link_libraries(inspect tag)

View File

@@ -1,48 +0,0 @@
/* Copyright (C) 2012 Lukas Lalinsky <lalinsky@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <iostream>
#include <stdlib.h>
#include <fileref.h>
#include <tfile.h>
using namespace std;
using namespace TagLib;
int main(int argc, char *argv[])
{
// process the command line args
for(int i = 1; i < argc; i++) {
cout << "******************** \"" << argv[i] << "\"********************" << endl;
FileRef f(argv[i]);
if(!f.isNull() && f.file()) {
cout << f.file()->toString().to8Bit(true) << endl;
}
}
}

View File

@@ -28,7 +28,6 @@
#include <fileref.h>
#include <tag.h>
#include <tpicturemap.h>
#include <tpropertymap.h>
using namespace std;
@@ -46,15 +45,13 @@ int main(int argc, char *argv[])
TagLib::Tag *tag = f.tag();
cout << "-- TAG (basic) --" << endl;
cout << "title - \"" << tag->title() << "\"" << endl;
cout << "artist - \"" << tag->artist() << "\"" << endl;
cout << "album - \"" << tag->album() << "\"" << endl;
cout << "year - \"" << tag->year() << "\"" << endl;
cout << "comment - \"" << tag->comment() << "\"" << endl;
cout << "track - \"" << tag->track() << "\"" << endl;
cout << "genre - \"" << tag->genre() << "\"" << endl;
if(!tag->pictures().isEmpty())
cout << "pictures -" << tag->pictures() << endl;
cout << "title - \"" << tag->title() << "\"" << endl;
cout << "artist - \"" << tag->artist() << "\"" << endl;
cout << "album - \"" << tag->album() << "\"" << endl;
cout << "year - \"" << tag->year() << "\"" << endl;
cout << "comment - \"" << tag->comment() << "\"" << endl;
cout << "track - \"" << tag->track() << "\"" << endl;
cout << "genre - \"" << tag->genre() << "\"" << endl;
TagLib::PropertyMap tags = f.file()->properties();

View File

@@ -22,10 +22,8 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <fstream>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string.h>
#include <stdio.h>
@@ -36,8 +34,6 @@
#include <tlist.h>
#include <fileref.h>
#include <tfile.h>
#include <tpicturemap.h>
#include <tag.h>
#include <tpropertymap.h>
@@ -74,7 +70,6 @@ void usage()
cout << " -R <tagname> <tagvalue>" << endl;
cout << " -I <tagname> <tagvalue>" << endl;
cout << " -D <tagname>" << endl;
cout << " -p <picture(jpg only, file between double quotes)>" << endl;
cout << endl;
exit(1);
@@ -115,16 +110,14 @@ int main(int argc, char *argv[])
if(fileList.isEmpty())
usage();
if(argv[argc-1][1] == 'p')
argc++;
for(int i = 1; i < argc - 1; i += 2) {
if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
char field = argv[i][1];
TagLib::String value = argv[i + 1];
TagLib::List<TagLib::FileRef>::Iterator it;
TagLib::List<TagLib::FileRef>::ConstIterator it;
for(it = fileList.begin(); it != fileList.end(); ++it) {
TagLib::Tag *t = (*it).tag();
@@ -174,31 +167,6 @@ int main(int argc, char *argv[])
checkForRejectedProperties((*it).file()->setProperties(map));
break;
}
case 'p':
{
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));
if(!data.find("JFIF")) {
cout << value.toCString() << " is not a JPEG." << endl;
return 1;
}
TagLib::Picture pic(data,
TagLib::Picture::FrontCover,
"image/jpeg",
"Added with taglib");
TagLib::PictureMap picMap(pic);
t->setPictures(picMap);
}
break;
default:
usage();
break;

View File

@@ -14,10 +14,10 @@ EOH
exit 1;
}
prefix=${CMAKE_INSTALL_PREFIX}
exec_prefix=${CMAKE_INSTALL_PREFIX}
libdir=${LIB_INSTALL_DIR}
includedir=${INCLUDE_INSTALL_DIR}
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/lib
includedir=${prefix}/include
flags=""
@@ -29,13 +29,13 @@ while test $# -gt 0
do
case $1 in
--libs)
flags="$flags -L$libdir -ltag"
flags="$flags -L$libdir -ltag @ZLIB_LIBRARIES_FLAGS@"
;;
--cflags)
flags="$flags -I$includedir/taglib"
;;
--version)
echo ${TAGLIB_LIB_VERSION_STRING}
echo @TAGLIB_LIB_VERSION_STRING@
;;
--prefix)
echo $prefix

View File

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

View File

@@ -7,5 +7,5 @@ Name: TagLib
Description: Audio meta-data library
Requires:
Version: @TAGLIB_LIB_VERSION_STRING@
Libs: -L${libdir} -ltag
Libs: -L${libdir} -ltag @ZLIB_LIBRARIES_FLAGS@
Cflags: -I${includedir}/taglib

View File

@@ -24,11 +24,9 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/s3m
${CMAKE_CURRENT_SOURCE_DIR}/it
${CMAKE_CURRENT_SOURCE_DIR}/xm
${CMAKE_CURRENT_SOURCE_DIR}/ebml
${CMAKE_CURRENT_SOURCE_DIR}/ebml/matroska
${CMAKE_CURRENT_SOURCE_DIR}/dsf
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
${CMAKE_SOURCE_DIR}/3rdparty
${taglib_SOURCE_DIR}/3rdparty
)
if(ZLIB_FOUND)
@@ -42,12 +40,12 @@ set(tag_HDRS
fileref.h
audioproperties.h
taglib_export.h
${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
toolkit/taglib.h
toolkit/tstring.h
toolkit/tlist.h
toolkit/tlist.tcc
toolkit/tstringlist.h
toolkit/tstringhandler.h
toolkit/tbytevector.h
toolkit/tbytevectorlist.h
toolkit/tbytevectorstream.h
@@ -56,8 +54,6 @@ set(tag_HDRS
toolkit/tfilestream.h
toolkit/tmap.h
toolkit/tmap.tcc
toolkit/tpicture.h
toolkit/tpicturemap.h
toolkit/tpropertymap.h
toolkit/trefcounter.h
toolkit/tdebuglistener.h
@@ -67,6 +63,7 @@ set(tag_HDRS
mpeg/xingheader.h
mpeg/id3v1/id3v1tag.h
mpeg/id3v1/id3v1genres.h
mpeg/id3v2/id3v2.h
mpeg/id3v2/id3v2extendedheader.h
mpeg/id3v2/id3v2frame.h
mpeg/id3v2/id3v2header.h
@@ -144,17 +141,6 @@ set(tag_HDRS
s3m/s3mproperties.h
xm/xmfile.h
xm/xmproperties.h
ebml/ebmlfile.h
ebml/ebmlelement.h
ebml/ebmlconstants.h
ebml/matroska/ebmlmatroskafile.h
ebml/matroska/ebmlmatroskaconstants.h
ebml/matroska/ebmlmatroskaaudio.h
dsf/dsffile.h
dsf/dsfproperties.h
dsdiff/dsdifffile.h
dsdiff/dsdiffproperties.h
dsdiff/dsdiffdiintag.h
)
set(mpeg_SRCS
@@ -310,32 +296,9 @@ set(xm_SRCS
xm/xmproperties.cpp
)
set(dsf_SRCS
dsf/dsffile.cpp
dsf/dsfproperties.cpp
)
set(ebml_SRCS
ebml/ebmlfile.cpp
ebml/ebmlelement.cpp
)
set(matroska_SRCS
ebml/matroska/ebmlmatroskafile.cpp
ebml/matroska/ebmlmatroskaaudio.cpp
)
set(dsdiff_SRCS
dsdiff/dsdifffile.cpp
dsdiff/dsdiffproperties.cpp
dsdiff/dsdiffdiintag.cpp
)
set(toolkit_SRCS
toolkit/taglib.cpp
toolkit/tstring.cpp
toolkit/tstringlist.cpp
toolkit/tstringhandler.cpp
toolkit/tbytevector.cpp
toolkit/tbytevectorlist.cpp
toolkit/tbytevectorstream.cpp
@@ -343,8 +306,6 @@ set(toolkit_SRCS
toolkit/tfile.cpp
toolkit/tfilestream.cpp
toolkit/tdebug.cpp
toolkit/tpicture.cpp
toolkit/tpicturemap.cpp
toolkit/tpropertymap.cpp
toolkit/trefcounter.cpp
toolkit/tdebuglistener.cpp
@@ -367,7 +328,6 @@ set(tag_LIB_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}
${ebml_SRCS} ${matroska_SRCS} ${dsf_SRCS} ${dsdiff_SRCS}
${zlib_SRCS}
tag.cpp
tagunion.cpp
@@ -391,8 +351,7 @@ set_target_properties(tag PROPERTIES
PUBLIC_HEADER "${tag_HDRS}"
)
if(VISIBILITY_HIDDEN)
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden
)
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
endif()
if(BUILD_FRAMEWORK)

View File

@@ -39,7 +39,6 @@
#include <id3v2header.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <tsmartptr.h>
#include "apefile.h"
#include "apetag.h"
@@ -59,21 +58,29 @@ public:
APELocation(-1),
APESize(0),
ID3v1Location(-1),
ID3v2Header(0),
ID3v2Location(-1),
ID3v2Size(0) {}
ID3v2Size(0),
properties(0) {}
long long APELocation;
long long APESize;
~FilePrivate()
{
delete ID3v2Header;
delete properties;
}
long long ID3v1Location;
long APELocation;
long APESize;
SCOPED_PTR<ID3v2::Header> ID3v2Header;
long long ID3v2Location;
long long ID3v2Size;
long ID3v1Location;
DoubleTagUnion tag;
ID3v2::Header *ID3v2Header;
long ID3v2Location;
long ID3v2Size;
SCOPED_PTR<AudioProperties> properties;
TagUnion tag;
Properties *properties;
};
////////////////////////////////////////////////////////////////////////////////
@@ -85,14 +92,14 @@ bool APE::File::isSupported(IOStream *stream)
// An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return (buffer.find("MAC ") != ByteVector::npos());
return (buffer.find("MAC ") >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
@@ -100,7 +107,7 @@ APE::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle)
read(readProperties);
}
APE::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) :
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
@@ -118,6 +125,16 @@ TagLib::Tag *APE::File::tag() const
return &d->tag;
}
PropertyMap APE::File::properties() const
{
return d->tag.properties();
}
void APE::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap APE::File::setProperties(const PropertyMap &properties)
{
if(ID3v1Tag())
@@ -126,9 +143,9 @@ PropertyMap APE::File::setProperties(const PropertyMap &properties)
return APETag(true)->setProperties(properties);
}
APE::AudioProperties *APE::File::audioProperties() const
APE::Properties *APE::File::audioProperties() const
{
return d->properties.get();
return d->properties;
}
bool APE::File::save()
@@ -178,7 +195,7 @@ bool APE::File::save()
}
const ByteVector data = APETag()->render();
insert(data, d->APELocation, static_cast<size_t>(d->APESize));
insert(data, d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
@@ -190,7 +207,7 @@ bool APE::File::save()
// APE tag is empty. Remove the old one.
if(d->APELocation >= 0) {
removeBlock(d->APELocation, static_cast<size_t>(d->APESize));
removeBlock(d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->APESize;
@@ -247,7 +264,7 @@ void APE::File::read(bool readProperties)
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location);
d->ID3v2Header.reset(new ID3v2::Header(readBlock(ID3v2::Header::size())));
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
d->ID3v2Size = d->ID3v2Header->completeTagSize();
}
@@ -275,7 +292,7 @@ void APE::File::read(bool readProperties)
if(readProperties) {
long long streamLength;
long streamLength;
if(d->APELocation >= 0)
streamLength = d->APELocation;
@@ -292,6 +309,6 @@ void APE::File::read(bool readProperties)
seek(0);
}
d->properties.reset(new AudioProperties(this, streamLength));
d->properties = new Properties(this, streamLength);
}
}

View File

@@ -90,7 +90,7 @@ namespace TagLib {
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an APE file from \a stream. If \a readProperties is true the
@@ -102,7 +102,7 @@ namespace TagLib {
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@@ -115,18 +115,31 @@ namespace TagLib {
*/
virtual TagLib::Tag *tag() const;
/*!
* 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;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
* tag will be updated as well.
*/
virtual PropertyMap setProperties(const PropertyMap &);
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the APE::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const;
virtual Properties *audioProperties() const;
/*!
* Saves the file.

View File

@@ -175,19 +175,19 @@ void APE::Footer::parse(const ByteVector &data)
// Read the version number
d->version = data.toUInt32LE(8);
d->version = data.toUInt(8, false);
// Read the tag size
d->tagSize = data.toUInt32LE(12);
d->tagSize = data.toUInt(12, false);
// Read the item count
d->itemCount = data.toUInt32LE(16);
d->itemCount = data.toUInt(16, false);
// Read the flags
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt32LE(20)));
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
d->headerPresent = flags[31];
d->footerPresent = !flags[30];
@@ -206,15 +206,15 @@ ByteVector APE::Footer::render(bool isHeader) const
// add the version number -- we always render a 2.000 tag regardless of what
// the tag originally was.
v.append(ByteVector::fromUInt32LE(2000));
v.append(ByteVector::fromUInt(2000, false));
// add the tag size
v.append(ByteVector::fromUInt32LE(d->tagSize));
v.append(ByteVector::fromUInt(d->tagSize, false));
// add the item count
v.append(ByteVector::fromUInt32LE(d->itemCount));
v.append(ByteVector::fromUInt(d->itemCount, false));
// render and add the flags
@@ -224,11 +224,11 @@ ByteVector APE::Footer::render(bool isHeader) const
flags[30] = false; // footer is always present
flags[29] = isHeader;
v.append(ByteVector::fromUInt32LE(flags.to_ulong()));
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
// add the reserved 64bit
v.append(ByteVector::fromUInt64BE(0));
v.append(ByteVector::fromLongLong(0));
return v;
}

View File

@@ -54,7 +54,7 @@ namespace TagLib {
* Constructs an APE footer based on \a data. parse() is called
* immediately.
*/
explicit Footer(const ByteVector &data);
Footer(const ByteVector &data);
/*!
* Destroys the footer.

View File

@@ -25,17 +25,17 @@
#include <tbytevectorlist.h>
#include <tdebug.h>
#include <tsmartptr.h>
#include "apeitem.h"
using namespace TagLib;
using namespace APE;
struct ItemData
class APE::Item::ItemPrivate
{
ItemData() :
type(Item::Text),
public:
ItemPrivate() :
type(Text),
readOnly(false) {}
Item::ItemTypes type;
@@ -45,15 +45,6 @@ struct ItemData
bool readOnly;
};
class APE::Item::ItemPrivate
{
public:
ItemPrivate() :
data(new ItemData()) {}
SHARED_PTR<ItemData> data;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
@@ -66,27 +57,27 @@ APE::Item::Item() :
APE::Item::Item(const String &key, const String &value) :
d(new ItemPrivate())
{
d->data->key = key;
d->data->text.append(value);
d->key = key;
d->text.append(value);
}
APE::Item::Item(const String &key, const StringList &values) :
d(new ItemPrivate())
{
d->data->key = key;
d->data->text = values;
d->key = key;
d->text = values;
}
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
d(new ItemPrivate())
{
d->data->key = key;
d->key = key;
if(binary) {
d->data->type = Binary;
d->data->value = value;
d->type = Binary;
d->value = value;
}
else {
d->data->text.append(value);
d->text.append(value);
}
}
@@ -115,122 +106,135 @@ void APE::Item::swap(Item &item)
void APE::Item::setReadOnly(bool readOnly)
{
d->data->readOnly = readOnly;
d->readOnly = readOnly;
}
bool APE::Item::isReadOnly() const
{
return d->data->readOnly;
return d->readOnly;
}
void APE::Item::setType(APE::Item::ItemTypes val)
{
d->data->type = val;
d->type = val;
}
APE::Item::ItemTypes APE::Item::type() const
{
return d->data->type;
return d->type;
}
String APE::Item::key() const
{
return d->data->key;
return d->key;
}
ByteVector APE::Item::binaryData() const
{
return d->data->value;
return d->value;
}
void APE::Item::setBinaryData(const ByteVector &value)
{
d->data->type = Binary;
d->data->value = value;
d->data->text.clear();
d->type = Binary;
d->value = 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->data->key = key;
d->key = key;
}
void APE::Item::setValue(const String &value)
{
d->data->type = Text;
d->data->text = value;
d->data->value.clear();
d->type = Text;
d->text = value;
d->value.clear();
}
void APE::Item::setValues(const StringList &value)
{
d->data->type = Text;
d->data->text = value;
d->data->value.clear();
d->type = Text;
d->text = value;
d->value.clear();
}
void APE::Item::appendValue(const String &value)
{
d->data->type = Text;
d->data->text.append(value);
d->data->value.clear();
d->type = Text;
d->text.append(value);
d->value.clear();
}
void APE::Item::appendValues(const StringList &values)
{
d->data->type = Text;
d->data->text.append(values);
d->data->value.clear();
d->type = Text;
d->text.append(values);
d->value.clear();
}
int APE::Item::size() const
{
size_t result = 8 + d->data->key.size() + 1;
switch(d->data->type) {
int result = 8 + d->key.size() + 1;
switch(d->type) {
case Text:
if(!d->data->text.isEmpty()) {
StringList::ConstIterator it = d->data->text.begin();
if(!d->text.isEmpty()) {
StringList::ConstIterator it = d->text.begin();
result += it->data(String::UTF8).size();
it++;
for(; it != d->data->text.end(); ++it)
for(; it != d->text.end(); ++it)
result += 1 + it->data(String::UTF8).size();
}
break;
case Binary:
case Locator:
result += d->data->value.size();
result += d->value.size();
break;
}
return static_cast<int>(result);
return result;
}
StringList APE::Item::toStringList() const
{
return d->text;
}
StringList APE::Item::values() const
{
return d->data->text;
return d->text;
}
String APE::Item::toString() const
{
if(d->data->type == Text && !isEmpty())
return d->data->text.front();
if(d->type == Text && !isEmpty())
return d->text.front();
else
return String();
}
bool APE::Item::isEmpty() const
{
switch(d->data->type) {
switch(d->type) {
case Text:
if(d->data->text.isEmpty())
if(d->text.isEmpty())
return true;
if(d->data->text.size() == 1 && d->data->text.front().isEmpty())
if(d->text.size() == 1 && d->text.front().isEmpty())
return true;
return false;
case Binary:
case Locator:
return d->data->value.isEmpty();
return d->value.isEmpty();
default:
return false;
}
@@ -245,51 +249,51 @@ void APE::Item::parse(const ByteVector &data)
return;
}
const unsigned int valueLength = data.toUInt32LE(0);
const unsigned int flags = data.toUInt32LE(4);
const unsigned int valueLength = data.toUInt(0, false);
const unsigned int flags = data.toUInt(4, false);
// An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8.
// We assume that the validity of the given key has been checked.
d->data->key = String(&data[8], String::Latin1);
d->key = String(&data[8], String::Latin1);
const ByteVector value = data.mid(8 + d->data->key.size() + 1, valueLength);
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
setReadOnly(flags & 1);
setType(ItemTypes((flags >> 1) & 3));
if(Text == d->data->type)
d->data->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
if(Text == d->type)
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
else
d->data->value = value;
d->value = value;
}
ByteVector APE::Item::render() const
{
ByteVector data;
unsigned int flags = ((d->data->readOnly) ? 1 : 0) | (d->data->type << 1);
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
ByteVector value;
if(isEmpty())
return data;
if(d->data->type == Text) {
StringList::ConstIterator it = d->data->text.begin();
if(d->type == Text) {
StringList::ConstIterator it = d->text.begin();
value.append(it->data(String::UTF8));
it++;
for(; it != d->data->text.end(); ++it) {
for(; it != d->text.end(); ++it) {
value.append('\0');
value.append(it->data(String::UTF8));
}
d->data->value = value;
d->value = value;
}
else
value.append(d->data->value);
value.append(d->value);
data.append(ByteVector::fromUInt32LE(value.size()));
data.append(ByteVector::fromUInt32LE(flags));
data.append(d->data->key.data(String::Latin1));
data.append(ByteVector::fromUInt(value.size(), false));
data.append(ByteVector::fromUInt(flags, false));
data.append(d->key.data(String::Latin1));
data.append(ByteVector('\0'));
data.append(value);

View File

@@ -61,6 +61,7 @@ namespace TagLib {
/*!
* 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);
/*!
@@ -111,6 +112,11 @@ 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.
*/
@@ -157,6 +163,11 @@ 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.

View File

@@ -29,7 +29,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <bitset>
#include "id3v2tag.h"
#include "apeproperties.h"
#include "apefile.h"
@@ -38,7 +38,7 @@
using namespace TagLib;
class APE::AudioProperties::PropertiesPrivate
class APE::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -63,59 +63,66 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
APE::AudioProperties::AudioProperties(File *file, long long streamLength, ReadStyle) :
TagLib::AudioProperties(),
APE::Properties::Properties(File *, 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())
{
read(file, streamLength);
}
APE::AudioProperties::~AudioProperties()
APE::Properties::~Properties()
{
delete d;
}
int APE::AudioProperties::length() const
int APE::Properties::length() const
{
return lengthInSeconds();
}
int APE::AudioProperties::lengthInSeconds() const
int APE::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int APE::AudioProperties::lengthInMilliseconds() const
int APE::Properties::lengthInMilliseconds() const
{
return d->length;
}
int APE::AudioProperties::bitrate() const
int APE::Properties::bitrate() const
{
return d->bitrate;
}
int APE::AudioProperties::sampleRate() const
int APE::Properties::sampleRate() const
{
return d->sampleRate;
}
int APE::AudioProperties::channels() const
int APE::Properties::channels() const
{
return d->channels;
}
int APE::AudioProperties::version() const
int APE::Properties::version() const
{
return d->version;
}
int APE::AudioProperties::bitsPerSample() const
int APE::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
unsigned int APE::AudioProperties::sampleFrames() const
unsigned int APE::Properties::sampleFrames() const
{
return d->sampleFrames;
}
@@ -131,14 +138,14 @@ namespace
if(header.size() < 6 || !header.startsWith("MAC "))
return -1;
return header.toUInt16LE(4);
return header.toUShort(4, false);
}
}
void APE::AudioProperties::read(File *file, long long streamLength)
void APE::Properties::read(File *file, long streamLength)
{
// First, we assume that the file pointer is set at the first descriptor.
long long offset = file->tell();
long offset = file->tell();
int version = headerVersion(file->readBlock(6));
// Next, we look for the descriptor.
@@ -149,7 +156,7 @@ void APE::AudioProperties::read(File *file, long long streamLength)
}
if(version < 0) {
debug("APE::AudioProperties::read() -- APE descriptor not found");
debug("APE::Properties::read() -- APE descriptor not found");
return;
}
@@ -167,17 +174,17 @@ void APE::AudioProperties::read(File *file, long long streamLength)
}
}
void APE::AudioProperties::analyzeCurrent(File *file)
void APE::Properties::analyzeCurrent(File *file)
{
// Read the descriptor
file->seek(2, File::Current);
const ByteVector descriptor = file->readBlock(44);
if(descriptor.size() < 44) {
debug("APE::AudioProperties::analyzeCurrent() -- descriptor is too short.");
debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
return;
}
const unsigned int descriptorBytes = descriptor.toUInt32LE(0);
const unsigned int descriptorBytes = descriptor.toUInt(0, false);
if((descriptorBytes - 52) > 0)
file->seek(descriptorBytes - 52, File::Current);
@@ -185,39 +192,39 @@ void APE::AudioProperties::analyzeCurrent(File *file)
// Read the header
const ByteVector header = file->readBlock(24);
if(header.size() < 24) {
debug("APE::AudioProperties::analyzeCurrent() -- MAC header is too short.");
debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
return;
}
// Get the APE info
d->channels = header.toUInt16LE(18);
d->sampleRate = header.toUInt32LE(20);
d->bitsPerSample = header.toUInt16LE(16);
d->channels = header.toShort(18, false);
d->sampleRate = header.toUInt(20, false);
d->bitsPerSample = header.toShort(16, false);
const unsigned int totalFrames = header.toUInt32LE(12);
const unsigned int totalFrames = header.toUInt(12, false);
if(totalFrames == 0)
return;
const unsigned int blocksPerFrame = header.toUInt32LE(4);
const unsigned int finalFrameBlocks = header.toUInt32LE(8);
const unsigned int blocksPerFrame = header.toUInt(4, false);
const unsigned int finalFrameBlocks = header.toUInt(8, false);
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
}
void APE::AudioProperties::analyzeOld(File *file)
void APE::Properties::analyzeOld(File *file)
{
const ByteVector header = file->readBlock(26);
if(header.size() < 26) {
debug("APE::AudioProperties::analyzeOld() -- MAC header is too short.");
debug("APE::Properties::analyzeOld() -- MAC header is too short.");
return;
}
const unsigned int totalFrames = header.toUInt32LE(18);
const unsigned int totalFrames = header.toUInt(18, false);
// Fail on 0 length APE files (catches non-finalized APE files)
if(totalFrames == 0)
return;
const short compressionLevel = header.toUInt32LE(0);
const short compressionLevel = header.toShort(0, false);
unsigned int blocksPerFrame;
if(d->version >= 3950)
blocksPerFrame = 73728 * 4;
@@ -227,19 +234,19 @@ void APE::AudioProperties::analyzeOld(File *file)
blocksPerFrame = 9216;
// Get the APE info
d->channels = header.toUInt16LE(4);
d->sampleRate = header.toUInt32LE(6);
d->channels = header.toShort(4, false);
d->sampleRate = header.toUInt(6, false);
const unsigned int finalFrameBlocks = header.toUInt32LE(22);
const unsigned int finalFrameBlocks = header.toUInt(22, false);
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
// Get the bit depth from the RIFF-fmt chunk.
file->seek(16, File::Current);
const ByteVector fmt = file->readBlock(28);
if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
debug("APE::AudioProperties::analyzeOld() -- fmt header is too short.");
debug("APE::Properties::analyzeOld() -- fmt header is too short.");
return;
}
d->bitsPerSample = fmt.toUInt16LE(26);
d->bitsPerSample = fmt.toShort(26, false);
}

View File

@@ -46,19 +46,27 @@ namespace TagLib {
* API.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of APE::Properties with the data read from the
* APE::File \a file.
*
* \deprecated
*/
AudioProperties(File *file, long long streamLength, ReadStyle style = Average);
TAGLIB_DEPRECATED Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this APE::AudioProperties instance.
* Create an instance of APE::Properties with the data read from the
* APE::File \a file.
*/
virtual ~AudioProperties();
Properties(File *file, long streamLength, ReadStyle style = Average);
/*!
* Destroys this APE::Properties instance.
*/
virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -68,7 +76,7 @@ namespace TagLib {
*
* \deprecated
*/
virtual int length() const;
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -76,14 +84,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
@@ -116,7 +126,11 @@ namespace TagLib {
int version() const;
private:
void read(File *file, long long streamLength);
Properties(const Properties &);
Properties &operator=(const Properties &);
void read(File *file, long streamLength);
void analyzeCurrent(File *file);
void analyzeOld(File *file);

View File

@@ -34,7 +34,6 @@
#include <tfile.h>
#include <tstring.h>
#include <tmap.h>
#include <tpicturemap.h>
#include <tpropertymap.h>
#include <tdebug.h>
#include <tutils.h>
@@ -81,7 +80,7 @@ public:
footerLocation(0) {}
File *file;
long long footerLocation;
long footerLocation;
Footer footer;
ItemListMap itemListMap;
@@ -97,7 +96,7 @@ APE::Tag::Tag() :
{
}
APE::Tag::Tag(TagLib::File *file, long long footerLocation) :
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
TagLib::Tag(),
d(new TagPrivate())
{
@@ -166,42 +165,6 @@ unsigned int APE::Tag::track() const
return d->itemListMap["TRACK"].toString().toInt();
}
TagLib::PictureMap APE::Tag::pictures() const
{
PictureMap map;
if(d->itemListMap.contains(FRONT_COVER)) {
Item front = d->itemListMap[FRONT_COVER];
if(Item::Binary == front.type()) {
ByteVector picture = front.binaryData();
const size_t index = picture.find('\0');
if(index < picture.size()) {
ByteVector desc = picture.mid(0, index + 1);
String mime = "image/jpeg";
ByteVector data = picture.mid(index + 1);
Picture p(data, Picture::FrontCover, mime, desc);
map.insert(p);
}
}
}
if(d->itemListMap.contains(BACK_COVER)) {
Item back = d->itemListMap[BACK_COVER];
if(Item::Binary == back.type()) {
ByteVector picture = back.binaryData();
const size_t index = picture.find('\0');
if(index < picture.size()) {
ByteVector desc = picture.mid(0, index + 1);
String mime = "image/jpeg";
ByteVector data = picture.mid(index + 1);
Picture p(data, Picture::BackCover, mime, desc);
map.insert(p);
}
}
}
return PictureMap(map);
}
void APE::Tag::setTitle(const String &s)
{
addValue("TITLE", s, true);
@@ -243,63 +206,6 @@ void APE::Tag::setTrack(unsigned int i)
addValue("TRACK", String::number(i), true);
}
void APE::Tag::setPictures(const PictureMap &l)
{
removeItem(FRONT_COVER);
removeItem(BACK_COVER);
for(PictureMap::ConstIterator pictureMapIt = l.begin();
pictureMapIt != l.end();
++pictureMapIt) {
Picture::Type type = pictureMapIt->first;
if(Picture::FrontCover != type && Picture::BackCover != type) {
std::cout << "APE: Trying to add a picture with wrong type"
<< std::endl;
continue;
}
const char *id;
switch(type) {
case Picture::FrontCover:
id = FRONT_COVER;
break;
case Picture::BackCover:
id = BACK_COVER;
break;
default:
id = FRONT_COVER;
break;
}
PictureList list = pictureMapIt->second;
for(PictureList::ConstIterator pictureListIt = list.begin();
pictureListIt != list.end();
++pictureListIt) {
Picture picture = *pictureListIt;
if(d->itemListMap.contains(id)) {
std::cout << "APE: Already added a picture of type "
<< id
<< " '"
<< picture.description()
<< "' "
<< "and next are being ignored"
<< std::endl;
break;
}
ByteVector data = picture.description().data(String::Latin1)
.append('\0')
.append(picture.data());
Item item;
item.setKey(id);
item.setType(Item::Binary);
item.setBinaryData(data);
setItem(item.key(), item);
}
}
}
namespace
{
// conversions of tag keys between what we use in PropertyMap and what's usual
@@ -330,7 +236,7 @@ PropertyMap APE::Tag::properties() const
if(tagName == keyConversions[i][1])
tagName = keyConversions[i][0];
}
properties[tagName].append(it->second.values());
properties[tagName].append(it->second.toStringList());
}
}
return properties;
@@ -487,7 +393,7 @@ ByteVector APE::Tag::render() const
}
d->footer.setItemCount(itemCount);
d->footer.setTagSize(static_cast<unsigned int>(data.size() + Footer::size()));
d->footer.setTagSize(data.size() + Footer::size());
d->footer.setHeaderPresent(true);
return d->footer.renderHeader() + data + d->footer.renderFooter();
@@ -500,18 +406,18 @@ void APE::Tag::parse(const ByteVector &data)
if(data.size() < 11)
return;
size_t pos = 0;
unsigned int pos = 0;
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
const size_t nullPos = data.find('\0', pos + 8);
if(nullPos == ByteVector::npos()) {
const int nullPos = data.find('\0', pos + 8);
if(nullPos < 0) {
debug("APE::Tag::parse() - Couldn't find a key/value separator. Stopped parsing.");
return;
}
const size_t keyLength = nullPos - pos - 8;
const size_t valLegnth = data.toUInt32LE(pos);
const unsigned int keyLength = nullPos - pos - 8;
const unsigned int valLegnth = data.toUInt(pos, false);
if(keyLength >= MinKeyLength
&& keyLength <= MaxKeyLength

View File

@@ -34,9 +34,6 @@
#include "apeitem.h"
#define FRONT_COVER "COVER ART (FRONT)"
#define BACK_COVER "COVER ART (BACK)"
namespace TagLib {
class File;
@@ -52,7 +49,7 @@ namespace TagLib {
*
* \see APE::Tag::itemListMap()
*/
typedef Map<String, Item> ItemListMap;
typedef Map<const String, Item> ItemListMap;
//! An APE tag implementation
@@ -69,7 +66,7 @@ namespace TagLib {
* Create an APE tag and parse the data in \a file with APE footer at
* \a tagOffset.
*/
Tag(TagLib::File *file, long long footerLocation);
Tag(TagLib::File *file, long footerLocation);
/*!
* Destroys this Tag instance.
@@ -98,15 +95,6 @@ namespace TagLib {
virtual unsigned int year() const;
virtual unsigned int track() const;
/**
* @brief pictures
* According to :
* http://www.hydrogenaud.io/forums/index.php?showtopic=40603&st=50&p=504669&#entry504669
* http://git.videolan.org/?p=vlc.git;a=blob;f=modules/meta_engine/taglib.cpp
* @return
*/
virtual PictureMap pictures() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
@@ -114,7 +102,6 @@ namespace TagLib {
virtual void setGenre(const String &s);
virtual void setYear(unsigned int i);
virtual void setTrack(unsigned int i);
virtual void setPictures(const PictureMap &l);
/*!
* Implements the unified tag dictionary interface -- export function.

View File

@@ -1,4 +1,4 @@
/**************************************************************************
/**************************************************************************
copyright : (C) 2005-2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
@@ -25,7 +25,7 @@
#include <taglib.h>
#include <tdebug.h>
#include <tsmartptr.h>
#include <trefcounter.h>
#include "asfattribute.h"
#include "asffile.h"
@@ -33,35 +33,21 @@
using namespace TagLib;
namespace
{
struct AttributeData
{
AttributeData() :
numericValue(0),
stream(0),
language(0) {}
ASF::Attribute::AttributeTypes type;
String stringValue;
ByteVector byteVectorValue;
ASF::Picture pictureValue;
unsigned long long numericValue;
int stream;
int language;
};
}
class ASF::Attribute::AttributePrivate
class ASF::Attribute::AttributePrivate : public RefCounter
{
public:
AttributePrivate() :
data(new AttributeData())
{
data->pictureValue = ASF::Picture::fromInvalid();
}
SHARED_PTR<AttributeData> data;
pictureValue(ASF::Picture::fromInvalid()),
numericValue(0),
stream(0),
language(0) {}
AttributeTypes type;
String stringValue;
ByteVector byteVectorValue;
ASF::Picture pictureValue;
unsigned long long numericValue;
int stream;
int language;
};
////////////////////////////////////////////////////////////////////////////////
@@ -71,61 +57,62 @@ public:
ASF::Attribute::Attribute() :
d(new AttributePrivate())
{
d->data->type = UnicodeType;
d->type = UnicodeType;
}
ASF::Attribute::Attribute(const ASF::Attribute &other) :
d(new AttributePrivate(*other.d))
d(other.d)
{
d->ref();
}
ASF::Attribute::Attribute(const String &value) :
d(new AttributePrivate())
{
d->data->type = UnicodeType;
d->data->stringValue = value;
d->type = UnicodeType;
d->stringValue = value;
}
ASF::Attribute::Attribute(const ByteVector &value) :
d(new AttributePrivate())
{
d->data->type = BytesType;
d->data->byteVectorValue = value;
d->type = BytesType;
d->byteVectorValue = value;
}
ASF::Attribute::Attribute(const ASF::Picture &value) :
d(new AttributePrivate())
{
d->data->type = BytesType;
d->data->pictureValue = value;
d->type = BytesType;
d->pictureValue = value;
}
ASF::Attribute::Attribute(unsigned int value) :
d(new AttributePrivate())
{
d->data->type = DWordType;
d->data->numericValue = value;
d->type = DWordType;
d->numericValue = value;
}
ASF::Attribute::Attribute(unsigned long long value) :
d(new AttributePrivate())
{
d->data->type = QWordType;
d->data->numericValue = value;
d->type = QWordType;
d->numericValue = value;
}
ASF::Attribute::Attribute(unsigned short value) :
d(new AttributePrivate())
{
d->data->type = WordType;
d->data->numericValue = value;
d->type = WordType;
d->numericValue = value;
}
ASF::Attribute::Attribute(bool value) :
d(new AttributePrivate())
{
d->data->type = BoolType;
d->data->numericValue = value;
d->type = BoolType;
d->numericValue = value;
}
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
@@ -143,62 +130,62 @@ void ASF::Attribute::swap(Attribute &other)
ASF::Attribute::~Attribute()
{
delete d;
if(d->deref())
delete d;
}
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
{
return d->data->type;
return d->type;
}
String ASF::Attribute::toString() const
{
return d->data->stringValue;
return d->stringValue;
}
ByteVector ASF::Attribute::toByteVector() const
{
if(d->data->pictureValue.isValid())
return d->data->pictureValue.render();
return d->data->byteVectorValue;
if(d->pictureValue.isValid())
return d->pictureValue.render();
return d->byteVectorValue;
}
unsigned short ASF::Attribute::toBool() const
{
return d->data->numericValue ? 1 : 0;
return d->numericValue ? 1 : 0;
}
unsigned short ASF::Attribute::toUShort() const
{
return static_cast<unsigned short>(d->data->numericValue);
return static_cast<unsigned short>(d->numericValue);
}
unsigned int ASF::Attribute::toUInt() const
{
return static_cast<unsigned int>(d->data->numericValue);
return static_cast<unsigned int>(d->numericValue);
}
unsigned long long ASF::Attribute::toULongLong() const
{
return static_cast<unsigned long long>(d->data->numericValue);
return static_cast<unsigned long long>(d->numericValue);
}
ASF::Picture ASF::Attribute::toPicture() const
{
return d->data->pictureValue;
return d->pictureValue;
}
String ASF::Attribute::parse(ASF::File &f, int kind)
{
unsigned int size, nameLength;
String name;
d->data->pictureValue = Picture::fromInvalid();
d->pictureValue = Picture::fromInvalid();
// extended content descriptor
if(kind == 0) {
nameLength = readWORD(&f);
name = readString(&f, nameLength);
d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f));
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readWORD(&f);
}
// metadata & metadata library
@@ -206,11 +193,11 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
int temp = readWORD(&f);
// metadata library
if(kind == 2) {
d->data->language = temp;
d->language = temp;
}
d->data->stream = readWORD(&f);
d->stream = readWORD(&f);
nameLength = readWORD(&f);
d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f));
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readDWORD(&f);
name = readString(&f, nameLength);
}
@@ -219,42 +206,42 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
debug("ASF::Attribute::parse() -- Value larger than 64kB");
}
switch(d->data->type) {
switch(d->type) {
case WordType:
d->data->numericValue = readWORD(&f);
d->numericValue = readWORD(&f);
break;
case BoolType:
if(kind == 0) {
d->data->numericValue = (readDWORD(&f) != 0);
d->numericValue = (readDWORD(&f) != 0);
}
else {
d->data->numericValue = (readWORD(&f) != 0);
d->numericValue = (readWORD(&f) != 0);
}
break;
case DWordType:
d->data->numericValue = readDWORD(&f);
d->numericValue = readDWORD(&f);
break;
case QWordType:
d->data->numericValue = readQWORD(&f);
d->numericValue = readQWORD(&f);
break;
case UnicodeType:
d->data->stringValue = readString(&f, size);
d->stringValue = readString(&f, size);
break;
case BytesType:
case GuidType:
d->data->byteVectorValue = f.readBlock(size);
d->byteVectorValue = f.readBlock(size);
break;
}
if(d->data->type == BytesType && name == "WM/Picture") {
d->data->pictureValue.parse(d->data->byteVectorValue);
if(d->data->pictureValue.isValid()) {
d->data->byteVectorValue.clear();
if(d->type == BytesType && name == "WM/Picture") {
d->pictureValue.parse(d->byteVectorValue);
if(d->pictureValue.isValid()) {
d->byteVectorValue.clear();
}
}
@@ -263,7 +250,7 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
int ASF::Attribute::dataSize() const
{
switch (d->data->type) {
switch (d->type) {
case WordType:
return 2;
case BoolType:
@@ -273,12 +260,12 @@ int ASF::Attribute::dataSize() const
case QWordType:
return 5;
case UnicodeType:
return static_cast<int>(d->data->stringValue.size() * 2 + 2);
return d->stringValue.size() * 2 + 2;
case BytesType:
if(d->data->pictureValue.isValid())
return d->data->pictureValue.dataSize();
if(d->pictureValue.isValid())
return d->pictureValue.dataSize();
case GuidType:
return static_cast<int>(d->data->byteVectorValue.size());
return d->byteVectorValue.size();
}
return 0;
}
@@ -287,55 +274,55 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
{
ByteVector data;
switch (d->data->type) {
switch (d->type) {
case WordType:
data.append(ByteVector::fromUInt16LE(toUShort()));
data.append(ByteVector::fromShort(toUShort(), false));
break;
case BoolType:
if(kind == 0) {
data.append(ByteVector::fromUInt32LE(toBool()));
data.append(ByteVector::fromUInt(toBool(), false));
}
else {
data.append(ByteVector::fromUInt16LE(toBool()));
data.append(ByteVector::fromShort(toBool(), false));
}
break;
case DWordType:
data.append(ByteVector::fromUInt32LE(toUInt()));
data.append(ByteVector::fromUInt(toUInt(), false));
break;
case QWordType:
data.append(ByteVector::fromUInt64LE(toULongLong()));
data.append(ByteVector::fromLongLong(toULongLong(), false));
break;
case UnicodeType:
data.append(renderString(d->data->stringValue));
data.append(renderString(d->stringValue));
break;
case BytesType:
if(d->data->pictureValue.isValid()) {
data.append(d->data->pictureValue.render());
if(d->pictureValue.isValid()) {
data.append(d->pictureValue.render());
break;
}
case GuidType:
data.append(d->data->byteVectorValue);
data.append(d->byteVectorValue);
break;
}
if(kind == 0) {
data = renderString(name, true) +
ByteVector::fromUInt16LE((int)d->data->type) +
ByteVector::fromUInt16LE(data.size()) +
ByteVector::fromShort((int)d->type, false) +
ByteVector::fromShort(data.size(), false) +
data;
}
else {
ByteVector nameData = renderString(name);
data = ByteVector::fromUInt16LE(kind == 2 ? d->data->language : 0) +
ByteVector::fromUInt16LE(d->data->stream) +
ByteVector::fromUInt16LE(nameData.size()) +
ByteVector::fromUInt16LE((int)d->data->type) +
ByteVector::fromUInt32LE(data.size()) +
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
ByteVector::fromShort(d->stream, false) +
ByteVector::fromShort(nameData.size(), false) +
ByteVector::fromShort((int)d->type, false) +
ByteVector::fromUInt(data.size(), false) +
nameData +
data;
}
@@ -345,20 +332,20 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
int ASF::Attribute::language() const
{
return d->data->language;
return d->language;
}
void ASF::Attribute::setLanguage(int value)
{
d->data->language = value;
d->language = value;
}
int ASF::Attribute::stream() const
{
return d->data->stream;
return d->stream;
}
void ASF::Attribute::setStream(int value)
{
d->data->stream = value;
d->stream = value;
}

View File

@@ -36,6 +36,7 @@ namespace TagLib
namespace ASF
{
class File;
class Picture;
@@ -184,13 +185,17 @@ namespace TagLib
*/
void setStream(int value);
#ifndef DO_NOT_DOCUMENT
/* THIS IS PRIVATE, DON'T TOUCH IT! */
String parse(ASF::File &file, int kind = 0);
#endif
//! Returns the size of the stored data
int dataSize() const;
private:
friend class File;
String parse(ASF::File &file, int kind = 0);
ByteVector render(const String &name, int kind = 0) const;
class AttributePrivate;

View File

@@ -28,7 +28,6 @@
#include <tpropertymap.h>
#include <tstring.h>
#include <tagutils.h>
#include <tsmartptr.h>
#include "asffile.h"
#include "asftag.h"
@@ -51,24 +50,37 @@ public:
class MetadataObject;
class MetadataLibraryObject;
typedef List<SHARED_PTR<BaseObject> > ObjectList;
typedef ObjectList::ConstIterator ObjectConstIterator;
FilePrivate():
headerSize(0) {}
headerSize(0),
tag(0),
properties(0),
contentDescriptionObject(0),
extendedContentDescriptionObject(0),
headerExtensionObject(0),
metadataObject(0),
metadataLibraryObject(0)
{
objects.setAutoDelete(true);
}
~FilePrivate()
{
delete tag;
delete properties;
}
unsigned long long headerSize;
SCOPED_PTR<ASF::Tag> tag;
SCOPED_PTR<ASF::AudioProperties> properties;
ASF::Tag *tag;
ASF::Properties *properties;
ObjectList objects;
List<BaseObject *> objects;
SHARED_PTR<ContentDescriptionObject> contentDescriptionObject;
SHARED_PTR<ExtendedContentDescriptionObject> extendedContentDescriptionObject;
SHARED_PTR<HeaderExtensionObject> headerExtensionObject;
SHARED_PTR<MetadataObject> metadataObject;
SHARED_PTR<MetadataLibraryObject> metadataLibraryObject;
ContentDescriptionObject *contentDescriptionObject;
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
HeaderExtensionObject *headerExtensionObject;
MetadataObject *metadataObject;
MetadataLibraryObject *metadataLibraryObject;
};
namespace
@@ -101,7 +113,7 @@ class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::Bas
{
ByteVector myGuid;
public:
explicit UnknownObject(const ByteVector &guid);
UnknownObject(const ByteVector &guid);
ByteVector guid() const;
};
@@ -157,7 +169,7 @@ public:
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
{
public:
ObjectList objects;
List<ASF::File::FilePrivate::BaseObject *> objects;
HeaderExtensionObject();
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
@@ -182,7 +194,7 @@ private:
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
{
data.clear();
if(size > 24 && static_cast<long long>(size) <= file->length())
if(size > 24 && size <= (unsigned int)(file->length()))
data = file->readBlock(size - 24);
else
data = ByteVector();
@@ -190,7 +202,7 @@ void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int siz
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
{
return guid() + ByteVector::fromUInt64LE(data.size() + 24) + data;
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
}
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
@@ -215,8 +227,8 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsign
return;
}
const long long duration = data.toInt64LE(40);
const long long preroll = data.toInt64LE(56);
const long long duration = data.toLongLong(40, false);
const long long preroll = data.toLongLong(56, false);
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
}
@@ -233,11 +245,11 @@ void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsi
return;
}
file->d->properties->setCodec(data.toUInt16LE(54));
file->d->properties->setChannels(data.toUInt16LE(56));
file->d->properties->setSampleRate(data.toUInt32LE(58));
file->d->properties->setBitrate(static_cast<int>(data.toUInt32LE(62) * 8.0 / 1000.0 + 0.5));
file->d->properties->setBitsPerSample(data.toUInt16LE(68));
file->d->properties->setCodec(data.toUShort(54, false));
file->d->properties->setChannels(data.toUShort(56, false));
file->d->properties->setSampleRate(data.toUInt(58, false));
file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
file->d->properties->setBitsPerSample(data.toUShort(68, false));
}
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
@@ -267,11 +279,11 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *f
const ByteVector v4 = renderString(file->d->tag->comment());
const ByteVector v5 = renderString(file->d->tag->rating());
data.clear();
data.append(ByteVector::fromUInt16LE(v1.size()));
data.append(ByteVector::fromUInt16LE(v2.size()));
data.append(ByteVector::fromUInt16LE(v3.size()));
data.append(ByteVector::fromUInt16LE(v4.size()));
data.append(ByteVector::fromUInt16LE(v5.size()));
data.append(ByteVector::fromShort(v1.size(), false));
data.append(ByteVector::fromShort(v2.size(), false));
data.append(ByteVector::fromShort(v3.size(), false));
data.append(ByteVector::fromShort(v4.size(), false));
data.append(ByteVector::fromShort(v5.size(), false));
data.append(v1);
data.append(v2);
data.append(v3);
@@ -298,7 +310,7 @@ void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromUInt16LE(attributeData.size()));
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -321,7 +333,7 @@ void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromUInt16LE(attributeData.size()));
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -344,13 +356,14 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsig
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromUInt16LE(attributeData.size()));
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
{
objects.setAutoDelete(true);
}
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
@@ -375,17 +388,17 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
file->setValid(false);
break;
}
SHARED_PTR<BaseObject> obj;
BaseObject *obj;
if(guid == metadataGuid) {
file->d->metadataObject.reset(new MetadataObject());
file->d->metadataObject = new MetadataObject();
obj = file->d->metadataObject;
}
else if(guid == metadataLibraryGuid) {
file->d->metadataLibraryObject.reset(new MetadataLibraryObject());
file->d->metadataLibraryObject = new MetadataLibraryObject();
obj = file->d->metadataLibraryObject;
}
else {
obj.reset(new UnknownObject(guid));
obj = new UnknownObject(guid);
}
obj->parse(file, (unsigned int)size);
objects.append(obj);
@@ -396,10 +409,10 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
{
data.clear();
for(ObjectConstIterator it = objects.begin(); it != objects.end(); ++it) {
for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
data.append((*it)->render(file));
}
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt32LE(data.size()) + data;
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);
}
@@ -418,7 +431,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
unsigned int pos = 16;
const int count = data.toUInt32LE(pos);
const int count = data.toUInt(pos, false);
pos += 4;
for(int i = 0; i < count; ++i) {
@@ -426,22 +439,22 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
if(pos >= data.size())
break;
const CodecType type = static_cast<CodecType>(data.toUInt16LE(pos));
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
pos += 2;
int nameLength = data.toUInt16LE(pos);
int nameLength = data.toUShort(pos, false);
pos += 2;
const unsigned int namePos = pos;
pos += nameLength * 2;
const int descLength = data.toUInt16LE(pos);
const int descLength = data.toUShort(pos, false);
pos += 2;
const unsigned int descPos = pos;
pos += descLength * 2;
const int infoLength = data.toUInt16LE(pos);
const int infoLength = data.toUShort(pos, false);
pos += 2 + infoLength * 2;
if(type == CodecListObject::Audio) {
@@ -474,7 +487,7 @@ bool ASF::File::isSupported(IOStream *stream)
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::File::File(FileName file, bool, AudioProperties::ReadStyle) :
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
@@ -482,7 +495,7 @@ ASF::File::File(FileName file, bool, AudioProperties::ReadStyle) :
read();
}
ASF::File::File(IOStream *stream, bool, AudioProperties::ReadStyle) :
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
@@ -497,12 +510,27 @@ ASF::File::~File()
ASF::Tag *ASF::File::tag() const
{
return d->tag.get();
return d->tag;
}
ASF::AudioProperties *ASF::File::audioProperties() const
PropertyMap ASF::File::properties() const
{
return d->properties.get();
return d->tag->properties();
}
void ASF::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag->removeUnsupportedProperties(properties);
}
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
ASF::Properties *ASF::File::audioProperties() const
{
return d->properties;
}
bool ASF::File::save()
@@ -518,23 +546,23 @@ bool ASF::File::save()
}
if(!d->contentDescriptionObject) {
d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject());
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
d->objects.append(d->contentDescriptionObject);
}
if(!d->extendedContentDescriptionObject) {
d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject());
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
d->objects.append(d->extendedContentDescriptionObject);
}
if(!d->headerExtensionObject) {
d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject());
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
d->objects.append(d->headerExtensionObject);
}
if(!d->metadataObject) {
d->metadataObject.reset(new FilePrivate::MetadataObject());
d->metadataObject = new FilePrivate::MetadataObject();
d->headerExtensionObject->objects.append(d->metadataObject);
}
if(!d->metadataLibraryObject) {
d->metadataLibraryObject.reset(new FilePrivate::MetadataLibraryObject());
d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
}
@@ -573,16 +601,16 @@ bool ASF::File::save()
}
ByteVector data;
for(FilePrivate::ObjectConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
data.append((*it)->render(this));
}
seek(16);
writeBlock(ByteVector::fromUInt64LE(data.size() + 30));
writeBlock(ByteVector::fromUInt32LE(d->objects.size()));
writeBlock(ByteVector::fromLongLong(data.size() + 30, false));
writeBlock(ByteVector::fromUInt(d->objects.size(), false));
writeBlock(ByteVector("\x01\x02", 2));
insert(data, 30, static_cast<size_t>(d->headerSize - 30));
insert(data, 30, static_cast<unsigned long>(d->headerSize - 30));
d->headerSize = data.size() + 30;
@@ -604,8 +632,8 @@ void ASF::File::read()
return;
}
d->tag.reset(new ASF::Tag());
d->properties.reset(new ASF::AudioProperties());
d->tag = new ASF::Tag();
d->properties = new ASF::Properties();
bool ok;
d->headerSize = readQWORD(this, &ok);
@@ -620,8 +648,8 @@ void ASF::File::read()
}
seek(2, Current);
SHARED_PTR<FilePrivate::FilePropertiesObject> filePropertiesObject;
SHARED_PTR<FilePrivate::StreamPropertiesObject> streamPropertiesObject;
FilePrivate::FilePropertiesObject *filePropertiesObject = 0;
FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0;
for(int i = 0; i < numObjects; i++) {
const ByteVector guid = readBlock(16);
if(guid.size() != 16) {
@@ -633,29 +661,29 @@ void ASF::File::read()
setValid(false);
break;
}
SHARED_PTR<FilePrivate::BaseObject> obj;
FilePrivate::BaseObject *obj;
if(guid == filePropertiesGuid) {
filePropertiesObject.reset(new FilePrivate::FilePropertiesObject());
filePropertiesObject = new FilePrivate::FilePropertiesObject();
obj = filePropertiesObject;
}
else if(guid == streamPropertiesGuid) {
streamPropertiesObject.reset(new FilePrivate::StreamPropertiesObject());
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
obj = streamPropertiesObject;
}
else if(guid == contentDescriptionGuid) {
d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject());
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
obj = d->contentDescriptionObject;
}
else if(guid == extendedContentDescriptionGuid) {
d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject());
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
obj = d->extendedContentDescriptionObject;
}
else if(guid == headerExtensionGuid) {
d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject());
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
obj = d->headerExtensionObject;
}
else if(guid == codecListGuid) {
obj.reset(new FilePrivate::CodecListObject());
obj = new FilePrivate::CodecListObject();
}
else {
if(guid == contentEncryptionGuid ||
@@ -663,7 +691,7 @@ void ASF::File::read()
guid == advancedContentEncryptionGuid) {
d->properties->setEncrypted(true);
}
obj.reset(new FilePrivate::UnknownObject(guid));
obj = new FilePrivate::UnknownObject(guid);
}
obj->parse(this, size);
d->objects.append(obj);

View File

@@ -48,21 +48,17 @@ namespace TagLib {
public:
/*!
* Contructs an ASF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an ASF file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an ASF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an ASF file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
@@ -72,7 +68,7 @@ namespace TagLib {
* responsible for deleting it after the File object.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@@ -91,10 +87,26 @@ namespace TagLib {
*/
virtual Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the ASF audio properties for this file.
*/
virtual AudioProperties *audioProperties() const;
virtual Properties *audioProperties() const;
/*!
* Save the file.

View File

@@ -25,7 +25,7 @@
#include <taglib.h>
#include <tdebug.h>
#include <tsmartptr.h>
#include <trefcounter.h>
#include "asfattribute.h"
#include "asffile.h"
@@ -34,25 +34,14 @@
using namespace TagLib;
namespace
{
struct PictureData
{
bool valid;
ASF::Picture::Type type;
String mimeType;
String description;
ByteVector picture;
};
}
class ASF::Picture::PicturePrivate
class ASF::Picture::PicturePrivate : public RefCounter
{
public:
PicturePrivate() :
data(new PictureData()) {}
SHARED_PTR<PictureData> data;
bool valid;
Type type;
String mimeType;
String description;
ByteVector picture;
};
////////////////////////////////////////////////////////////////////////////////
@@ -62,69 +51,71 @@ public:
ASF::Picture::Picture() :
d(new PicturePrivate())
{
d->data->valid = true;
d->valid = true;
}
ASF::Picture::Picture(const Picture& other) :
d(new PicturePrivate(*other.d))
d(other.d)
{
d->ref();
}
ASF::Picture::~Picture()
{
delete d;
if(d->deref())
delete d;
}
bool ASF::Picture::isValid() const
{
return d->data->valid;
return d->valid;
}
String ASF::Picture::mimeType() const
{
return d->data->mimeType;
return d->mimeType;
}
void ASF::Picture::setMimeType(const String &value)
{
d->data->mimeType = value;
d->mimeType = value;
}
ASF::Picture::Type ASF::Picture::type() const
{
return d->data->type;
return d->type;
}
void ASF::Picture::setType(const ASF::Picture::Type& t)
{
d->data->type = t;
d->type = t;
}
String ASF::Picture::description() const
{
return d->data->description;
return d->description;
}
void ASF::Picture::setDescription(const String &desc)
{
d->data->description = desc;
d->description = desc;
}
ByteVector ASF::Picture::picture() const
{
return d->data->picture;
return d->picture;
}
void ASF::Picture::setPicture(const ByteVector &p)
{
d->data->picture = p;
d->picture = p;
}
int ASF::Picture::dataSize() const
{
return static_cast<int>(
9 + (d->data->mimeType.length() + d->data->description.length()) * 2 +
d->data->picture.size());
return
9 + (d->mimeType.length() + d->description.length()) * 2 +
d->picture.size();
}
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
@@ -146,47 +137,47 @@ ByteVector ASF::Picture::render() const
return ByteVector();
return
ByteVector((char)d->data->type) +
ByteVector::fromUInt32LE(d->data->picture.size()) +
renderString(d->data->mimeType) +
renderString(d->data->description) +
d->data->picture;
ByteVector((char)d->type) +
ByteVector::fromUInt(d->picture.size(), false) +
renderString(d->mimeType) +
renderString(d->description) +
d->picture;
}
void ASF::Picture::parse(const ByteVector& bytes)
{
d->data->valid = false;
d->valid = false;
if(bytes.size() < 9)
return;
size_t pos = 0;
d->data->type = (Type)bytes[0]; ++pos;
const unsigned int dataLen = bytes.toUInt32LE(pos); pos+=4;
int pos = 0;
d->type = (Type)bytes[0]; ++pos;
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
const ByteVector nullStringTerminator(2, 0);
size_t endPos = bytes.find(nullStringTerminator, pos, 2);
if(endPos == ByteVector::npos())
int endPos = bytes.find(nullStringTerminator, pos, 2);
if(endPos < 0)
return;
d->data->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
d->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
pos = endPos+2;
endPos = bytes.find(nullStringTerminator, pos, 2);
if(endPos == ByteVector::npos())
if(endPos < 0)
return;
d->data->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
d->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
pos = endPos+2;
if(dataLen + pos != bytes.size())
return;
d->data->picture = bytes.mid(pos, dataLen);
d->data->valid = true;
d->picture = bytes.mid(pos, dataLen);
d->valid = true;
return;
}
ASF::Picture ASF::Picture::fromInvalid()
{
Picture ret;
ret.d->data->valid = false;
ret.d->valid = false;
return ret;
}

View File

@@ -35,7 +35,6 @@ namespace TagLib
{
namespace ASF
{
class Attribute;
//! An ASF attached picture interface implementation
@@ -47,8 +46,7 @@ namespace TagLib
* \see Attribute::toPicture()
* \see Attribute::Attribute(const Picture& picture)
*/
class TAGLIB_EXPORT Picture
{
class TAGLIB_EXPORT Picture {
public:
/*!
@@ -105,7 +103,7 @@ namespace TagLib
Picture();
/*!
* Constructs an picture as a copy of \a other.
* Construct an picture as a copy of \a other.
*/
Picture(const Picture& other);
@@ -208,15 +206,16 @@ namespace TagLib
*/
int dataSize() const;
private:
friend class Attribute;
void parse(const ByteVector &);
#ifndef DO_NOT_DOCUMENT
/* THIS IS PRIVATE, DON'T TOUCH IT! */
void parse(const ByteVector& );
static Picture fromInvalid();
#endif
class PicturePrivate;
PicturePrivate *d;
};
private:
class PicturePrivate;
PicturePrivate *d;
};
}
}

View File

@@ -29,7 +29,7 @@
using namespace TagLib;
class ASF::AudioProperties::PropertiesPrivate
class ASF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -38,7 +38,7 @@ public:
sampleRate(0),
channels(0),
bitsPerSample(0),
codec(ASF::AudioProperties::Unknown),
codec(ASF::Properties::Unknown),
encrypted(false) {}
int length;
@@ -46,7 +46,7 @@ public:
int sampleRate;
int channels;
int bitsPerSample;
ASF::AudioProperties::Codec codec;
ASF::Properties::Codec codec;
String codecName;
String codecDescription;
bool encrypted;
@@ -56,68 +56,68 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::AudioProperties::AudioProperties() :
TagLib::AudioProperties(),
ASF::Properties::Properties() :
AudioProperties(AudioProperties::Average),
d(new PropertiesPrivate())
{
}
ASF::AudioProperties::~AudioProperties()
ASF::Properties::~Properties()
{
delete d;
}
int ASF::AudioProperties::length() const
int ASF::Properties::length() const
{
return lengthInSeconds();
}
int ASF::AudioProperties::lengthInSeconds() const
int ASF::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int ASF::AudioProperties::lengthInMilliseconds() const
int ASF::Properties::lengthInMilliseconds() const
{
return d->length;
}
int ASF::AudioProperties::bitrate() const
int ASF::Properties::bitrate() const
{
return d->bitrate;
}
int ASF::AudioProperties::sampleRate() const
int ASF::Properties::sampleRate() const
{
return d->sampleRate;
}
int ASF::AudioProperties::channels() const
int ASF::Properties::channels() const
{
return d->channels;
}
int ASF::AudioProperties::bitsPerSample() const
int ASF::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
ASF::AudioProperties::Codec ASF::AudioProperties::codec() const
ASF::Properties::Codec ASF::Properties::codec() const
{
return d->codec;
}
String ASF::AudioProperties::codecName() const
String ASF::Properties::codecName() const
{
return d->codecName;
}
String ASF::AudioProperties::codecDescription() const
String ASF::Properties::codecDescription() const
{
return d->codecDescription;
}
bool ASF::AudioProperties::isEncrypted() const
bool ASF::Properties::isEncrypted() const
{
return d->encrypted;
}
@@ -126,32 +126,37 @@ bool ASF::AudioProperties::isEncrypted() const
// private members
////////////////////////////////////////////////////////////////////////////////
void ASF::AudioProperties::setLengthInMilliseconds(int value)
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;
}
void ASF::AudioProperties::setBitrate(int value)
void ASF::Properties::setBitrate(int value)
{
d->bitrate = value;
}
void ASF::AudioProperties::setSampleRate(int value)
void ASF::Properties::setSampleRate(int value)
{
d->sampleRate = value;
}
void ASF::AudioProperties::setChannels(int value)
void ASF::Properties::setChannels(int value)
{
d->channels = value;
}
void ASF::AudioProperties::setBitsPerSample(int value)
void ASF::Properties::setBitsPerSample(int value)
{
d->bitsPerSample = value;
}
void ASF::AudioProperties::setCodec(int value)
void ASF::Properties::setCodec(int value)
{
switch(value)
{
@@ -173,17 +178,17 @@ void ASF::AudioProperties::setCodec(int value)
}
}
void ASF::AudioProperties::setCodecName(const String &value)
void ASF::Properties::setCodecName(const String &value)
{
d->codecName = value;
}
void ASF::AudioProperties::setCodecDescription(const String &value)
void ASF::Properties::setCodecDescription(const String &value)
{
d->codecDescription = value;
}
void ASF::AudioProperties::setEncrypted(bool value)
void ASF::Properties::setEncrypted(bool value)
{
d->encrypted = value;
}

View File

@@ -34,14 +34,11 @@ namespace TagLib {
namespace ASF {
class File;
//! An implementation of ASF audio properties
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
friend class File;
public:
/*!
* Audio codec types can be used in ASF file.
*/
@@ -74,14 +71,14 @@ namespace TagLib {
};
/*!
* Creates an instance of ASF::AudioProperties.
* Creates an instance of ASF::Properties.
*/
AudioProperties();
Properties();
/*!
* Destroys this ASF::AudioProperties instance.
* Destroys this ASF::Properties instance.
*/
virtual ~AudioProperties();
virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -91,7 +88,7 @@ namespace TagLib {
*
* \deprecated
*/
virtual int length() const;
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -99,14 +96,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
@@ -160,7 +159,10 @@ namespace TagLib {
*/
bool isEncrypted() const;
private:
#ifndef DO_NOT_DOCUMENT
// deprecated
void setLength(int value);
void setLengthInMilliseconds(int value);
void setBitrate(int value);
void setSampleRate(int value);
@@ -170,7 +172,9 @@ namespace TagLib {
void setCodecName(const String &value);
void setCodecDescription(const String &value);
void setEncrypted(bool value);
#endif
private:
class PropertiesPrivate;
PropertiesPrivate *d;
};

View File

@@ -23,7 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tpicturemap.h>
#include <tpropertymap.h>
#include "asftag.h"
@@ -111,91 +110,6 @@ String ASF::Tag::genre() const
return String();
}
PictureMap ASF::Tag::pictures() const
{
PictureMap map;
if(d->attributeListMap.contains("WM/Picture")) {
AttributeList list = d->attributeListMap["WM/Picture"];
for(AttributeList::ConstIterator it = list.begin();
it != list.end();
++it) {
ASF::Picture asfPicture = (*it).toPicture();
TagLib::Picture::Type type;
switch(asfPicture.type()) {
case ASF::Picture::FileIcon:
type = TagLib::Picture::FileIcon;
break;
case ASF::Picture::OtherFileIcon:
type = TagLib::Picture::OtherFileIcon;
break;
case ASF::Picture::FrontCover:
type = TagLib::Picture::FrontCover;
break;
case ASF::Picture::BackCover:
type = TagLib::Picture::BackCover;
break;
case ASF::Picture::LeafletPage:
type = TagLib::Picture::LeafletPage;
break;
case ASF::Picture::Media:
type = TagLib::Picture::Media;
break;
case ASF::Picture::LeadArtist:
type = TagLib::Picture::LeadArtist;
break;
case ASF::Picture::Artist:
type = TagLib::Picture::Artist;
break;
case ASF::Picture::Conductor:
type = TagLib::Picture::Conductor;
break;
case ASF::Picture::Band:
type = TagLib::Picture::Band;
break;
case ASF::Picture::Composer:
type = TagLib::Picture::Composer;
break;
case ASF::Picture::Lyricist:
type = TagLib::Picture::Lyricist;
break;
case ASF::Picture::RecordingLocation:
type = TagLib::Picture::RecordingLocation;
break;
case ASF::Picture::DuringRecording:
type = TagLib::Picture::DuringRecording;
break;
case ASF::Picture::DuringPerformance:
type = TagLib::Picture::DuringPerformance;
break;
case ASF::Picture::MovieScreenCapture:
type = TagLib::Picture::MovieScreenCapture;
break;
case ASF::Picture::ColouredFish:
type = TagLib::Picture::ColouredFish;
break;
case ASF::Picture::Illustration:
type = TagLib::Picture::Illustration;
break;
case ASF::Picture::BandLogo:
type = TagLib::Picture::BandLogo;
break;
case ASF::Picture::PublisherLogo:
type = TagLib::Picture::PublisherLogo;
break;
default:
type = TagLib::Picture::Other;
break;
}
TagLib::Picture picture(asfPicture.picture(),
type,
asfPicture.mimeType(),
asfPicture.description());
map.insert(picture);
}
}
return PictureMap(map);
}
void ASF::Tag::setTitle(const String &value)
{
d->title = value;
@@ -241,94 +155,6 @@ void ASF::Tag::setTrack(unsigned int value)
setAttribute("WM/TrackNumber", String::number(value));
}
void ASF::Tag::setPictures(const PictureMap &l)
{
removeItem("WM/Picture");
for(PictureMap::ConstIterator pictureMapIt = l.begin();
pictureMapIt != l.end();
++pictureMapIt)
{
PictureList list = pictureMapIt->second;
for( PictureList::ConstIterator pictureListIt = list.begin();
pictureListIt != list.end();
++pictureListIt)
{
const TagLib::Picture picture = (*pictureListIt);
ASF::Picture asfPicture;
asfPicture.setPicture(picture.data());
asfPicture.setMimeType(picture.mime());
asfPicture.setDescription(picture.description());
switch (picture.type()) {
case TagLib::Picture::Other:
asfPicture.setType(ASF::Picture::Other);
break;
case TagLib::Picture::FileIcon:
asfPicture.setType(ASF::Picture::FileIcon);
break;
case TagLib::Picture::OtherFileIcon:
asfPicture.setType(ASF::Picture::OtherFileIcon);
break;
case TagLib::Picture::FrontCover:
asfPicture.setType(ASF::Picture::FrontCover);
break;
case TagLib::Picture::BackCover:
asfPicture.setType(ASF::Picture::BackCover);
break;
case TagLib::Picture::LeafletPage:
asfPicture.setType(ASF::Picture::LeafletPage);
break;
case TagLib::Picture::Media:
asfPicture.setType(ASF::Picture::Media);
break;
case TagLib::Picture::LeadArtist:
asfPicture.setType(ASF::Picture::LeadArtist);
break;
case TagLib::Picture::Artist:
asfPicture.setType(ASF::Picture::Artist);
break;
case TagLib::Picture::Conductor:
asfPicture.setType(ASF::Picture::Conductor);
break;
case TagLib::Picture::Band:
asfPicture.setType(ASF::Picture::Band);
break;
case TagLib::Picture::Composer:
asfPicture.setType(ASF::Picture::Composer);
break;
case TagLib::Picture::Lyricist:
asfPicture.setType(ASF::Picture::Lyricist);
break;
case TagLib::Picture::RecordingLocation:
asfPicture.setType(ASF::Picture::RecordingLocation);
break;
case TagLib::Picture::DuringRecording:
asfPicture.setType(ASF::Picture::DuringRecording);
break;
case TagLib::Picture::DuringPerformance:
asfPicture.setType(ASF::Picture::DuringPerformance);
break;
case TagLib::Picture::MovieScreenCapture:
asfPicture.setType(ASF::Picture::MovieScreenCapture);
break;
case TagLib::Picture::ColouredFish:
asfPicture.setType(ASF::Picture::ColouredFish);
break;
case TagLib::Picture::Illustration:
asfPicture.setType(ASF::Picture::Illustration);
break;
case TagLib::Picture::BandLogo:
asfPicture.setType(ASF::Picture::BandLogo);
break;
case TagLib::Picture::PublisherLogo:
asfPicture.setType(ASF::Picture::PublisherLogo);
break;
}
addAttribute("WM/Picture", Attribute(asfPicture));
}
}
}
ASF::AttributeListMap& ASF::Tag::attributeListMap()
{
return d->attributeListMap;

View File

@@ -98,8 +98,6 @@ namespace TagLib {
*/
virtual unsigned int track() const;
virtual PictureMap pictures() const;
/*!
* Sets the title to \a s.
*/
@@ -146,8 +144,6 @@ namespace TagLib {
*/
virtual void setTrack(unsigned int i);
virtual void setPictures(const PictureMap &l);
/*!
* Returns true if the tag does not contain any data. This should be
* reimplemented in subclasses that provide more than the basic tagging
@@ -164,6 +160,7 @@ namespace TagLib {
* Returns a reference to the item list map. This is an AttributeListMap of
* all of the items in the tag.
*/
// BIC: return by value
const AttributeListMap &attributeListMap() const;
/*!

View File

@@ -45,7 +45,7 @@ namespace TagLib
return 0;
}
if(ok) *ok = true;
return v.toUInt16LE(0);
return v.toUShort(false);
}
inline unsigned int readDWORD(File *file, bool *ok = 0)
@@ -56,7 +56,7 @@ namespace TagLib
return 0;
}
if(ok) *ok = true;
return v.toUInt32LE(0);
return v.toUInt(false);
}
inline long long readQWORD(File *file, bool *ok = 0)
@@ -67,13 +67,13 @@ namespace TagLib
return 0;
}
if(ok) *ok = true;
return v.toInt64LE(0);
return v.toLongLong(false);
}
inline String readString(File *file, int length)
{
ByteVector data = file->readBlock(length);
size_t size = data.size();
unsigned int size = data.size();
while (size >= 2) {
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
break;
@@ -88,9 +88,9 @@ namespace TagLib
inline ByteVector renderString(const String &str, bool includeLength = false)
{
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromUInt16LE(0);
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
if(includeLength) {
data = ByteVector::fromUInt16LE(data.size()) + data;
data = ByteVector::fromShort(data.size(), false) + data;
}
return data;
}

View File

@@ -23,34 +23,89 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstringlist.h>
#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(); \
else if(dynamic_cast<const ASF::Properties*>(this)) \
return dynamic_cast<const ASF::Properties*>(this)->function_name(); \
else if(dynamic_cast<const FLAC::Properties*>(this)) \
return dynamic_cast<const FLAC::Properties*>(this)->function_name(); \
else if(dynamic_cast<const MP4::Properties*>(this)) \
return dynamic_cast<const MP4::Properties*>(this)->function_name(); \
else if(dynamic_cast<const MPC::Properties*>(this)) \
return dynamic_cast<const MPC::Properties*>(this)->function_name(); \
else if(dynamic_cast<const MPEG::Properties*>(this)) \
return dynamic_cast<const MPEG::Properties*>(this)->function_name(); \
else if(dynamic_cast<const Ogg::Opus::Properties*>(this)) \
return dynamic_cast<const Ogg::Opus::Properties*>(this)->function_name(); \
else if(dynamic_cast<const Ogg::Speex::Properties*>(this)) \
return dynamic_cast<const Ogg::Speex::Properties*>(this)->function_name(); \
else if(dynamic_cast<const TrueAudio::Properties*>(this)) \
return dynamic_cast<const TrueAudio::Properties*>(this)->function_name(); \
else if(dynamic_cast<const RIFF::AIFF::Properties*>(this)) \
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->function_name(); \
else if(dynamic_cast<const RIFF::WAV::Properties*>(this)) \
return dynamic_cast<const RIFF::WAV::Properties*>(this)->function_name(); \
else if(dynamic_cast<const Vorbis::Properties*>(this)) \
return dynamic_cast<const Vorbis::Properties*>(this)->function_name(); \
else if(dynamic_cast<const WavPack::Properties*>(this)) \
return dynamic_cast<const WavPack::Properties*>(this)->function_name(); \
else \
return (default_value);
class AudioProperties::AudioPropertiesPrivate
{
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
AudioProperties::~AudioProperties()
{
}
String AudioProperties::toString() const
int AudioProperties::lengthInSeconds() const
{
StringList desc;
desc.append("Audio");
desc.append(String::number(length()) + " seconds");
desc.append(String::number(bitrate()) + " kbps");
return desc.toString(", ");
VIRTUAL_FUNCTION_WORKAROUND(lengthInSeconds, 0)
}
int AudioProperties::lengthInMilliseconds() const
{
VIRTUAL_FUNCTION_WORKAROUND(lengthInMilliseconds, 0)
}
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////
AudioProperties::AudioProperties() :
AudioProperties::AudioProperties(ReadStyle) :
d(0)
{
}

View File

@@ -27,7 +27,6 @@
#define TAGLIB_AUDIOPROPERTIES_H
#include "taglib_export.h"
#include "tstring.h"
namespace TagLib {
@@ -76,14 +75,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const = 0;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const = 0;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the most appropriate bit rate for the file in kb/s. For constant
@@ -102,27 +103,23 @@ namespace TagLib {
*/
virtual int channels() const = 0;
/*!
* Returns description of the audio file.
*/
virtual String toString() const;
protected:
/*!
* Constructs an audio properties instance. This is protected as this class
* Construct an audio properties instance. This is protected as this class
* should not be instantiated directly, but should be instantiated via its
* subclasses and can be fetched from the FileRef or File APIs.
*
* \see ReadStyle
*/
AudioProperties();
AudioProperties(ReadStyle style);
private:
// Noncopyable. Derived classes as well.
AudioProperties(const AudioProperties &);
AudioProperties &operator=(const AudioProperties &);
class PropertiesPrivate;
PropertiesPrivate *d;
class AudioPropertiesPrivate;
AudioPropertiesPrivate *d;
};
}

View File

@@ -1,166 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "dsdiffdiintag.h"
#include "tstringlist.h"
#include "tpropertymap.h"
#include "tpicturemap.h"
using namespace TagLib;
using namespace DSDIFF::DIIN;
class DSDIFF::DIIN::Tag::TagPrivate
{
public:
TagPrivate()
{
}
String title;
String artist;
};
DSDIFF::DIIN::Tag::Tag() : TagLib::Tag()
{
d = new TagPrivate;
}
DSDIFF::DIIN::Tag::~Tag()
{
delete d;
}
String DSDIFF::DIIN::Tag::title() const
{
return d->title;
}
String DSDIFF::DIIN::Tag::artist() const
{
return d->artist;
}
String DSDIFF::DIIN::Tag::album() const
{
return String();
}
String DSDIFF::DIIN::Tag::comment() const
{
return String();
}
String DSDIFF::DIIN::Tag::genre() const
{
return String();
}
unsigned int DSDIFF::DIIN::Tag::year() const
{
return 0;
}
unsigned int DSDIFF::DIIN::Tag::track() const
{
return 0;
}
PictureMap DSDIFF::DIIN::Tag::pictures() const
{
return PictureMap();
}
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 &)
{
}
void DSDIFF::DIIN::Tag::setComment(const String &)
{
}
void DSDIFF::DIIN::Tag::setGenre(const String &)
{
}
void DSDIFF::DIIN::Tag::setYear(unsigned int)
{
}
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
{
}
void DSDIFF::DIIN::Tag::setPictures( const PictureMap& l )
{
}
PropertyMap DSDIFF::DIIN::Tag::properties() const
{
PropertyMap properties;
properties["TITLE"] = d->title;
properties["ARTIST"] = d->artist;
return properties;
}
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap properties(origProps);
properties.removeEmpty();
StringList oneValueSet;
if(properties.contains("TITLE")) {
d->title = properties["TITLE"].front();
oneValueSet.append("TITLE");
} else
d->title = String();
if(properties.contains("ARTIST")) {
d->artist = properties["ARTIST"].front();
oneValueSet.append("ARTIST");
} else
d->artist = String();
// for each tag that has been set above, remove the first entry in the corresponding
// value list. The others will be returned as unsupported by this format.
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
if(properties[*it].size() == 1)
properties.erase(*it);
else
properties[*it].erase(properties[*it].begin());
}
return properties;
}

View File

@@ -1,160 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSDIFFDIINTAG_H
#define TAGLIB_DSDIFFDIINTAG_H
#include "tag.h"
namespace TagLib {
namespace DSDIFF {
namespace DIIN {
/*!
* Tags from the Edited Master Chunk Info
*
* Only Title and Artist tags are supported
*/
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
Tag();
virtual ~Tag();
/*!
* Returns the track name; if no track name is present in the tag
* String() will be returned.
*/
virtual String title() const;
/*!
* Returns the artist name; if no artist name is present in the tag
* String() will be returned.
*/
virtual String artist() const;
/*!
* Not supported. Therefore always returns String().
*/
virtual String album() const;
/*!
* Not supported. Therefore always returns String().
*/
virtual String comment() const;
/*!
* Not supported. Therefore always returns String().
*/
virtual String genre() const;
/*!
* Not supported. Therefore always returns 0.
*/
virtual unsigned int year() const;
/*!
* Not supported. Therefore always returns 0.
*/
virtual unsigned int track() const;
/*!
* Not supported. Therefore always returns an empty list.
*/
virtual PictureMap pictures() const;
/*!
* Sets the title to \a title. If \a title is String() then this
* value will be cleared.
*/
virtual void setTitle(const String &title);
/*!
* Sets the artist to \a artist. If \a artist is String() then this
* value will be cleared.
*/
virtual void setArtist(const String &artist);
/*!
* Not supported and therefore ignored.
*/
virtual void setAlbum(const String &album);
/*!
* Not supported and therefore ignored.
*/
virtual void setComment(const String &comment);
/*!
* Not supported and therefore ignored.
*/
virtual void setGenre(const String &genre);
/*!
* Not supported and therefore ignored.
*/
virtual void setYear(unsigned int year);
/*!
* Not supported and therefore ignored.
*/
virtual void setTrack(unsigned int track);
/*!
* Not supported and therefore ignored.
*/
virtual void setPictures( const PictureMap& l );
/*!
* Implements the unified property interface -- export function.
* Since the DIIN tag is very limited, the exported map is as well.
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* Because of the limitations of the DIIN file tag, any tags besides
* TITLE and ARTIST, will be
* returned. Additionally, if the map contains tags with multiple values,
* all but the first will be contained in the returned map of unsupported
* properties.
*/
PropertyMap setProperties(const PropertyMap &);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
};
}
}
}
#endif

View File

@@ -1,844 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <tsmartptr.h>
#include "tagunion.h"
#include "dsdifffile.h"
using namespace TagLib;
struct Chunk64
{
ByteVector name;
unsigned long long offset;
unsigned long long size;
char padding;
};
namespace
{
enum {
ID3v2Index = 0,
DIINIndex = 1
};
enum {
PROPChunk = 0,
DIINChunk = 1
};
}
class DSDIFF::File::FilePrivate
{
public:
FilePrivate() :
endianness(BigEndian),
size(0),
isID3InPropChunk(false),
duplicateID3V2chunkIndex(-1),
id3v2TagChunkID("ID3 "),
hasID3v2(false),
hasDiin(false)
{
childChunkIndex[ID3v2Index] = -1;
childChunkIndex[DIINIndex] = -1;
}
Endianness endianness;
ByteVector type;
unsigned long long size;
ByteVector format;
std::vector<Chunk64> chunks;
std::vector<Chunk64> childChunks[2];
int childChunkIndex[2];
bool isID3InPropChunk; // Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
int duplicateID3V2chunkIndex; // 2 ID3 chunks are present. This is then the index of the one in
// PROP chunk that will be removed upon next save to remove duplicates.
SCOPED_PTR<AudioProperties> properties;
DoubleTagUnion tag;
ByteVector id3v2TagChunkID;
bool hasID3v2;
bool hasDiin;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool DSDIFF::File::isSupported(IOStream *stream)
{
// A DSDIFF file has to start with "FRM8????????DSD ".
const ByteVector id = Utils::readHeader(stream, 16, false);
return (id.startsWith("FRM8") && id.containsAt("DSD ", 12));
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::File::File(FileName file, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) : TagLib::File(file)
{
d = new FilePrivate;
d->endianness = BigEndian;
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) : TagLib::File(stream)
{
d = new FilePrivate;
d->endianness = BigEndian;
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::~File()
{
delete d;
}
TagLib::Tag *DSDIFF::File::tag() const
{
return &d->tag;
}
ID3v2::Tag *DSDIFF::File::ID3v2Tag() const
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
}
bool DSDIFF::File::hasID3v2Tag() const
{
return d->hasID3v2;
}
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag() const
{
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
}
bool DSDIFF::File::hasDIINTag() const
{
return d->hasDiin;
}
PropertyMap DSDIFF::File::properties() const
{
if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
return PropertyMap();
}
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported)
{
if(d->hasID3v2)
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(unsupported);
if(d->hasDiin)
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->removeUnsupportedProperties(unsupported);
}
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
}
DSDIFF::AudioProperties *DSDIFF::File::audioProperties() const
{
return d->properties.get();
}
bool DSDIFF::File::save()
{
if(readOnly()) {
debug("DSDIFF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("DSDIFF::File::save() -- Trying to save invalid file.");
return false;
}
// First: save ID3V2 chunk
ID3v2::Tag *id3v2Tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
if(d->isID3InPropChunk) {
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(), PROPChunk);
d->hasID3v2 = true;
}
else {
// Empty tag: remove it
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
d->hasID3v2 = false;
}
}
else {
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render());
d->hasID3v2 = true;
}
else {
// Empty tag: remove it
setRootChunkData(d->id3v2TagChunkID, ByteVector());
d->hasID3v2 = false;
}
}
// Second: save the DIIN chunk
if(d->hasDiin) {
DSDIFF::DIIN::Tag *diinTag = d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
if(!diinTag->title().isEmpty()) {
ByteVector diinTitle;
if(d->endianness == BigEndian)
diinTitle.append(ByteVector::fromUInt32BE(diinTag->title().size()));
else
diinTitle.append(ByteVector::fromUInt32LE(diinTag->title().size()));
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
setChildChunkData("DITI", diinTitle, DIINChunk);
}
else
setChildChunkData("DITI", ByteVector(), DIINChunk);
if(!diinTag->artist().isEmpty()) {
ByteVector diinArtist;
if(d->endianness == BigEndian)
diinArtist.append(ByteVector::fromUInt32BE(diinTag->artist().size()));
else
diinArtist.append(ByteVector::fromUInt32LE(diinTag->artist().size()));
diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString()));
setChildChunkData("DIAR", diinArtist, DIINChunk);
}
else
setChildChunkData("DIAR", ByteVector(), DIINChunk);
}
// Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any
if(d->duplicateID3V2chunkIndex>=0) {
setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk);
d->duplicateID3V2chunkIndex = -1;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
{
if(data.isEmpty()) {
// Null data: remove chunk
// Update global size
unsigned long long removedChunkTotalSize = d->chunks[i].size + d->chunks[i].padding + 12;
d->size -= removedChunkTotalSize;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
removeBlock(d->chunks[i].offset - 12, removedChunkTotalSize);
// Update the internal offsets
for(unsigned long r = i + 1; r < d->chunks.size(); r++)
d->chunks[r].offset = d->chunks[r - 1].offset + 12
+ d->chunks[r - 1].size + d->chunks[r - 1].padding;
d->chunks.erase(d->chunks.begin() + i);
}
else {
// Non null data: update chunk
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// Now update the specific chunk
writeChunk(d->chunks[i].name,
data,
d->chunks[i].offset - 12,
d->chunks[i].size + d->chunks[i].padding + 12);
d->chunks[i].size = data.size();
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
// Finally update the internal offsets
updateRootChunksStructure(i + 1);
}
}
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data)
{
if(d->chunks.size() == 0) {
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
return;
}
for(unsigned int i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == name) {
setRootChunkData(i, data);
return;
}
}
// Couldn't find an existing chunk, so let's create a new one.
unsigned int i = d->chunks.size() - 1;
unsigned long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding;
// First we update the global size
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// Now add the chunk to the file
writeChunk(name,
data,
offset,
std::max<unsigned long long>(0, length() - offset),
(offset & 1) ? 1 : 0);
Chunk64 chunk;
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 12;
chunk.padding = (data.size() & 0x01) ? 1 : 0;
d->chunks.push_back(chunk);
}
void DSDIFF::File::setChildChunkData(unsigned int i,
const ByteVector &data,
unsigned int childChunkNum)
{
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
if(data.isEmpty()) {
// Null data: remove chunk
// Update global size
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
d->size -= removedChunkTotalSize;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// Update child chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
else
insert(ByteVector::fromUInt64LE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Remove the chunk
removeBlock(childChunks[i].offset - 12, removedChunkTotalSize);
// Update the internal offsets
// For child chunks
if((i + 1) < childChunks.size()) {
childChunks[i + 1].offset = childChunks[i].offset;
i++;
for(i++; i < childChunks.size(); i++)
childChunks[i].offset = childChunks[i - 1].offset + 12
+ childChunks[i - 1].size + childChunks[i - 1].padding;
}
// And for root chunks
for(i = d->childChunkIndex[childChunkNum] + 1; i < d->chunks.size(); i++)
d->chunks[i].offset = d->chunks[i - 1].offset + 12
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
childChunks.erase(childChunks.begin() + i);
}
else {
// Non null data: update chunk
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// And the PROP chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1)
- (childChunks[i].size + childChunks[i].padding);
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
else
insert(ByteVector::fromUInt64LE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Now update the specific chunk
writeChunk(childChunks[i].name,
data,
childChunks[i].offset - 12,
childChunks[i].size + childChunks[i].padding + 12);
childChunks[i].size = data.size();
childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
// Now update the internal offsets
// For child Chunks
for(i++; i < childChunks.size(); i++)
childChunks[i].offset = childChunks[i - 1].offset + 12
+ childChunks[i - 1].size + childChunks[i - 1].padding;
// And for root chunks
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
}
}
void DSDIFF::File::setChildChunkData(const ByteVector &name,
const ByteVector &data,
unsigned int childChunkNum)
{
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
if(childChunks.size() == 0) {
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
return;
}
for(unsigned int i = 0; i < childChunks.size(); i++) {
if(childChunks[i].name == name) {
setChildChunkData(i, data, childChunkNum);
return;
}
}
// Do not attempt to remove a non existing chunk
if(data.isEmpty())
return;
// Couldn't find an existing chunk, so let's create a new one.
unsigned int i = childChunks.size() - 1;
unsigned long offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding;
// First we update the global size
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// And the child chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1)
+ ((data.size() + 1) & ~1) + 12;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
else
insert(ByteVector::fromUInt64LE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Now add the chunk to the file
unsigned long long nextRootChunkIdx = length();
if((d->childChunkIndex[childChunkNum] + 1) < static_cast<int>(d->chunks.size()))
nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12;
writeChunk(name, data, offset,
std::max<unsigned long long>(0, nextRootChunkIdx - offset),
(offset & 1) ? 1 : 0);
// For root chunks
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
Chunk64 chunk;
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 12;
chunk.padding = (data.size() & 0x01) ? 1 : 0;
childChunks.push_back(chunk);
}
static bool isValidChunkID(const ByteVector &name)
{
if(name.size() != 4)
return false;
for(int i = 0; i < 4; i++) {
if(name[i] < 32 || name[i] > 127)
return false;
}
return true;
}
void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
{
for(unsigned int i = startingChunk; i < d->chunks.size(); i++)
d->chunks[i].offset = d->chunks[i - 1].offset + 12
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
// Update childchunks structure as well
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[PROPChunk];
if(childChunksToUpdate.size() > 0) {
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12;
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
}
}
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[DIINChunk];
if(childChunksToUpdate.size() > 0) {
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12;
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
}
}
}
void DSDIFF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesStyle)
{
bool bigEndian = (d->endianness == BigEndian);
d->type = readBlock(4);
d->size = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
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 = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
if(!isValidChunkID(chunkName)) {
debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<unsigned long long>(tell()) + chunkSize > static_cast<unsigned long long>(length())) {
debug("DSDIFF::File::read() -- Chunk '" + chunkName
+ "' has invalid size (larger than the file size)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = chunkName;
chunk.size = chunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// Check padding
chunk.padding = 0;
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 0))
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->chunks.push_back(chunk);
}
unsigned long long lengthDSDSamplesTimeChannels = 0; // For DSD uncompressed
unsigned long long audioDataSizeinBytes = 0; // For computing bitrate
unsigned long dstNumFrames = 0; // For DST compressed frames
unsigned short dstFrameRate = 0; // For DST compressed frames
for(unsigned int i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == "DSD ") {
lengthDSDSamplesTimeChannels = d->chunks[i].size * 8;
audioDataSizeinBytes = d->chunks[i].size;
}
else if(d->chunks[i].name == "DST ") {
// Now decode the chunks inside the DST chunk to read the DST Frame Information one
long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size;
seek(d->chunks[i].offset);
audioDataSizeinBytes = d->chunks[i].size;
while(tell() + 12 <= dstChunkEnd) {
ByteVector dstChunkName = readBlock(4);
long long dstChunkSize = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
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 = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
dstFrameRate = bigEndian ? readBlock(2).toUInt16BE(0) : readBlock(2).toUInt16LE(0);
break; // Found the wanted one, no need to look at the others
}
seek(dstChunkSize, Current);
// Check padding
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 0))
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
}
}
}
else if(d->chunks[i].name == "PROP") {
d->childChunkIndex[PROPChunk] = i;
// Now decodes the chunks inside the PROP chunk
long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size;
seek(d->chunks[i].offset + 4); // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
while(tell() + 12 <= propChunkEnd) {
ByteVector propChunkName = readBlock(4);
long long propChunkSize = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
if(!isValidChunkID(propChunkName)) {
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + propChunkSize > propChunkEnd) {
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName
+ "' has invalid size (larger than the PROP chunk)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = propChunkName;
chunk.size = propChunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// Check padding
chunk.padding = 0;
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 0))
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->childChunks[PROPChunk].push_back(chunk);
}
}
else if(d->chunks[i].name == "DIIN") {
d->childChunkIndex[DIINChunk] = i;
d->hasDiin = true;
// Now decode the chunks inside the DIIN chunk
long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size;
seek(d->chunks[i].offset);
while(tell() + 12 <= diinChunkEnd) {
ByteVector diinChunkName = readBlock(4);
long long diinChunkSize = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
if(!isValidChunkID(diinChunkName)) {
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + diinChunkSize > diinChunkEnd) {
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName
+ "' has invalid size (larger than the DIIN chunk)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = diinChunkName;
chunk.size = diinChunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// Check padding
chunk.padding = 0;
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 0))
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->childChunks[DIINChunk].push_back(chunk);
}
}
else if(d->chunks[i].name == "ID3 " || d->chunks[i].name == "id3 ") {
d->id3v2TagChunkID = d->chunks[i].name;
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->chunks[i].offset));
d->isID3InPropChunk = false;
d->hasID3v2 = true;
}
}
if(!isValid())
return;
if(d->childChunkIndex[PROPChunk] < 0) {
debug("DSDIFF::File::read() -- no PROP chunk found");
setValid(false);
return;
}
// Read properties
unsigned int sampleRate=0;
unsigned short channels=0;
for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) {
if(d->childChunks[PROPChunk][i].name == "ID3 " || d->childChunks[PROPChunk][i].name == "id3 ") {
if(d->hasID3v2) {
d->duplicateID3V2chunkIndex = i;
continue; // ID3V2 tag has already been found at root level
}
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset));
d->isID3InPropChunk = true;
d->hasID3v2 = true;
}
else if(d->childChunks[PROPChunk][i].name == "FS ") {
// Sample rate
seek(d->childChunks[PROPChunk][i].offset);
sampleRate = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
}
else if(d->childChunks[PROPChunk][i].name == "CHNL") {
// Channels
seek(d->childChunks[PROPChunk][i].offset);
channels = bigEndian ? readBlock(2).toInt16BE(0) : readBlock(2).toInt16LE(0);
}
}
// 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 = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
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 = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
if(artistStrLength <= d->childChunks[DIINChunk][i].size) {
ByteVector artistStr = readBlock(artistStrLength);
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setArtist(artistStr);
}
}
}
}
if(readProperties) {
if(lengthDSDSamplesTimeChannels == 0) {
// DST compressed signal : need to compute length of DSD uncompressed frames
if(dstFrameRate > 0)
lengthDSDSamplesTimeChannels = (unsigned long long)dstNumFrames
* (unsigned long long)sampleRate / (unsigned long long)dstFrameRate;
else
lengthDSDSamplesTimeChannels = 0;
}
else {
// In DSD uncompressed files, the read number of samples is the total for each channel
if(channels > 0)
lengthDSDSamplesTimeChannels /= channels;
}
int bitrate = 0;
if(lengthDSDSamplesTimeChannels > 0)
bitrate = (audioDataSizeinBytes*8*sampleRate) / lengthDSDSamplesTimeChannels / 1000;
d->properties.reset(new AudioProperties(sampleRate,
channels,
lengthDSDSamplesTimeChannels,
bitrate,
propertiesStyle));
}
if(!ID3v2Tag()) {
d->tag.access<ID3v2::Tag>(ID3v2Index, true);
d->isID3InPropChunk = false; // By default, ID3 chunk is at root level
d->hasID3v2 = false;
}
}
void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
unsigned long long offset, unsigned long replace,
unsigned int leadingPadding)
{
ByteVector combined;
if(leadingPadding)
combined.append(ByteVector(leadingPadding, '\x00'));
combined.append(name);
if(d->endianness == BigEndian)
combined.append(ByteVector::fromUInt64BE(data.size()));
else
combined.append(ByteVector::fromUInt64LE(data.size()));
combined.append(data);
if((data.size() & 0x01) != 0)
combined.append('\x00');
insert(combined, offset, replace);
}

View File

@@ -1,260 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSDIFFFILE_H
#define TAGLIB_DSDIFFFILE_H
#include "rifffile.h"
#include "id3v2tag.h"
#include "dsdiffproperties.h"
#include "dsdiffdiintag.h"
namespace TagLib {
//! An implementation of DSDIFF metadata
/*!
* This is implementation of DSDIFF metadata.
*
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
* chunk as well as properties from the file.
* Description of the DSDIFF format is available
* at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
* DSDIFF standard does not explictly specify the ID3V2 chunk
* It can be found at the root level, but also sometimes inside the PROP chunk
* In addition, title and artist info are stored as part of the standard
*/
namespace DSDIFF {
//! An implementation of TagLib::File with DSDIFF specific methods
/*!
* This implements and provides an interface for DSDIFF files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to DSDIFF files.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* Constructs an DSDIFF file from \a file. If \a readProperties is true
* the file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Constructs an DSDIFF file from \a stream. If \a readProperties is true
* the file's audio properties will also be read.
*
* \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,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN
* tags. The ID3v2 tag is given priority in reading the information -- if
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
* the information from the ID3v2 tag will be returned.
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use the tag-type specific calls.
*
* \note As this tag is not implemented as an ID3v2 tag or a DIIN tag,
* but a union of the two this pointer may not be cast to the specific
* tag types.
*
* \see ID3v2Tag()
* \see DIINTag()
*/
virtual Tag *tag() const;
/*!
* Returns the ID3V2 Tag for this file.
*
* \note This always returns a valid pointer regardless of whether or not
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the
* file on disk actually has an ID3v2 tag.
*
* \see hasID3v2Tag()
*/
virtual ID3v2::Tag *ID3v2Tag() const;
/*!
* Returns the DSDIFF DIIN Tag for this file
*
*/
DSDIFF::DIIN::Tag *DIINTag() const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the DSDIFF::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const;
/*!
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
* will duplicate its content into the other tag. This returns true
* if saving was successful.
*
* If neither exists or if both tags are empty, this will strip the tags
* from the file.
*
* This is the same as calling save(AllTags);
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use paramaterized save call below.
*
* \see save(int tags)
*/
virtual bool save();
/*!
* Save the file. This will attempt to save all of the tag types that are
* specified by OR-ing together TagTypes values. The save() method above
* uses AllTags. This returns true if saving was successful.
*
* This strips all tags not included in the mask, but does not modify them
* in memory, so later calls to save() which make use of these tags will
* remain valid. This also strips empty tags.
*/
bool save(int tags);
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
*
* \see ID3v2Tag()
*/
bool hasID3v2Tag() const;
/*!
* Returns whether or not the file on disk actually has the DSDIFF
* Title & Artist tag.
*
* \see DIINTag()
*/
bool hasDIINTag() const;
/*!
* Returns whether or not the given \a stream can be opened as a DSDIFF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
protected:
enum Endianness { BigEndian, LittleEndian };
File(FileName file, Endianness endianness);
File(IOStream *stream, Endianness endianness);
private:
File(const File &);
File &operator=(const File &);
/*!
* Sets the data for the the specified chunk at root level to \a data.
*
* \warning This will update the file immediately.
*/
void setRootChunkData(unsigned int i, const ByteVector &data);
/*!
* Sets the data for the root-level chunk \a name to \a data.
* If a root-level chunk with the given name already exists
* it will be overwritten, otherwise it will be
* created after the existing chunks.
*
* \warning This will update the file immediately.
*/
void setRootChunkData(const ByteVector &name, const ByteVector &data);
/*!
* Sets the data for the the specified child chunk to \a data.
*
* If data is null, then remove the chunk
*
* \warning This will update the file immediately.
*/
void setChildChunkData(unsigned int i, const ByteVector &data,
unsigned int childChunkNum);
/*!
* Sets the data for the child chunk \a name to \a data. If a chunk with
* the given name already exists it will be overwritten, otherwise it will
* be created after the existing chunks inside child chunk.
*
* If data is null, then remove the chunks with \a name name
*
* \warning This will update the file immediately.
*/
void setChildChunkData(const ByteVector &name, const ByteVector &data,
unsigned int childChunkNum);
void updateRootChunksStructure(unsigned int startingChunk);
void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle);
void writeChunk(const ByteVector &name, const ByteVector &data,
unsigned long long offset, unsigned long replace = 0,
unsigned int leadingPadding = 0);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -1,117 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstring.h>
#include <tdebug.h>
#include "dsdiffproperties.h"
using namespace TagLib;
class DSDIFF::AudioProperties::AudioPropertiesPrivate
{
public:
AudioPropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
sampleWidth(0),
sampleCount(0)
{
}
int length;
int bitrate;
int sampleRate;
int channels;
int sampleWidth;
unsigned long long sampleCount;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::AudioProperties::AudioProperties(const unsigned int sampleRate,
const unsigned short channels,
const unsigned long long samplesCount,
const int bitrate,
ReadStyle style) : TagLib::AudioProperties(), d(new AudioPropertiesPrivate)
{
d->channels = channels;
d->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::AudioProperties::~AudioProperties()
{
delete d;
}
int DSDIFF::AudioProperties::length() const
{
return lengthInSeconds();
}
int DSDIFF::AudioProperties::lengthInSeconds() const
{
return d->length / 1000;
}
int DSDIFF::AudioProperties::lengthInMilliseconds() const
{
return d->length;
}
int DSDIFF::AudioProperties::bitrate() const
{
return d->bitrate;
}
int DSDIFF::AudioProperties::sampleRate() const
{
return d->sampleRate;
}
int DSDIFF::AudioProperties::channels() const
{
return d->channels;
}
int DSDIFF::AudioProperties::bitsPerSample() const
{
return d->sampleWidth;
}
long long DSDIFF::AudioProperties::sampleCount() const
{
return d->sampleCount;
}

View File

@@ -1,83 +0,0 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSDIFFPROPERTIES_H
#define TAGLIB_DSDIFFPROPERTIES_H
#include "audioproperties.h"
namespace TagLib {
namespace DSDIFF {
class File;
//! An implementation of audio property reading for DSDIFF
/*!
* This reads the data from an DSDIFF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
{
public:
/*!
* Create an instance of DSDIFF::AudioProperties with the data read from the
* ByteVector \a data.
*/
AudioProperties(const unsigned int sampleRate, const unsigned short channels,
const unsigned long long samplesCount, const int bitrate,
ReadStyle style);
/*!
* Destroys this DSDIFF::AudioProperties instance.
*/
virtual ~AudioProperties();
// Reimplementations.
virtual int length() const;
virtual int lengthInSeconds() const;
virtual int lengthInMilliseconds() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
int bitsPerSample() const;
long long sampleCount() const;
private:
AudioProperties(const AudioProperties &);
AudioProperties &operator=(const AudioProperties &);
class AudioPropertiesPrivate;
AudioPropertiesPrivate *d;
};
}
}
#endif

View File

@@ -1,234 +0,0 @@
/***************************************************************************
copyright : (C) 2013 - 2018 by Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include <tsmartptr.h>
#include <tagutils.h>
#include "dsffile.h"
using namespace TagLib;
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
class DSF::File::FilePrivate
{
public:
FilePrivate() :
fileSize(0),
metadataOffset(0) {}
long long fileSize;
long long metadataOffset;
SCOPED_PTR<AudioProperties> properties;
SCOPED_PTR<ID3v2::Tag> tag;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool DSF::File::isSupported(IOStream *stream)
{
// A DSF file has to start with "DSD "
const ByteVector id = Utils::readHeader(stream, 4, false);
return id.startsWith("DSD ");
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSF::File::File(FileName file, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
TagLib::File(file),
d(new FilePrivate())
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSF::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSF::File::~File()
{
delete d;
}
ID3v2::Tag *DSF::File::tag() const
{
return d->tag.get();
}
DSF::AudioProperties *DSF::File::audioProperties() const
{
return d->properties.get();
}
PropertyMap DSF::File::properties() const
{
return d->tag->properties();
}
PropertyMap DSF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
bool DSF::File::save()
{
if(readOnly()) {
debug("DSF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("DSF::File::save() -- Trying to save invalid file.");
return false;
}
// Three things must be updated: the file size, the tag data, and the metadata offset
if(d->tag->isEmpty()) {
long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromUInt64LE(newFileSize), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset to 0 since there is no longer a tag
if(d->metadataOffset) {
insert(ByteVector::fromUInt64LE(0ULL), 20, 8);
d->metadataOffset = 0;
}
// Delete the old tag
truncate(newFileSize);
}
else {
ByteVector tagData = d->tag->render();
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
long long newFileSize = newMetadataOffset + tagData.size();
long long oldTagSize = d->fileSize - newMetadataOffset;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromUInt64LE(newFileSize), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset
if(d->metadataOffset != newMetadataOffset) {
insert(ByteVector::fromUInt64LE(newMetadataOffset), 20, 8);
d->metadataOffset = newMetadataOffset;
}
// Delete the old tag and write the new one
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesStyle)
{
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
// The file format is not chunked in the sense of a RIFF File, though
// DSD chunk
ByteVector chunkName = readBlock(4);
if(chunkName != "DSD ") {
debug("DSF::File::read() -- Not a DSF file.");
setValid(false);
return;
}
long long chunkSize = readBlock(8).toInt64LE(0);
// Integrity check
if(28 != chunkSize) {
debug("DSF::File::read() -- File is corrupted.");
setValid(false);
return;
}
d->fileSize = readBlock(8).toInt64LE(0);
// File is malformed or corrupted
if(d->fileSize != length()) {
debug("DSF::File::read() -- File is corrupted.");
setValid(false);
return;
}
d->metadataOffset = readBlock(8).toInt64LE(0);
// File is malformed or corrupted
if(d->metadataOffset > d->fileSize) {
debug("DSF::File::read() -- Invalid metadata offset.");
setValid(false);
return;
}
// Format chunk
chunkName = readBlock(4);
if(chunkName != "fmt ") {
debug("DSF::File::read() -- Missing 'fmt ' chunk.");
setValid(false);
return;
}
chunkSize = readBlock(8).toInt64LE(0);
d->properties.reset(
new AudioProperties(readBlock(static_cast<size_t>(chunkSize)), propertiesStyle));
// Skip the data chunk
// A metadata offset of 0 indicates the absence of an ID3v2 tag
if(0 == d->metadataOffset)
d->tag.reset(new ID3v2::Tag());
else
d->tag.reset(new ID3v2::Tag(this, d->metadataOffset));
}

View File

@@ -1,127 +0,0 @@
/***************************************************************************
copyright : (C) 2013 - 2018 by Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSFFILE_H
#define TAGLIB_DSFFILE_H
#include "tfile.h"
#include "id3v2tag.h"
#include "dsfproperties.h"
namespace TagLib {
//! An implementation of DSF metadata
/*!
* This is implementation of DSF metadata.
*
* This supports an ID3v2 tag as well as properties from the file.
*/
namespace DSF {
//! An implementation of TagLib::File with DSF specific methods
/*!
* This implements and provides an interface for DSF files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to DSF files.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* Contructs an DSF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Contructs an DSF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the Tag for this file.
*/
virtual ID3v2::Tag *tag() const;
/*!
* Returns the DSF::AudioProperties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
virtual PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
virtual PropertyMap setProperties(const PropertyMap &);
/*!
* Saves the file.
*/
virtual bool save();
/*!
* Returns whether or not the given \a stream can be opened as a DSF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -1,158 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstring.h>
#include <tdebug.h>
#include "dsfproperties.h"
using namespace TagLib;
class DSF::AudioProperties::PropertiesPrivate
{
public:
PropertiesPrivate() :
formatVersion(0),
formatID(0),
channelType(0),
channelNum(0),
samplingFrequency(0),
bitsPerSample(0),
sampleCount(0),
blockSizePerChannel(0),
bitrate(0),
length(0) {}
// Nomenclature is from DSF file format specification
unsigned int formatVersion;
unsigned int formatID;
unsigned int channelType;
unsigned int channelNum;
unsigned int samplingFrequency;
unsigned int bitsPerSample;
long long sampleCount;
unsigned int blockSizePerChannel;
// Computed
int bitrate;
int length;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSF::AudioProperties::AudioProperties(const ByteVector &data, ReadStyle style) :
d(new PropertiesPrivate())
{
read(data);
}
DSF::AudioProperties::~AudioProperties()
{
delete d;
}
int DSF::AudioProperties::length() const
{
return lengthInSeconds();
}
int DSF::AudioProperties::lengthInSeconds() const
{
return d->length / 1000;
}
int DSF::AudioProperties::lengthInMilliseconds() const
{
return d->length;
}
int DSF::AudioProperties::bitrate() const
{
return d->bitrate;
}
int DSF::AudioProperties::sampleRate() const
{
return d->samplingFrequency;
}
int DSF::AudioProperties::channels() const
{
return d->channelNum;
}
// DSF specific
int DSF::AudioProperties::formatVersion() const
{
return d->formatVersion;
}
int DSF::AudioProperties::formatID() const
{
return d->formatID;
}
int DSF::AudioProperties::channelType() const
{
return d->channelType;
}
int DSF::AudioProperties::bitsPerSample() const
{
return d->bitsPerSample;
}
long long DSF::AudioProperties::sampleCount() const
{
return d->sampleCount;
}
int DSF::AudioProperties::blockSizePerChannel() const
{
return d->blockSizePerChannel;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSF::AudioProperties::read(const ByteVector &data)
{
d->formatVersion = data.toUInt32LE(0);
d->formatID = data.toUInt32LE(4);
d->channelType = data.toUInt32LE(8);
d->channelNum = data.toUInt32LE(12);
d->samplingFrequency = data.toUInt32LE(16);
d->bitsPerSample = data.toUInt32LE(20);
d->sampleCount = data.toInt64LE(24);
d->blockSizePerChannel = data.toUInt32LE(32);
d->bitrate = static_cast<int>(d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5);
if(d->samplingFrequency > 0)
d->length = static_cast<int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5);
}

View File

@@ -1,91 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSFPROPERTIES_H
#define TAGLIB_DSFPROPERTIES_H
#include "audioproperties.h"
namespace TagLib {
namespace DSF {
class File;
//! An implementation of audio property reading for DSF
/*!
* This reads the data from a DSF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
{
public:
/*!
* Create an instance of DSF::AudioProperties with the data read from the
* ByteVector \a data.
*/
AudioProperties(const ByteVector &data, ReadStyle style);
/*!
* Destroys this DSF::AudioProperties instance.
*/
virtual ~AudioProperties();
// Reimplementations.
virtual int length() const;
virtual int lengthInSeconds() const;
virtual int lengthInMilliseconds() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
int formatVersion() const;
int formatID() const;
/*!
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels,
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels
*/
int channelType() const;
int bitsPerSample() const;
long long sampleCount() const;
int blockSizePerChannel() const;
private:
AudioProperties(const AudioProperties &);
AudioProperties &operator=(const AudioProperties &);
void read(const ByteVector &data);
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
#endif

View File

@@ -1,58 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_EBML_CONSTANTS
#define TAGLIB_EBML_CONSTANTS
namespace TagLib {
namespace EBML {
//! Shorter representation of the type.
typedef unsigned long long int ulli;
//! The id of an EBML Void element that is just a placeholder.
const ulli Void = 0xecL;
//! The id of an EBML CRC32 element that contains a crc32 value.
const ulli CRC32 = 0xc3L;
//! A namespace containing the ids of the EBML header's elements.
namespace Header {
const ulli EBML = 0x1a45dfa3L;
const ulli EBMLVersion = 0x4286L;
const ulli EBMLReadVersion = 0x42f7L;
const ulli EBMLMaxIDWidth = 0x42f2L;
const ulli EBMLMaxSizeWidth = 0x42f3L;
const ulli DocType = 0x4282L;
const ulli DocTypeVersion = 0x4287L;
const ulli DocTypeReadVersion = 0x4285L;
}
}
}
#endif

View File

@@ -1,489 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "tdebug.h"
#include "ebmlelement.h"
using namespace TagLib;
class EBML::Element::ElementPrivate
{
public:
// The id of this element.
ulli id;
// The position of the element, where the header begins.
long long position;
// The size of the element as read from the header. Note: Actually an ulli but
// due to the variable integer size limited, thus long long is ok.
long long size;
// The position of the element's data.
long long data;
// The element's children.
List<Element *> children;
// True: Treated this element as container and read children.
bool populated;
// The parent element. If NULL (0) this is the document root.
Element *parent;
// The file used to read and write.
File *document;
// Destructor: Clean up all children.
~ElementPrivate()
{
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
delete *i;
}
}
// Reads a variable length integer from the file at the given position
// and saves its value to result. If cutOne is true the first one of the
// binary representation of the result is removed (required for size). If
// cutOne is false the one will remain in the result (required for id).
// This method returns the position directly after the read integer.
long long readVInt(long long position, ulli *result, bool cutOne = true)
{
document->seek(position);
// Determine the length of the integer
char firstByte = document->readBlock(1)[0];
size_t byteSize = 1;
for(size_t i = 0; i < 8 && ((firstByte << i) & (1 << 7)) == 0; ++i)
++byteSize;
// Load the integer
document->seek(position);
ByteVector vint = document->readBlock(byteSize);
// Cut the one if requested
if(cutOne)
vint[0] = (vint[0] & (~(1 << (8 - byteSize))));
// Store the result and return the current position
if(result)
*result = static_cast<ulli>(vint.toInt64BE(0));
return position + byteSize;
}
// Returns a BytVector containing the given number in the variable integer
// format. Truncates numbers > 2^56 (^ means potency in this case).
// If addOne is true, the ByteVector will remain the One to determine the
// integer's length.
// If shortest is true, the ByteVector will be as short as possible (required
// for the id)
ByteVector createVInt(ulli number, bool addOne = true, bool shortest = true)
{
ByteVector vint = ByteVector::fromUInt64BE(number);
// Do we actually need to calculate the length of the variable length
// integer? If not, then prepend the 0b0000 0001 if necessary and return the
// vint.
if(!shortest) {
if(addOne)
vint[0] = 1;
return vint;
}
// Calculate the minimal length of the variable length integer
size_t byteSize = vint.size();
for(size_t i = 0; byteSize > 0 && vint[i] == 0; ++i)
--byteSize;
if(!addOne)
return ByteVector(vint.data() + vint.size() - byteSize, byteSize);
ulli firstByte = (1ULL << (vint.size() - byteSize));
// The most significant byte loses #bytSize bits for storing information.
// Therefore, we might need to increase byteSize.
if(number >= (firstByte << (8 * (byteSize - 1))) && byteSize < vint.size())
++byteSize;
// Add the one at the correct position
size_t firstBytePosition = vint.size() - byteSize;
vint[firstBytePosition] |= (1 << firstBytePosition);
return ByteVector(vint.data() + firstBytePosition, byteSize);
}
// Returns a void element within this element which is at least "least" in
// size. Uses best fit method. Returns a null pointer if no suitable element
// was found.
Element *searchVoid(long long least = 0L)
{
Element *currentBest = 0;
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
if((*i)->d->id == Void &&
// We need room for the header if we don't remove the element.
((((*i)->d->size + (*i)->d->data - (*i)->d->position) == least || ((*i)->d->size >= least)) &&
// best fit
(!currentBest || (*i)->d->size < currentBest->d->size))
) {
currentBest = *i;
}
}
return currentBest;
}
// Replaces this element by a Void element. Returns true on success and false
// on error.
bool makeVoid()
{
ulli realSize = size + data - position;
ByteVector header(createVInt(Void, false));
ulli leftSize = realSize - (header.size() + sizeof(ulli));
// Does not make sense to create a Void element
if (leftSize > realSize)
return false;
header.append(createVInt(leftSize, true, false));
// Write to file
document->seek(position);
document->writeBlock(header);
// Update data
data = position + header.size();
size = leftSize;
return true;
// XXX: We actually should merge Voids, if possible.
}
// Reading constructor: Reads all unknown information from the file.
ElementPrivate(File *p_document, Element *p_parent = 0, long long p_position = 0) :
id(0),
position(p_position),
data(0),
populated(false),
parent(p_parent),
document(p_document)
{
if(parent) {
ulli ssize;
data = readVInt(readVInt(position, &id, false), &ssize);
size = static_cast<long long>(ssize);
}
else {
document->seek(0, File::End);
size = document->tell();
}
}
// Writing constructor: Takes given information, calculates missing information
// and writes everything to the file.
// Tries to use void elements if available in the parent.
ElementPrivate(ulli p_id, File *p_document, Element *p_parent,
long long p_position, long long p_size) :
id(p_id),
position(p_position),
size(p_size),
populated(true), // It is a new element so we know, there are no children.
parent(p_parent),
document(p_document)
{
// header
ByteVector content(createVInt(id, false).append(createVInt(size, true, false)));
data = position + content.size();
// space for children
content.resize(static_cast<size_t>(data - position + size));
Element *freeSpace;
if (!(freeSpace = searchVoid(content.size()))) {
// We have to make room
document->insert(content, position);
// Update parents
for(Element *current = parent; current->d->parent; current = current->d->parent) {
current->d->size += content.size();
// Create new header and write it.
ByteVector parentHeader(createVInt(current->d->id, false).append(createVInt(current->d->size, true, false)));
size_t oldHeaderSize = static_cast<size_t>(current->d->data - current->d->position);
if(oldHeaderSize < parentHeader.size()) {
ByteVector secondHeader(createVInt(current->d->id, false).append(createVInt(current->d->size)));
if(oldHeaderSize == secondHeader.size()) {
// Write the header where the old one was.
document->seek(current->d->position);
document->writeBlock(secondHeader);
continue; // Very important here!
}
}
// Insert the new header
document->insert(parentHeader, current->d->position, oldHeaderSize);
current->d->data = current->d->position + parentHeader.size();
}
}
else {
document->seek(freeSpace->d->position);
if((freeSpace->d->size + freeSpace->d->data - freeSpace->d->position)
== static_cast<long long>(content.size())) {
// Write to file
document->writeBlock(content);
// Update parent
for(List<Element *>::Iterator i = parent->d->children.begin();
i != parent->d->children.end(); ++i) {
if(freeSpace == *i)
parent->d->children.erase(i);
}
delete freeSpace;
}
else {
ulli newSize = freeSpace->d->size - content.size();
ByteVector newVoid(createVInt(Void, false).append(createVInt(newSize, true, false)));
// Check if the original size of the size field was really 8 byte
if (static_cast<long long>(newVoid.size()) != (freeSpace->d->data - freeSpace->d->position))
newVoid = createVInt(Void, false).append(createVInt(newSize));
// Update freeSpace
freeSpace->d->size = newSize;
freeSpace->d->data = freeSpace->d->position + newVoid.size();
// Write to file
document->writeBlock(
newVoid.resize(static_cast<size_t>(newVoid.size() + newSize)).append(content));
}
}
}
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::Element::~Element()
{
delete d;
}
EBML::Element::Element(EBML::File *document)
: d(new EBML::Element::ElementPrivate(document))
{
}
EBML::Element *EBML::Element::getChild(EBML::ulli id)
{
populate();
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
++i) {
if ((*i)->d->id == id)
return *i;
}
return 0;
}
List<EBML::Element *> EBML::Element::getChildren(EBML::ulli id)
{
populate();
List<Element *> result;
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
++i) {
if ((*i)->d->id == id)
result.append(*i);
}
return result;
}
List<EBML::Element *> EBML::Element::getChildren()
{
populate();
return d->children;
}
EBML::Element *EBML::Element::getParent()
{
return d->parent;
}
ByteVector EBML::Element::getAsBinary()
{
d->document->seek(d->data);
return d->document->readBlock(static_cast<size_t>(d->size));
}
String EBML::Element::getAsString()
{
return String(getAsBinary(), String::UTF8);
}
long long EBML::Element::getAsInt()
{
// The debug note about returning 0 because of empty data is irrelevant. The
// behavior is as expected.
return getAsBinary().toInt64BE(0);
}
EBML::ulli EBML::Element::getAsUnsigned()
{
// The debug note about returning 0 because of empty data is irrelevant. The
// behavior is as expected.
return static_cast<ulli>(getAsBinary().toInt64BE(0));
}
long double EBML::Element::getAsFloat()
{
const ByteVector bin = getAsBinary();
switch (bin.size())
{
case 4:
return bin.toFloat32BE(0);
case 8:
return bin.toFloat64BE(0);
case 10:
return bin.toFloat80BE(0);
default:
debug("EBML::Element::getAsFloat() - Invalid data size. Returning 0.");
return 0.0;
}
}
EBML::Element *EBML::Element::addElement(EBML::ulli id)
{
Element *elem = new Element(
new ElementPrivate(id, d->document, this, d->data + d->size, 0)
);
d->children.append(elem);
return elem;
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, const ByteVector &binary)
{
Element *elem = new Element(
new ElementPrivate(id, d->document, this, d->data + d->size, binary.size())
);
d->document->seek(elem->d->data);
d->document->writeBlock(binary);
d->children.append(elem);
return elem;
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, const String &string)
{
return addElement(id, string.data(String::UTF8));
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, signed long long number)
{
return addElement(id, ByteVector::fromUInt64BE(number));
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, EBML::ulli number)
{
return addElement(id, ByteVector::fromUInt64BE(number));
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, long double number)
{
// Probably, we will never need this method.
return 0;
}
bool EBML::Element::removeChildren(EBML::ulli id, bool useVoid)
{
bool result = false;
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
if((*i)->d->id == id) {
removeChild(*i, useVoid);
result = true;
}
return result;
}
bool EBML::Element::removeChildren(bool useVoid)
{
// Maybe a better implementation, because we probably create a lot of voids
// in a row where a huge Void would be more appropriate.
if (d->children.isEmpty())
return false;
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
removeChild(*i, useVoid);
return true;
}
bool EBML::Element::removeChild(Element *element, bool useVoid)
{
if (!d->children.contains(element))
return false;
if(!useVoid || !element->d->makeVoid()) {
d->document->removeBlock(element->d->position, static_cast<size_t>(element->d->size));
// Update parents
for(Element* current = this; current; current = current->d->parent)
current->d->size -= element->d->size;
// Update this element
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
if(element == *i)
d->children.erase(i);
delete element;
}
return true;
}
void EBML::Element::setAsBinary(const ByteVector &binary)
{
// Maybe: Search for void element after this one
d->document->insert(binary, d->data, static_cast<size_t>(d->size));
}
void EBML::Element::setAsString(const String &string)
{
setAsBinary(string.data(String::UTF8));
}
void EBML::Element::setAsInt(signed long long number)
{
setAsBinary(ByteVector::fromUInt64BE(number));
}
void EBML::Element::setAsUnsigned(EBML::ulli number)
{
setAsBinary(ByteVector::fromUInt64BE(number));
}
void EBML::Element::setAsFloat(long double)
{
// Probably, we will never need this method.
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void EBML::Element::populate()
{
if(!d->populated) {
d->populated = true;
long long end = d->data + d->size;
for(long long i = d->data; i < end;) {
Element *elem = new Element(
new ElementPrivate(d->document, this, i)
);
d->children.append(elem);
i = elem->d->data + elem->d->size;
}
}
}
EBML::Element::Element(EBML::Element::ElementPrivate *pe) : d(pe)
{}

View File

@@ -1,276 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_EBMLELEMENT_H
#define TAGLIB_EBMLELEMENT_H
#include "tlist.h"
#include "tbytevector.h"
#include "tstring.h"
#include "ebmlfile.h"
namespace TagLib {
namespace EBML {
/*!
* Represents an element of the EBML. The only instance of this child, that
* is directly used is the root element. Every other element is accessed
* via pointers to the elements within the root element.
*
* Just create one root instance per file to prevent race conditions.
*
* Changes of the document tree will be directly written back to the file.
* Invalid values (exceeding the maximal value defined in the RFC) will be
* truncated.
*
* This class should not be used by library users since the proper file
* class should handle the internals.
*
* NOTE: Currently does not adjust CRC32 values.
*/
class TAGLIB_EXPORT Element
{
public:
//! Destroys the instance of the element.
~Element();
/*!
* Creates an root element using document.
*/
explicit Element(File *document);
/*!
* Returns the first found child element with the given id. Returns a null
* pointer if the child does not exist.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *getChild(const ulli id);
/*!
* Returns a list of all child elements with the given id. Returns an
* empty list if no such element exists.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
List<Element *> getChildren(const ulli id);
/*!
* Returns a list of every child elements available. Returns an empty list
* if there are no children.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
List<Element *> getChildren();
/*!
* Returns the parent element or null if no such element exists.
*/
Element *getParent();
/*!
* Returns the raw content of the element.
*/
ByteVector getAsBinary();
/*!
* Returns the content of this element interpreted as a string.
*/
String getAsString();
/*!
* Returns the content of this element interpreted as an signed integer.
*
* Do not call this method if *this element is not an INT element (see
* corresponding DTD)
*/
long long getAsInt();
/*!
* Returns the content of this element interpreted as an unsigned integer.
*
* Do not call this method if *this element is not an UINT element (see
* corresponding DTD)
*/
ulli getAsUnsigned();
/*!
* Returns the content of this element interpreted as a floating point
* type.
*
* Do not call this method if *this element is not an FLOAT element (see
* corresponding DTD)
*
* NOTE: There are 10 byte floats defined, therefore we might need a long
* double to store the value.
*/
long double getAsFloat();
/*!
* Adds an empty element with given id to this element. Returns a pointer
* to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id);
/*!
* Adds a new element, containing the given binary, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id, const ByteVector &binary);
/*!
* Adds a new element, containing the given string, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id, const String &string);
/*!
* Adds a new element, containing the given integer, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id, signed long long number);
/*!
* Adds a new element, containing the given unsigned integer, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id, ulli number);
/*!
* Adds a new element, containing the given floating point value, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*
* This method is not implemented!
*/
Element *addElement(ulli id, long double number);
/*!
* Removes all children with the given id. Returns false if there was no
* such element.
* If useVoid is true, the element will be changed to a void element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*
* Every pointer to a removed element is invalidated.
*/
bool removeChildren(ulli id, bool useVoid = true);
/*!
* Removes all children. Returns false if this element had no children.
* If useVoid ist rue, the element will be changed to a void element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*
* Every pointer to a removed element is invalidated.
*/
bool removeChildren(bool useVoid = true);
/*!
* Removes the given element.
* If useVoid is true, the element will be changed to a void element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*
* The pointer to the given element is invalidated.
*/
bool removeChild(Element *element, bool useVoid = true);
/*!
* Writes the given binary to this element.
*/
void setAsBinary(const ByteVector &binary);
/*!
* Writes the given string to this element.
*/
void setAsString(const String &string);
/*!
* Writes the given integer to this element.
*/
void setAsInt(signed long long number);
/*!
* Writes the given unsigned integer to this element.
*/
void setAsUnsigned(ulli number);
/*!
* Writes the given floating point variable to this element.
*
* This method is not implemented!
*/
void setAsFloat(long double number);
private:
//! Non-copyable
Element(const Element &);
//! Non-copyable
Element &operator=(const File &);
//! Lazy parsing. This method will be triggered when trying to access
//! children.
void populate();
class ElementPrivate;
ElementPrivate *d;
//! Creates a new Element from an ElementPrivate. (The constructor takes
//! ownership of the pointer and will delete it when the element is
//! destroyed.
Element(ElementPrivate *pe);
};
}
}
#endif

View File

@@ -1,102 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "ebmlelement.h"
using namespace TagLib;
class EBML::File::FilePrivate
{
public:
explicit FilePrivate(File *document) : root(document)
{
}
// Performs a few basic checks and creates the FilePrivate if they were
// successful.
static FilePrivate *checkAndCreate(File* document)
{
document->seek(0);
ByteVector magical = document->readBlock(4);
if(static_cast<ulli>(magical.toUInt32BE(0)) != Header::EBML)
return 0;
FilePrivate *d = new FilePrivate(document);
Element *head = d->root.getChild(Header::EBML);
Element *p;
if(!head ||
!((p = head->getChild(Header::EBMLVersion)) && p->getAsUnsigned() == 1L) ||
!((p = head->getChild(Header::EBMLReadVersion)) && p->getAsUnsigned() == 1L) ||
// Actually 4 is the current maximum of the EBML spec, but we support up to 8
!((p = head->getChild(Header::EBMLMaxIDWidth)) && p->getAsUnsigned() <= 8) ||
!((p = head->getChild(Header::EBMLMaxSizeWidth)) && p->getAsUnsigned() <= 8)
) {
delete d;
return 0;
}
return d;
}
Element root;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::File::~File()
{
delete d;
}
EBML::Element *EBML::File::getDocumentRoot()
{
if(!d && isValid())
d = new FilePrivate(this);
return &d->root;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
EBML::File::File(FileName file) :
TagLib::File(file)
{
if(isOpen()) {
d = FilePrivate::checkAndCreate(this);
if(!d)
setValid(false);
}
}
EBML::File::File(IOStream *stream) :
TagLib::File(stream)
{
if(isOpen()) {
d = FilePrivate::checkAndCreate(this);
if(!d)
setValid(false);
}
}

View File

@@ -1,86 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_EBMLFILE_H
#define TAGLIB_EBMLFILE_H
#include "taglib_export.h"
#include "tfile.h"
#include "ebmlconstants.h"
namespace TagLib {
//! A namespace for the classes used by EBML-based metadata files
namespace EBML {
class Element;
/*!
* Represents an EBML file. It offers access to the root element which can
* be used to obtain the necessary information and to change the file
* according to changes.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
//! Destroys the instance of the file.
virtual ~File();
/*!
* Returns a pointer to the document root element of the EBML file.
*/
Element *getDocumentRoot();
protected:
/*!
* Constructs an instance of an EBML file from \a file.
*
* This constructor is protected since an object should be created
* through a specific subclass.
*/
explicit File(FileName file);
/*!
* Constructs an instance of an EBML file from an IOStream.
*
* This constructor is protected since an object should be created
* through a specific subclass.
*/
explicit File(IOStream *stream);
private:
//! Non-copyable
File(const File&);
File &operator=(const File &);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -1,135 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "ebmlmatroskaconstants.h"
#include "ebmlmatroskaaudio.h"
using namespace TagLib;
class EBML::Matroska::AudioProperties::AudioPropertiesPrivate
{
public:
// Constructor
AudioPropertiesPrivate() :
length(0),
bitrate(0),
channels(1),
samplerate(8000) {}
// The length of the file
int length;
// The bitrate
int bitrate;
// The amount of channels
int channels;
// The sample rate
int samplerate;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::Matroska::AudioProperties::AudioProperties(File *document) :
d(new AudioPropertiesPrivate())
{
read(document);
}
EBML::Matroska::AudioProperties::~AudioProperties()
{
delete d;
}
int EBML::Matroska::AudioProperties::length() const
{
return lengthInSeconds();
}
int EBML::Matroska::AudioProperties::lengthInSeconds() const
{
return d->length / 1000;
}
int EBML::Matroska::AudioProperties::lengthInMilliseconds() const
{
return d->length;
}
int EBML::Matroska::AudioProperties::bitrate() const
{
return d->bitrate;
}
int EBML::Matroska::AudioProperties::channels() const
{
return d->channels;
}
int EBML::Matroska::AudioProperties::sampleRate() const
{
return d->samplerate;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void EBML::Matroska::AudioProperties::read(File *document)
{
Element *elem = document->getDocumentRoot()->getChild(Constants::Segment);
Element *info = elem->getChild(Constants::SegmentInfo);
Element *value;
if(info && (value = info->getChild(Constants::Duration))) {
const double length = value->getAsFloat() / 1000000.0;
if((value = info->getChild(Constants::TimecodeScale)))
d->length = static_cast<int>(length * value->getAsUnsigned() + 0.5);
else
d->length = static_cast<int>(length * 1000000 + 0.5);
}
info = elem->getChild(Constants::Tracks);
if(!info || !(info = info->getChild(Constants::TrackEntry)) ||
!(info = info->getChild(Constants::Audio))) {
return;
}
// Dirty bitrate:
if(d->length > 0)
d->bitrate = static_cast<int>(document->length() * 8.0 / d->length + 0.5);
if((value = info->getChild(Constants::Channels)))
d->channels = static_cast<int>(value->getAsUnsigned());
if((value = info->getChild(Constants::SamplingFrequency)))
d->samplerate = static_cast<int>(value->getAsFloat());
}

View File

@@ -1,92 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_EBMLMATROSKAAUDIO_H
#define TAGLIB_EBMLMATROSKAAUDIO_H
#include "ebmlmatroskafile.h"
#include "audioproperties.h"
namespace TagLib {
namespace EBML {
namespace Matroska {
/*!
* This class represents the audio properties of a matroska file.
* Currently all information are read from the container format and
* could be inexact.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
{
public:
//! Destructor
virtual ~AudioProperties();
/*!
* Constructs an instance from a file.
*/
explicit AudioProperties(File *document);
/*!
* Returns the length of the file.
*/
virtual int length() const;
virtual int lengthInSeconds() const;
virtual int lengthInMilliseconds() const;
/*!
* Returns the bit rate of the file. Since the container format does not
* offer a proper value, it ist currently calculated by dividing the
* file size by the length.
*/
virtual int bitrate() const;
/*!
* Returns the amount of channels of the file.
*/
virtual int channels() const;
/*!
* Returns the sample rate of the file.
*/
virtual int sampleRate() const;
private:
void read(File *document);
class AudioPropertiesPrivate;
AudioPropertiesPrivate *d;
};
}
}
}
#endif

View File

@@ -1,140 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_EBMLMATROSKACONSTANTS_H
#define TAGLIB_EBMLMATROSKACONSTANTS_H
#include "ebmlconstants.h"
#include "tstring.h"
namespace TagLib {
namespace EBML {
namespace Matroska {
namespace Constants {
//! ID of an Matroska segment.
const ulli Segment = 0x18538067L;
//! ID of the tags element.
const ulli Tags = 0x1254c367L;
//! ID of the tag element.
const ulli Tag = 0x7373L;
//! ID of the targets element.
const ulli Targets = 0x63c0L;
//! ID of the target type value element.
const ulli TargetTypeValue = 0x68caL;
//! ID of the target type element.
const ulli TargetType = 0x63caL;
//! ID of a simple tag element.
const ulli SimpleTag = 0x67c8L;
//! ID of the tag name.
const ulli TagName = 0x45a3L;
//! ID of the tag content.
const ulli TagString = 0x4487L;
//! The DocType of a matroska file.
const String DocTypeMatroska = "matroska";
//! The DocType of a WebM file.
const String DocTypeWebM = "webm";
//! The TITLE entry
const String TITLE = "TITLE";
//! The ARTIST entry
const String ARTIST = "ARTIST";
//! The COMMENT entry
const String COMMENT = "COMMENT";
//! The GENRE entry
const String GENRE = "GENRE";
//! The DATE_RELEASE entry
const String DATE_RELEASE = "DATE_RELEASE";
//! The PART_NUMBER entry
const String PART_NUMBER = "PART_NUMBER";
//! The TargetTypeValue of the most common grouping level (e.g. album)
const ulli MostCommonGroupingValue = 50;
//! The TargetTypeValue of the most common parts of a group (e.g. track)
const ulli MostCommonPartValue = 30;
//! Name of the TargetType of an album.
const String ALBUM = "ALBUM";
//! Name of the TargetType of a track.
const String TRACK = "TRACK";
// For AudioProperties
//! ID of the Info block within the Segment.
const ulli SegmentInfo = 0x1549a966L;
//! ID of the duration element.
const ulli Duration = 0x4489L;
//! ID of TimecodeScale element.
const ulli TimecodeScale = 0x2ad7b1L;
//! ID of the Tracks container
const ulli Tracks = 0x1654ae6bL;
//! ID of a TrackEntry element.
const ulli TrackEntry = 0xaeL;
//! ID of the Audio container.
const ulli Audio = 0xe1L;
//! ID of the SamplingFrequency element.
const ulli SamplingFrequency = 0xb5L;
//! ID of the Channels element.
const ulli Channels = 0x9fL;
//! ID of the BitDepth element.
const ulli BitDepth = 0x6264L;
}
}
}
}
#endif

View File

@@ -1,563 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "ebmlmatroskaconstants.h"
#include "ebmlmatroskaaudio.h"
#include "tpicturemap.h"
#include "tpropertymap.h"
using namespace TagLib;
class EBML::Matroska::File::FilePrivate
{
public:
// Returns true if simpleTag has a TagName and TagString child and writes
// their contents into name and value.
bool extractContent(Element *simpleTag, String &name, String &value)
{
Element *n = simpleTag->getChild(Constants::TagName);
Element *v = simpleTag->getChild(Constants::TagString);
if(!n || !v)
return false;
name = n->getAsString();
value = v->getAsString();
return true;
}
explicit FilePrivate(File *p_document) : tag(0), document(p_document)
{
// Just get the first segment, because "Typically a Matroska file is
// composed of 1 segment."
Element* elem = document->getDocumentRoot()->getChild(Constants::Segment);
// We take the first tags element (there shouldn't be more), if there is
// non such element, consider the file as not compatible.
if(!elem || !(elem = elem->getChild(Constants::Tags))) {
document->setValid(false);
return;
}
// Load all Tag entries
List<Element *> entries = elem->getChildren(Constants::Tag);
for(List<Element *>::Iterator i = entries.begin(); i != entries.end(); ++i) {
Element *target = (*i)->getChild(Constants::Targets);
ulli ttvalue = 50; // 50 is default (see spec.)
if(target && (target = target->getChild(Constants::TargetTypeValue)))
ttvalue = target->getAsUnsigned();
// Load all SimpleTags
PropertyMap tagEntries;
List<Element *> simpleTags = (*i)->getChildren(Constants::SimpleTag);
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end();
++j) {
String name, value;
if(!extractContent(*j, name, value))
continue;
tagEntries.insert(name, StringList(value));
}
tags.append(std::pair<PropertyMap, std::pair<Element *, ulli> >(tagEntries, std::pair<Element *, ulli>(*i, ttvalue)));
}
}
// Creates Tag and AudioProperties. Late creation because both require a fully
// functional FilePrivate (well AudioProperties doesn't...)
void lateCreate()
{
tag = new Tag(document);
audio = new AudioProperties(document);
}
// Checks the EBML header and creates the FilePrivate.
static FilePrivate *checkAndCreate(File *document)
{
Element *elem = document->getDocumentRoot()->getChild(Header::EBML);
Element *child = elem->getChild(Header::DocType);
if(child) {
String dt = child->getAsString();
if (dt == Constants::DocTypeMatroska || dt == Constants::DocTypeWebM) {
FilePrivate *fp = new FilePrivate(document);
return fp;
}
}
return 0;
}
// The tags with their Element and TargetTypeValue
List<std::pair<PropertyMap, std::pair<Element *, ulli> > > tags;
// The tag
Tag *tag;
// The audio properties
AudioProperties *audio;
// The corresponding file.
File *document;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::Matroska::File::~File()
{
if (d) {
delete d->tag;
delete d->audio;
delete d;
}
}
EBML::Matroska::File::File(FileName file, bool, AudioProperties::ReadStyle) : EBML::File(file), d(0)
{
if(isValid() && isOpen()) {
d = FilePrivate::checkAndCreate(this);
if(!d)
setValid(false);
else
d->lateCreate();
}
}
EBML::Matroska::File::File(IOStream *stream, bool, AudioProperties::ReadStyle) : EBML::File(stream), d(0)
{
if(isValid() && isOpen()) {
d = FilePrivate::checkAndCreate(this);
if(!d)
setValid(false);
else
d->lateCreate();
}
}
Tag *EBML::Matroska::File::tag() const
{
return d->tag;
}
PropertyMap EBML::Matroska::File::properties() const
{
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
i != d->tags.end(); ++i) {
if(best == d->tags.end() || best->second.second > i->second.second)
best = i;
}
return best->first;
}
PropertyMap EBML::Matroska::File::setProperties(const PropertyMap &properties)
{
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
i != d->tags.end(); ++i) {
if(best == d->tags.end() || best->second.second > i->second.second)
best = i;
}
std::pair<PropertyMap, std::pair<Element *, ulli> > replace(properties, best->second);
d->tags.erase(best);
d->tags.prepend(replace);
return PropertyMap();
}
AudioProperties *EBML::Matroska::File::audioProperties() const
{
return d->audio;
}
bool EBML::Matroska::File::save()
{
if(readOnly())
return false;
// C++11 features would be nice: for(auto &i : d->tags) { /* ... */ }
// Well, here we just iterate each extracted element.
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
i != d->tags.end(); ++i) {
for(PropertyMap::Iterator j = i->first.begin(); j != i->first.end(); ++j) {
// No element? Create it!
if(!i->second.first) {
// Should be save, since we already checked, when creating the object.
Element *container = d->document->getDocumentRoot()
->getChild(Constants::Segment)->getChild(Constants::Tags);
// Create Targets container
i->second.first = container->addElement(Constants::Tag);
Element *target = i->second.first->addElement(Constants::Targets);
if(i->second.second == Constants::MostCommonPartValue)
target->addElement(Constants::TargetType, Constants::TRACK);
else if(i->second.second == Constants::MostCommonGroupingValue)
target->addElement(Constants::TargetType, Constants::ALBUM);
target->addElement(Constants::TargetTypeValue, i->second.second);
}
// Find entries
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
StringList::Iterator str = j->second.begin();
List<Element *>::Iterator k = simpleTags.begin();
for(; k != simpleTags.end(); ++k) {
String name, value;
if(!d->extractContent(*k, name, value))
continue;
// Write entry from StringList
if(name == j->first) {
if(str == j->second.end()) {
// We have all StringList elements but still found another element
// with the same name? Let's delete it!
i->second.first->removeChild(*k);
}
else {
if(value != *str) {
// extractContent already checked for availability
(*k)->getChild(Constants::TagString)->setAsString(*str);
}
++str;
}
}
}
// If we didn't write the complete StringList, we have to write the rest.
for(; str != j->second.end(); ++str) {
Element *stag = i->second.first->addElement(Constants::SimpleTag);
stag->addElement(Constants::TagName, j->first);
stag->addElement(Constants::TagString, *str);
}
}
// Finally, we have to find elements that are not in the PropertyMap and
// remove them.
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end(); ++j) {
String name, value;
if(!d->extractContent(*j, name, value))
continue;
if(i->first.find(name) == i->first.end()){
i->second.first->removeChild(*j);}
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
//
// Tag
//
////////////////////////////////////////////////////////////////////////////////
class EBML::Matroska::File::Tag::TagPrivate
{
public:
// Creates a TagPrivate instance
explicit TagPrivate(File *p_document) :
document(p_document),
title(document->d->tags.end()),
artist(document->d->tags.end()),
album(document->d->tags.end()),
comment(document->d->tags.end()),
genre(document->d->tags.end()),
year(document->d->tags.end()),
track(document->d->tags.end())
{
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
// Just save it, if the title is more specific, or there is no title yet.
if(i->first.find(Constants::TITLE) != i->first.end() &&
(title == document->d->tags.end() ||
title->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
title = i;
}
// Same goes for artist.
if(i->first.find(Constants::ARTIST) != i->first.end() &&
(artist == document->d->tags.end() ||
artist->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
artist = i;
}
// Here, we also look for a title (the album title), but since we
// specified the granularity, we have to search for it exactly.
// Therefore it is possible, that title and album are the same (if only
// the title of the album is given).
if(i->first.find(Constants::TITLE) != i->first.end() &&
i->second.second == Constants::MostCommonGroupingValue) {
album = i;
}
// Again the same as title and artist.
if(i->first.find(Constants::COMMENT) != i->first.end() &&
(comment == document->d->tags.end() ||
comment->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
comment = i;
}
// Same goes for genre.
if(i->first.find(Constants::GENRE) != i->first.end() &&
(genre == document->d->tags.end() ||
genre->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
genre = i;
}
// And year (in our case: DATE_REALEASE)
if(i->first.find(Constants::DATE_RELEASE) != i->first.end() &&
(year == document->d->tags.end() ||
year->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
year = i;
}
// And track (in our case: PART_NUMBER)
if(i->first.find(Constants::PART_NUMBER) != i->first.end() &&
(track == document->d->tags.end() ||
track->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
track = i;
}
}
}
// Searches for the Tag with given TargetTypeValue (returns the first one)
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator
find(ulli ttv)
{
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
if(i->second.second == ttv)
return i;
}
return document->d->tags.end();
}
// Updates the given information
void update(
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator t,
const String &tagname,
const String &s
)
{
t->first.find(tagname)->second.front() = s;
}
// Inserts a tag with given information
void insert(const String &tagname, const ulli ttv, const String &s)
{
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
if(i->second.second == ttv) {
i->first.insert(tagname, StringList(s));
return;
}
}
// Not found? Create new!
PropertyMap pm;
pm.insert(tagname, StringList(s));
document->d->tags.append(
std::pair<PropertyMap, std::pair<Element *, ulli> >(pm,
std::pair<Element *, ulli>(static_cast<Element *>(0), ttv)
)
);
}
// The PropertyMap from the Matroska::File
File *document;
// Iterators to the tags.
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator title;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator artist;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator album;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator comment;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator genre;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator year;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator track;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::Matroska::File::Tag::~Tag()
{
delete e;
}
EBML::Matroska::File::Tag::Tag(EBML::Matroska::File *document) :
e(new EBML::Matroska::File::Tag::TagPrivate(document))
{
}
String EBML::Matroska::File::Tag::title() const
{
if(e->title != e->document->d->tags.end())
return e->title->first.find(Constants::TITLE)->second.front();
else
return String();
}
String EBML::Matroska::File::Tag::artist() const
{
if(e->artist != e->document->d->tags.end())
return e->artist->first.find(Constants::ARTIST)->second.front();
else
return String();
}
String EBML::Matroska::File::Tag::album() const
{
if(e->album != e->document->d->tags.end())
return e->album->first.find(Constants::TITLE)->second.front();
else
return String();
}
PictureMap EBML::Matroska::File::Tag::pictures() const
{
return PictureMap();
}
String EBML::Matroska::File::Tag::comment() const
{
if(e->comment != e->document->d->tags.end())
return e->comment->first.find(Constants::COMMENT)->second.front();
else
return String();
}
String EBML::Matroska::File::Tag::genre() const
{
if(e->genre != e->document->d->tags.end())
return e->genre->first.find(Constants::GENRE)->second.front();
else
return String();
}
unsigned int EBML::Matroska::File::Tag::year() const
{
if(e->year != e->document->d->tags.end())
return e->year->first.find(Constants::DATE_RELEASE)->second.front().toInt();
else
return 0;
}
unsigned int EBML::Matroska::File::Tag::track() const
{
if(e->track != e->document->d->tags.end())
return e->track->first.find(Constants::PART_NUMBER)->second.front().toInt();
else
return 0;
}
void EBML::Matroska::File::Tag::setTitle(const String &s)
{
if(e->title != e->document->d->tags.end())
e->update(e->title, Constants::TITLE, s);
else
e->insert(Constants::TITLE, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setArtist(const String &s)
{
if(e->artist != e->document->d->tags.end())
e->update(e->artist, Constants::ARTIST, s);
else
e->insert(Constants::ARTIST, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setAlbum(const String &s)
{
if(e->album != e->document->d->tags.end())
e->update(e->album, Constants::TITLE, s);
else
e->insert(Constants::TITLE, Constants::MostCommonGroupingValue, s);
}
void EBML::Matroska::File::Tag::setPictures(const PictureMap& p )
{
(void)p; // avoid warning for unused variable
}
void EBML::Matroska::File::Tag::setComment(const String &s)
{
if(e->comment != e->document->d->tags.end())
e->update(e->comment, Constants::COMMENT, s);
else
e->insert(Constants::COMMENT, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setGenre(const String &s)
{
if(e->genre != e->document->d->tags.end())
e->update(e->genre, Constants::GENRE, s);
else
e->insert(Constants::GENRE, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setYear(unsigned int i)
{
String s = String::number(i);
if(e->year != e->document->d->tags.end())
e->update(e->year, Constants::DATE_RELEASE, s);
else
e->insert(Constants::DATE_RELEASE, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setTrack(unsigned int i)
{
String s = String::number(i);
if(e->track != e->document->d->tags.end())
e->update(e->track, Constants::PART_NUMBER, s);
else
e->insert(Constants::PART_NUMBER, Constants::MostCommonPartValue, s);
}

View File

@@ -1,228 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_EBMLMATROSKAFILE_H
#define TAGLIB_EBMLMATROSKAFILE_H
#include "ebmlelement.h"
#include "audioproperties.h"
namespace TagLib {
namespace EBML {
//! Implementation for reading Matroska tags.
namespace Matroska {
/*!
* Implements the TagLib::File API and offers access to the tags of the
* matroska file.
*/
class TAGLIB_EXPORT File : public EBML::File
{
public:
//! Destroys the instance of the file.
virtual ~File();
/*!
* Constructs a Matroska file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
explicit File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Constructs a Matroska file from \a stream. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
explicit File(IOStream *stream, bool readproperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Returns the pointer to a tag that allow access on common tags.
*/
virtual TagLib::Tag *tag() const;
/*!
* Exports the tags to a PropertyMap. Due to the diversity of the
* Matroska format (e.g. multiple media streams in one file, each with
* its own tags), only the best fitting tags are taken into account.
* There are no unsupported tags.
*/
virtual PropertyMap properties() const;
/*!
* Sets the tags of the file to those specified in properties. The
* returned PropertyMap is always empty.
* Note: Only the best fitting tags are taken into account.
*/
virtual PropertyMap setProperties(const PropertyMap &properties);
/*!
* Returns a pointer to this file's audio properties.
*
* I'm glad about not having a setAudioProperties method ;)
*/
virtual AudioProperties *audioProperties() const;
/*!
* Saves the file. Returns true on success.
*/
bool save();
/*!
* Offers access to a few common tag entries.
*/
class Tag : public TagLib::Tag
{
public:
//! Destroys the tag.
~Tag();
/*!
* Creates a new Tag for Matroska files. The given properties are gained
* by the Matroska::File.
*/
explicit Tag(File *document);
/*!
* Returns the track name; if no track name is present in the tag
* String::null will be returned.
*/
virtual String title() const;
/*!
* Returns the artist name; if no artist name is present in the tag
* String::null will be returned.
*/
virtual String artist() const;
/*!
* Returns the album name; if no album name is present in the tag
* String::null will be returned.
*/
virtual String album() const;
/*!
* Returns a list of pictures; if no picture is present in the tag
* an empty PictureMap is returned
*/
virtual PictureMap pictures() const;
/*!
* Returns the track comment; if no comment is present in the tag
* String::null will be returned.
*/
virtual String comment() const;
/*!
* Returns the genre name; if no genre is present in the tag String::null
* will be returned.
*/
virtual String genre() const;
/*!
* Returns the year; if there is no year set, this will return 0.
*/
virtual unsigned int year() const;
/*!
* Returns the track number; if there is no track number set, this will
* return 0.
*/
virtual unsigned int track() const;
/*!
* Sets the title to s. If s is String::null then this value will be
* cleared.
*/
virtual void setTitle(const String &s);
/*!
* Sets the artist to s. If s is String::null then this value will be
* cleared.
*/
virtual void setArtist(const String &s);
/*!
* Sets the album to s. If s is String::null then this value will be
* cleared.
*/
virtual void setAlbum(const String &s);
/*!
* Sets the picture map to p. If p is empty then this value will be
* cleared
*/
virtual void setPictures(const PictureMap& p );
/*!
* Sets the comment to s. If s is String::null then this value will be
* cleared.
*/
virtual void setComment(const String &s);
/*!
* Sets the genre to s. If s is String::null then this value will be
* cleared.
*/
virtual void setGenre(const String &s);
/*!
* Sets the year to i. If s is 0 then this value will be cleared.
*/
virtual void setYear(unsigned int i);
/*!
* Sets the track to i. If s is 0 then this value will be cleared.
*/
virtual void setTrack(unsigned int i);
private:
class TagPrivate;
TagPrivate *e;
};
private:
class FilePrivate;
FilePrivate *d;
};
}
}
}
#endif

View File

@@ -31,7 +31,7 @@
#include <tfilestream.h>
#include <tstring.h>
#include <tdebug.h>
#include <tsmartptr.h>
#include <trefcounter.h>
#include "fileref.h"
#include "asffile.h"
@@ -52,9 +52,6 @@
#include "s3mfile.h"
#include "itfile.h"
#include "xmfile.h"
#include "dsffile.h"
#include "dsdifffile.h"
#include "ebmlmatroskafile.h"
using namespace TagLib;
@@ -84,14 +81,14 @@ namespace
AudioProperties::ReadStyle audioPropertiesStyle)
{
#ifdef _WIN32
const String s(stream->name().wstr());
const String s = stream->name().toString();
#else
const String s(stream->name());
#endif
String ext;
const size_t pos = s.rfind(".");
if(pos != String::npos())
const int pos = s.rfind(".");
if(pos != -1)
ext = s.substr(pos + 1).upper();
// If this list is updated, the method defaultFileExtensions() should also be
@@ -108,7 +105,7 @@ namespace
if(ext == "OGG")
return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "FLAC")
return new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
return new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "MPC")
return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
@@ -138,13 +135,6 @@ namespace
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "DFF" || ext == "DSDIFF")
return new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "DSF")
return new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
if (ext == "MKA" || ext == "MKV") {
return new EBML::Matroska::File(stream, readAudioProperties, audioPropertiesStyle);
}
return 0;
}
@@ -163,7 +153,7 @@ namespace
else if(Ogg::FLAC::File::isSupported(stream))
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(FLAC::File::isSupported(stream))
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
file = new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
else if(MPC::File::isSupported(stream))
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(WavPack::File::isSupported(stream))
@@ -184,10 +174,6 @@ namespace
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
else if(APE::File::isSupported(stream))
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
else if(DSDIFF::File::isSupported(stream))
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(DSF::File::isSupported(stream))
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
// isSupported() only does a quick check, so double check the file here.
@@ -201,29 +187,94 @@ namespace
return 0;
}
struct FileRefData
// 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)
{
FileRefData() :
file(0),
stream(0) {}
File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
~FileRefData() {
#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;
delete stream;
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);
File *file;
IOStream *stream;
};
return 0;
}
}
class FileRef::FileRefPrivate
class FileRef::FileRefPrivate : public RefCounter
{
public:
FileRefPrivate() :
data(new FileRefData()) {}
RefCounter(),
file(0),
stream(0) {}
SHARED_PTR<FileRefData> data;
~FileRefPrivate() {
delete file;
delete stream;
}
File *file;
IOStream *stream;
};
////////////////////////////////////////////////////////////////////////////////
@@ -251,17 +302,19 @@ FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::Re
FileRef::FileRef(File *file) :
d(new FileRefPrivate())
{
d->data->file = file;
d->file = file;
}
FileRef::FileRef(const FileRef &ref) :
d(new FileRefPrivate(*ref.d))
d(ref.d)
{
d->ref();
}
FileRef::~FileRef()
{
delete d;
if(d->deref())
delete d;
}
Tag *FileRef::tag() const
@@ -270,37 +323,7 @@ Tag *FileRef::tag() const
debug("FileRef::tag() - Called without a valid file.");
return 0;
}
return d->data->file->tag();
}
PropertyMap FileRef::properties() const
{
if(isNull()) {
debug("FileRef::properties() - Called without a valid file.");
return PropertyMap();
}
return d->data->file->properties();
}
void FileRef::removeUnsupportedProperties(const StringList& properties)
{
if(isNull()) {
debug("FileRef::removeUnsupportedProperties() - Called without a valid file.");
return;
}
d->data->file->removeUnsupportedProperties(properties);
}
PropertyMap FileRef::setProperties(const PropertyMap &properties)
{
if(isNull()) {
debug("FileRef::setProperties() - Called without a valid file.");
return PropertyMap();
}
return d->data->file->setProperties(properties);
return d->file->tag();
}
AudioProperties *FileRef::audioProperties() const
@@ -309,12 +332,12 @@ AudioProperties *FileRef::audioProperties() const
debug("FileRef::audioProperties() - Called without a valid file.");
return 0;
}
return d->data->file->audioProperties();
return d->file->audioProperties();
}
File *FileRef::file() const
{
return d->data->file;
return d->file;
}
bool FileRef::save()
@@ -323,7 +346,7 @@ bool FileRef::save()
debug("FileRef::save() - Called without a valid file.");
return false;
}
return d->data->file->save();
return d->file->save();
}
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
@@ -364,23 +387,13 @@ StringList FileRef::defaultFileExtensions()
l.append("s3m");
l.append("it");
l.append("xm");
l.append("dsf");
l.append("dff");
l.append("dsdiff"); // alias for "dff"
l.append("mka");
l.append("mkv");
return l;
}
bool FileRef::isValid() const
{
return (d->data->file && d->data->file->isValid());
}
bool FileRef::isNull() const
{
return !isValid();
return (!d->file || !d->file->isValid());
}
FileRef &FileRef::operator=(const FileRef &ref)
@@ -398,12 +411,18 @@ void FileRef::swap(FileRef &ref)
bool FileRef::operator==(const FileRef &ref) const
{
return (ref.d->data == d->data);
return (ref.d->file == d->file);
}
bool FileRef::operator!=(const FileRef &ref) const
{
return (ref.d->data != d->data);
return (ref.d->file != d->file);
}
File *FileRef::create(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) // static
{
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
}
////////////////////////////////////////////////////////////////////////////////
@@ -415,27 +434,27 @@ void FileRef::parse(FileName fileName, bool readAudioProperties,
{
// Try user-defined resolvers.
d->data->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(d->data->file)
d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try to resolve file types based on the file extension.
d->data->stream = new FileStream(fileName);
d->data->file = detectByExtension(d->data->stream, readAudioProperties, audioPropertiesStyle);
if(d->data->file)
d->stream = new FileStream(fileName);
d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// At last, try to resolve file types based on the actual content.
d->data->file = detectByContent(d->data->stream, readAudioProperties, audioPropertiesStyle);
if(d->data->file)
d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Stream have to be closed here if failed to resolve file types.
delete d->data->stream;
d->data->stream = 0;
delete d->stream;
d->stream = 0;
}
void FileRef::parse(IOStream *stream, bool readAudioProperties,
@@ -445,11 +464,11 @@ void FileRef::parse(IOStream *stream, bool readAudioProperties,
// Try to resolve file types based on the file extension.
d->data->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
if(d->data->file)
d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// At last, try to resolve file types based on the actual content of the file.
d->data->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
}

View File

@@ -26,10 +26,10 @@
#ifndef TAGLIB_FILEREF_H
#define TAGLIB_FILEREF_H
#include "taglib_export.h"
#include "tfile.h"
#include "tstringlist.h"
#include "tpropertymap.h"
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
@@ -91,9 +91,8 @@ namespace TagLib {
class TAGLIB_EXPORT FileTypeResolver
{
TAGLIB_IGNORE_MISSING_DESTRUCTOR
public:
virtual ~FileTypeResolver() {}
/*!
* This method must be overridden to provide an additional file type
* resolver. If the resolver is able to determine the file type it should
@@ -174,41 +173,6 @@ 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 the according
* specialization in the File subclasses.
* For each metadata object of the file that could not be parsed into the PropertyMap
* format, the returend map's unsupportedData() list will contain one entry identifying
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
* to remove (a subset of) them.
* For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2
* 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 this File to those specified in \a properties. Calls the
* according specialization method in the subclasses of File to do the translation
* into the format-specific details.
* If some value(s) could not be written imported to the specific metadata format,
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
* indicating that no problems occured.
* With file types that support several tag formats (for instance, MP3 files can have
* ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one
* (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't
* 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);
/*!
* Returns the audio properties for this FileRef. If no audio properties
* were read then this will returns a null pointer.
@@ -269,17 +233,8 @@ namespace TagLib {
*/
static StringList defaultFileExtensions();
/*!
* Returns true if the file is open and readable.
*
* \note Just a negative of isNull().
*/
bool isValid() const;
/*!
* Returns true if the file (and as such other pointers) are null.
*
* \note Just a negative of isValid().
*/
bool isNull() const;
@@ -304,6 +259,21 @@ 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
*/
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);

View File

@@ -30,7 +30,6 @@
#include <tagunion.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <tsmartptr.h>
#include <id3v2header.h>
#include <id3v2tag.h>
@@ -46,61 +45,53 @@ using namespace TagLib;
namespace
{
typedef List<SHARED_PTR<FLAC::MetadataBlock> > BlockList;
typedef List<FLAC::MetadataBlock *> BlockList;
typedef BlockList::Iterator BlockIterator;
typedef BlockList::Iterator BlockConstIterator;
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
const long long MinPaddingLength = 4096;
const long long MaxPaddingLegnth = 1024 * 1024;
const long MinPaddingLength = 4096;
const long MaxPaddingLegnth = 1024 * 1024;
const char LastBlockFlag = '\x80';
}
namespace TagLib
{
namespace FLAC
{
// Enables BlockList::find() to take raw pointers.
bool operator==(SHARED_PTR<MetadataBlock> lhs, MetadataBlock *rhs)
{
return lhs.get() == rhs;
}
}
}
class FLAC::File::FilePrivate
{
public:
explicit FilePrivate(const ID3v2::FrameFactory *frameFactory) :
ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
ID3v2FrameFactory(frameFactory),
ID3v2Location(-1),
ID3v2OriginalSize(0),
ID3v1Location(-1),
properties(0),
flacStart(0),
streamStart(0),
scanned(false)
{
if(frameFactory)
ID3v2FrameFactory = frameFactory;
blocks.setAutoDelete(true);
}
~FilePrivate()
{
delete properties;
}
const ID3v2::FrameFactory *ID3v2FrameFactory;
long long ID3v2Location;
long long ID3v2OriginalSize;
long ID3v2Location;
long ID3v2OriginalSize;
long long ID3v1Location;
long ID3v1Location;
TripleTagUnion tag;
TagUnion tag;
SCOPED_PTR<AudioProperties> properties;
Properties *properties;
ByteVector xiphCommentData;
BlockList blocks;
long long flacStart;
long long streamStart;
long flacStart;
long streamStart;
bool scanned;
};
@@ -113,15 +104,23 @@ bool FLAC::File::isSupported(IOStream *stream)
// A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return (buffer.find("fLaC") != ByteVector::npos());
return (buffer.find("fLaC") >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
if(isOpen())
read(readProperties);
}
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate(frameFactory))
{
@@ -129,8 +128,8 @@ FLAC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle,
read(readProperties);
}
FLAC::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate(frameFactory))
{
@@ -148,14 +147,24 @@ TagLib::Tag *FLAC::File::tag() const
return &d->tag;
}
PropertyMap FLAC::File::properties() const
{
return d->tag.properties();
}
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
{
d->tag.removeUnsupportedProperties(unsupported);
}
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
{
return xiphComment(true)->setProperties(properties);
}
FLAC::AudioProperties *FLAC::File::audioProperties() const
FLAC::Properties *FLAC::File::audioProperties() const
{
return d->properties.get();
return d->properties;
}
bool FLAC::File::save()
@@ -181,20 +190,20 @@ bool FLAC::File::save()
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
if((*it)->code() == MetadataBlock::VorbisComment) {
// Set the new Vorbis Comment block
delete *it;
d->blocks.erase(it);
break;
}
}
d->blocks.append(SHARED_PTR<MetadataBlock>(
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData)));
d->blocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
// Render data for the metadata blocks
ByteVector data;
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
ByteVector blockData = (*it)->render();
ByteVector blockHeader = ByteVector::fromUInt32BE(blockData.size());
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
blockHeader[0] = (*it)->code();
data.append(blockHeader);
data.append(blockData);
@@ -202,8 +211,8 @@ bool FLAC::File::save()
// Compute the amount of padding, and append that to data.
long long originalLength = d->streamStart - d->flacStart;
long long paddingLength = originalLength - data.size() - 4;
long originalLength = d->streamStart - d->flacStart;
long paddingLength = originalLength - data.size() - 4;
if(paddingLength <= 0) {
paddingLength = MinPaddingLength;
@@ -211,7 +220,7 @@ bool FLAC::File::save()
else {
// Padding won't increase beyond 1% of the file size or 1MB.
long long threshold = length() / 100;
long threshold = length() / 100;
threshold = std::max(threshold, MinPaddingLength);
threshold = std::min(threshold, MaxPaddingLegnth);
@@ -219,14 +228,14 @@ bool FLAC::File::save()
paddingLength = MinPaddingLength;
}
ByteVector paddingHeader = ByteVector::fromUInt32BE(static_cast<unsigned int>(paddingLength));
ByteVector paddingHeader = ByteVector::fromUInt(paddingLength);
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
data.append(paddingHeader);
data.resize(static_cast<size_t>(data.size() + paddingLength));
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
// Write the data to the file
insert(data, d->flacStart, static_cast<size_t>(originalLength));
insert(data, d->flacStart, originalLength);
d->streamStart += (static_cast<long>(data.size()) - originalLength);
@@ -243,7 +252,7 @@ bool FLAC::File::save()
d->ID3v2Location = 0;
data = ID3v2Tag()->render();
insert(data, d->ID3v2Location, static_cast<size_t>(d->ID3v2OriginalSize));
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
d->flacStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
d->streamStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
@@ -258,7 +267,7 @@ bool FLAC::File::save()
// ID3v2 tag is empty. Remove the old one.
if(d->ID3v2Location >= 0) {
removeBlock(d->ID3v2Location, static_cast<size_t>(d->ID3v2OriginalSize));
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
d->flacStart -= d->ID3v2OriginalSize;
d->streamStart -= d->ID3v2OriginalSize;
@@ -313,11 +322,28 @@ 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->get());
Picture *picture = dynamic_cast<Picture *>(*it);
if(picture) {
pictures.append(picture);
}
@@ -327,20 +353,24 @@ List<FLAC::Picture *> FLAC::File::pictureList()
void FLAC::File::addPicture(Picture *picture)
{
d->blocks.append(SHARED_PTR<Picture>(picture));
d->blocks.append(picture);
}
void FLAC::File::removePicture(Picture *picture)
void FLAC::File::removePicture(Picture *picture, bool del)
{
BlockIterator it = d->blocks.find(picture);
if(it != d->blocks.end())
d->blocks.erase(it);
if(del)
delete picture;
}
void FLAC::File::removePictures()
{
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
if(dynamic_cast<Picture *>(it->get())) {
if(dynamic_cast<Picture *>(*it)) {
delete *it;
it = d->blocks.erase(it);
}
else {
@@ -418,14 +448,14 @@ void FLAC::File::read(bool readProperties)
const ByteVector infoData = d->blocks.front()->render();
long long streamLength;
long streamLength;
if(d->ID3v1Location >= 0)
streamLength = d->ID3v1Location - d->streamStart;
else
streamLength = length() - d->streamStart;
d->properties.reset(new AudioProperties(infoData, streamLength));
d->properties = new Properties(infoData, streamLength);
}
}
@@ -439,7 +469,7 @@ void FLAC::File::scan()
if(!isValid())
return;
long long nextBlockOffset;
long nextBlockOffset;
if(d->ID3v2Location >= 0)
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
@@ -474,7 +504,7 @@ void FLAC::File::scan()
const char blockType = header[0] & ~LastBlockFlag;
const bool isLastBlock = (header[0] & LastBlockFlag) != 0;
const size_t blockLength = header.toUInt24BE(1);
const unsigned int blockLength = header.toUInt(1U, 3U);
// First block should be the stream_info metadata
@@ -499,32 +529,33 @@ void FLAC::File::scan()
return;
}
SHARED_PTR<MetadataBlock> block;
MetadataBlock *block = 0;
// Found the vorbis-comment
if(blockType == MetadataBlock::VorbisComment) {
if(d->xiphCommentData.isEmpty()) {
d->xiphCommentData = data;
block.reset(new UnknownMetadataBlock(MetadataBlock::VorbisComment, data));
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
}
else {
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
}
}
else if(blockType == MetadataBlock::Picture) {
SHARED_PTR<FLAC::Picture> picture(new FLAC::Picture());
FLAC::Picture *picture = new FLAC::Picture();
if(picture->parse(data)) {
block = picture;
}
else {
debug("FLAC::File::scan() -- invalid picture found, discarding");
delete picture;
}
}
else if(blockType == MetadataBlock::Padding) {
// Skip all padding blocks.
}
else {
block.reset(new UnknownMetadataBlock(blockType, data));
block = new UnknownMetadataBlock(blockType, data);
}
if(block)

View File

@@ -83,6 +83,18 @@ namespace TagLib {
AllTags = 0xffff
};
/*!
* Constructs a FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated This constructor will be dropped in favor of the one below
* in a future version.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
@@ -92,27 +104,27 @@ namespace TagLib {
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file,
// BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = 0);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs a FLAC file from \a file. If \a readProperties is true the
* Constructs a FLAC file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream,
// BIC: merge with the above constructor
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = 0);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@@ -129,6 +141,16 @@ namespace TagLib {
*/
virtual TagLib::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains more than one tag (e.g. XiphComment and ID3v1),
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
* converted to the PropertyMap.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &);
/*!
* Implements the unified property interface -- import function.
* This always creates a Xiph comment, if none exists. The return value
@@ -136,13 +158,13 @@ namespace TagLib {
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
* in the FLAC specification.
*/
virtual PropertyMap setProperties(const PropertyMap &);
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the FLAC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const;
virtual Properties *audioProperties() const;
/*!
* Save the file. This will primarily save the XiphComment, but
@@ -210,15 +232,42 @@ 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.
*/
List<Picture *> pictureList();
/*!
* Removes an attached picture. The picture's memory will be freed.
* Removes an attached picture. If \a del is true the picture's memory
* will be freed; if it is false, it must be deleted by the user.
*/
void removePicture(Picture *picture);
void removePicture(Picture *picture, bool del = true);
/*!
* Remove all attached images.

View File

@@ -78,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
return false;
}
size_t pos = 0;
d->type = FLAC::Picture::Type(data.toUInt32BE(pos));
unsigned int pos = 0;
d->type = FLAC::Picture::Type(data.toUInt(pos));
pos += 4;
const unsigned int mimeTypeLength = data.toUInt32BE(pos);
unsigned int mimeTypeLength = data.toUInt(pos);
pos += 4;
if(pos + mimeTypeLength + 24 > data.size()) {
debug("Invalid picture block.");
@@ -89,7 +89,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
pos += mimeTypeLength;
const unsigned int descriptionLength = data.toUInt32BE(pos);
unsigned int descriptionLength = data.toUInt(pos);
pos += 4;
if(pos + descriptionLength + 20 > data.size()) {
debug("Invalid picture block.");
@@ -97,15 +97,15 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->description = String(data.mid(pos, descriptionLength), String::UTF8);
pos += descriptionLength;
d->width = data.toUInt32BE(pos);
d->width = data.toUInt(pos);
pos += 4;
d->height = data.toUInt32BE(pos);
d->height = data.toUInt(pos);
pos += 4;
d->colorDepth = data.toUInt32BE(pos);
d->colorDepth = data.toUInt(pos);
pos += 4;
d->numColors = data.toUInt32BE(pos);
d->numColors = data.toUInt(pos);
pos += 4;
const unsigned int dataLength = data.toUInt32BE(pos);
unsigned int dataLength = data.toUInt(pos);
pos += 4;
if(pos + dataLength > data.size()) {
debug("Invalid picture block.");
@@ -119,18 +119,18 @@ bool FLAC::Picture::parse(const ByteVector &data)
ByteVector FLAC::Picture::render() const
{
ByteVector result;
result.append(ByteVector::fromUInt32BE(d->type));
result.append(ByteVector::fromUInt(d->type));
ByteVector mimeTypeData = d->mimeType.data(String::UTF8);
result.append(ByteVector::fromUInt32BE(mimeTypeData.size()));
result.append(ByteVector::fromUInt(mimeTypeData.size()));
result.append(mimeTypeData);
ByteVector descriptionData = d->description.data(String::UTF8);
result.append(ByteVector::fromUInt32BE(descriptionData.size()));
result.append(ByteVector::fromUInt(descriptionData.size()));
result.append(descriptionData);
result.append(ByteVector::fromUInt32BE(d->width));
result.append(ByteVector::fromUInt32BE(d->height));
result.append(ByteVector::fromUInt32BE(d->colorDepth));
result.append(ByteVector::fromUInt32BE(d->numColors));
result.append(ByteVector::fromUInt32BE(d->data.size()));
result.append(ByteVector::fromUInt(d->width));
result.append(ByteVector::fromUInt(d->height));
result.append(ByteVector::fromUInt(d->colorDepth));
result.append(ByteVector::fromUInt(d->numColors));
result.append(ByteVector::fromUInt(d->data.size()));
result.append(d->data);
return result;
}

View File

@@ -89,7 +89,7 @@ namespace TagLib {
};
Picture();
explicit Picture(const ByteVector &data);
Picture(const ByteVector &data);
~Picture();
/*!
@@ -199,6 +199,8 @@ namespace TagLib {
PicturePrivate *d;
};
typedef List<Picture> PictureList;
}
}

View File

@@ -31,7 +31,7 @@
using namespace TagLib;
class FLAC::AudioProperties::PropertiesPrivate
class FLAC::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -55,64 +55,71 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::AudioProperties::AudioProperties(const ByteVector &data, long long streamLength, ReadStyle) :
TagLib::AudioProperties(),
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
read(data, streamLength);
}
FLAC::AudioProperties::~AudioProperties()
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::AudioProperties::length() const
int FLAC::Properties::length() const
{
return lengthInSeconds();
}
int FLAC::AudioProperties::lengthInSeconds() const
int FLAC::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int FLAC::AudioProperties::lengthInMilliseconds() const
int FLAC::Properties::lengthInMilliseconds() const
{
return d->length;
}
int FLAC::AudioProperties::bitrate() const
int FLAC::Properties::bitrate() const
{
return d->bitrate;
}
int FLAC::AudioProperties::sampleRate() const
int FLAC::Properties::sampleRate() const
{
return d->sampleRate;
}
int FLAC::AudioProperties::bitsPerSample() const
int FLAC::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
int FLAC::AudioProperties::sampleWidth() const
int FLAC::Properties::sampleWidth() const
{
return bitsPerSample();
}
int FLAC::AudioProperties::channels() const
int FLAC::Properties::channels() const
{
return d->channels;
}
unsigned long long FLAC::AudioProperties::sampleFrames() const
unsigned long long FLAC::Properties::sampleFrames() const
{
return d->sampleFrames;
}
ByteVector FLAC::AudioProperties::signature() const
ByteVector FLAC::Properties::signature() const
{
return d->signature;
}
@@ -121,14 +128,14 @@ ByteVector FLAC::AudioProperties::signature() const
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength)
void FLAC::Properties::read(const ByteVector &data, long streamLength)
{
if(data.size() < 18) {
debug("FLAC::AudioProperties::read() - FLAC properties must contain at least 18 bytes.");
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
return;
}
size_t pos = 0;
unsigned int pos = 0;
// Minimum block size (in samples)
pos += 2;
@@ -142,7 +149,7 @@ void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength)
// Maximum frame size (in bytes)
pos += 3;
const unsigned int flags = data.toUInt32BE(pos);
const unsigned int flags = data.toUInt(pos, true);
pos += 4;
d->sampleRate = flags >> 12;
@@ -153,7 +160,7 @@ void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength)
// stream length in samples. (Audio files measured in days)
const unsigned long long hi = flags & 0xf;
const unsigned long long lo = data.toUInt32BE(pos);
const unsigned long long lo = data.toUInt(pos, true);
pos += 4;
d->sampleFrames = (hi << 32) | lo;

View File

@@ -42,19 +42,27 @@ namespace TagLib {
* API.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Creates an instance of FLAC::AudioProperties with the data read from
* the ByteVector \a data.
* Create an instance of FLAC::Properties with the data read from the
* ByteVector \a data.
*/
AudioProperties(const ByteVector &data, long long streamLength, ReadStyle style = Average);
// BIC: switch to const reference
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
/*!
* Destroys this FLAC::AudioProperties instance.
* Create an instance of FLAC::Properties with the data read from the
* FLAC::File \a file.
*/
virtual ~AudioProperties();
// BIC: remove
Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this FLAC::Properties instance.
*/
virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -64,7 +72,7 @@ namespace TagLib {
*
* \deprecated
*/
virtual int length() const;
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -72,14 +80,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
@@ -110,7 +120,7 @@ namespace TagLib {
*
* \deprecated
*/
int sampleWidth() const;
TAGLIB_DEPRECATED int sampleWidth() const;
/*!
* Return the number of sample frames.
@@ -124,7 +134,10 @@ namespace TagLib {
ByteVector signature() const;
private:
void read(const ByteVector &data, long long streamLength);
Properties(const Properties &);
Properties &operator=(const Properties &);
void read(const ByteVector &data, long streamLength);
class PropertiesPrivate;
PropertiesPrivate *d;

View File

@@ -36,13 +36,13 @@ using namespace IT;
class IT::File::FilePrivate
{
public:
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle)
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: tag(), properties(propertiesStyle)
{
}
Mod::Tag tag;
IT::AudioProperties properties;
IT::Properties properties;
};
IT::File::File(FileName file, bool readProperties,
@@ -73,7 +73,17 @@ Mod::Tag *IT::File::tag() const
return &d->tag;
}
IT::AudioProperties *IT::File::audioProperties() const
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;
}
@@ -104,7 +114,7 @@ bool IT::File::save()
StringList lines = d->tag.comment().split("\n");
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
unsigned int instrumentOffset = 0;
unsigned long instrumentOffset = 0;
if(!readU32L(instrumentOffset))
return false;
@@ -119,7 +129,7 @@ bool IT::File::save()
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
unsigned int sampleOffset = 0;
unsigned long sampleOffset = 0;
if(!readU32L(sampleOffset))
return false;
@@ -146,14 +156,14 @@ bool IT::File::save()
unsigned short special = 0;
unsigned short messageLength = 0;
unsigned int messageOffset = 0;
unsigned long messageOffset = 0;
seek(46);
if(!readU16L(special))
return false;
unsigned int fileSize = static_cast<unsigned int>(File::length());
if(special & AudioProperties::MessageAttached) {
unsigned long fileSize = File::length();
if(special & Properties::MessageAttached) {
seek(54);
if(!readU16L(messageLength) || !readU32L(messageOffset))
return false;
@@ -171,7 +181,7 @@ bool IT::File::save()
if(messageOffset + messageLength >= fileSize) {
// append new message
seek(54);
writeU16L(static_cast<unsigned short>(message.size()));
writeU16L(message.size());
writeU32L(messageOffset);
seek(messageOffset);
writeBlock(message);
@@ -223,14 +233,14 @@ void IT::File::read(bool)
// sample/instrument names are abused as comments so
// I just add all together.
String message;
if(special & AudioProperties::MessageAttached) {
if(special & Properties::MessageAttached) {
READ_U16L_AS(messageLength);
READ_U32L_AS(messageOffset);
seek(messageOffset);
ByteVector messageBytes = readBlock(messageLength);
READ_ASSERT(messageBytes.size() == messageLength);
const size_t index = messageBytes.find((char) 0);
if(index != ByteVector::npos())
int index = messageBytes.find((char) 0);
if(index > -1)
messageBytes.resize(index, 0);
messageBytes.replace('\r', '\n');
message = messageBytes;
@@ -267,7 +277,7 @@ void IT::File::read(bool)
// in the instrument/sample names and more characters
// afterwards. The spec does not mention such a case.
// Currently I just discard anything after a nil, but
// e.g. VLC seems to interprete a nil as a space. I
// e.g. VLC seems to interpret a nil as a space. I
// don't know what is the proper behaviour.
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));

View File

@@ -67,11 +67,23 @@ namespace TagLib {
Mod::Tag *tag() const;
/*!
* 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 &);
/*!
* Returns the IT::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
IT::AudioProperties *audioProperties() const;
IT::Properties *audioProperties() const;
/*!
* Save the file.

View File

@@ -1,4 +1,4 @@
/***************************************************************************
/***************************************************************************
copyright :(C) 2011 by Mathias Panzenböck
email : grosser.meister.morti@gmx.net
***************************************************************************/
@@ -29,7 +29,7 @@
using namespace TagLib;
using namespace IT;
class IT::AudioProperties::PropertiesPrivate
class IT::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -47,7 +47,9 @@ public:
tempo(0),
bpmSpeed(0),
panningSeparation(0),
pitchWheelDepth(0) {}
pitchWheelDepth(0)
{
}
int channels;
unsigned short lengthInPatterns;
@@ -66,201 +68,193 @@ public:
unsigned char pitchWheelDepth;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
IT::AudioProperties::AudioProperties(AudioProperties::ReadStyle) :
TagLib::AudioProperties(),
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle),
d(new PropertiesPrivate())
{
}
IT::AudioProperties::~AudioProperties()
IT::Properties::~Properties()
{
delete d;
}
int IT::AudioProperties::length() const
int IT::Properties::length() const
{
return 0;
}
int IT::AudioProperties::lengthInSeconds() const
int IT::Properties::lengthInSeconds() const
{
return 0;
}
int IT::AudioProperties::lengthInMilliseconds() const
int IT::Properties::lengthInMilliseconds() const
{
return 0;
}
int IT::AudioProperties::bitrate() const
int IT::Properties::bitrate() const
{
return 0;
}
int IT::AudioProperties::sampleRate() const
int IT::Properties::sampleRate() const
{
return 0;
}
int IT::AudioProperties::channels() const
int IT::Properties::channels() const
{
return d->channels;
}
unsigned short IT::AudioProperties::lengthInPatterns() const
unsigned short IT::Properties::lengthInPatterns() const
{
return d->lengthInPatterns;
}
bool IT::AudioProperties::stereo() const
bool IT::Properties::stereo() const
{
return d->flags & Stereo;
}
unsigned short IT::AudioProperties::instrumentCount() const
unsigned short IT::Properties::instrumentCount() const
{
return d->instrumentCount;
}
unsigned short IT::AudioProperties::sampleCount() const
unsigned short IT::Properties::sampleCount() const
{
return d->sampleCount;
}
unsigned short IT::AudioProperties::patternCount() const
unsigned short IT::Properties::patternCount() const
{
return d->patternCount;
}
unsigned short IT::AudioProperties::version() const
unsigned short IT::Properties::version() const
{
return d->version;
}
unsigned short IT::AudioProperties::compatibleVersion() const
unsigned short IT::Properties::compatibleVersion() const
{
return d->compatibleVersion;
}
unsigned short IT::AudioProperties::flags() const
unsigned short IT::Properties::flags() const
{
return d->flags;
}
unsigned short IT::AudioProperties::special() const
unsigned short IT::Properties::special() const
{
return d->special;
}
unsigned char IT::AudioProperties::globalVolume() const
unsigned char IT::Properties::globalVolume() const
{
return d->globalVolume;
}
unsigned char IT::AudioProperties::mixVolume() const
unsigned char IT::Properties::mixVolume() const
{
return d->mixVolume;
}
unsigned char IT::AudioProperties::tempo() const
unsigned char IT::Properties::tempo() const
{
return d->tempo;
}
unsigned char IT::AudioProperties::bpmSpeed() const
unsigned char IT::Properties::bpmSpeed() const
{
return d->bpmSpeed;
}
unsigned char IT::AudioProperties::panningSeparation() const
unsigned char IT::Properties::panningSeparation() const
{
return d->panningSeparation;
}
unsigned char IT::AudioProperties::pitchWheelDepth() const
unsigned char IT::Properties::pitchWheelDepth() const
{
return d->pitchWheelDepth;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void IT::AudioProperties::setChannels(int channels)
void IT::Properties::setChannels(int channels)
{
d->channels = channels;
}
void IT::AudioProperties::setLengthInPatterns(unsigned short lengthInPatterns)
void IT::Properties::setLengthInPatterns(unsigned short lengthInPatterns)
{
d->lengthInPatterns = lengthInPatterns;
}
void IT::AudioProperties::setInstrumentCount(unsigned short instrumentCount)
void IT::Properties::setInstrumentCount(unsigned short instrumentCount)
{
d->instrumentCount = instrumentCount;
}
void IT::AudioProperties::setSampleCount(unsigned short sampleCount)
void IT::Properties::setSampleCount(unsigned short sampleCount)
{
d->sampleCount = sampleCount;
}
void IT::AudioProperties::setPatternCount(unsigned short patternCount)
void IT::Properties::setPatternCount(unsigned short patternCount)
{
d->patternCount = patternCount;
}
void IT::AudioProperties::setFlags(unsigned short flags)
void IT::Properties::setFlags(unsigned short flags)
{
d->flags = flags;
}
void IT::AudioProperties::setSpecial(unsigned short special)
void IT::Properties::setSpecial(unsigned short special)
{
d->special = special;
}
void IT::AudioProperties::setCompatibleVersion(unsigned short compatibleVersion)
void IT::Properties::setCompatibleVersion(unsigned short compatibleVersion)
{
d->compatibleVersion = compatibleVersion;
}
void IT::AudioProperties::setVersion(unsigned short version)
void IT::Properties::setVersion(unsigned short version)
{
d->version = version;
}
void IT::AudioProperties::setGlobalVolume(unsigned char globalVolume)
void IT::Properties::setGlobalVolume(unsigned char globalVolume)
{
d->globalVolume = globalVolume;
}
void IT::AudioProperties::setMixVolume(unsigned char mixVolume)
void IT::Properties::setMixVolume(unsigned char mixVolume)
{
d->mixVolume = mixVolume;
}
void IT::AudioProperties::setTempo(unsigned char tempo)
void IT::Properties::setTempo(unsigned char tempo)
{
d->tempo = tempo;
}
void IT::AudioProperties::setBpmSpeed(unsigned char bpmSpeed)
void IT::Properties::setBpmSpeed(unsigned char bpmSpeed)
{
d->bpmSpeed = bpmSpeed;
}
void IT::AudioProperties::setPanningSeparation(unsigned char panningSeparation)
void IT::Properties::setPanningSeparation(unsigned char panningSeparation)
{
d->panningSeparation = panningSeparation;
}
void IT::AudioProperties::setPitchWheelDepth(unsigned char pitchWheelDepth)
void IT::Properties::setPitchWheelDepth(unsigned char pitchWheelDepth)
{
d->pitchWheelDepth = pitchWheelDepth;
}

View File

@@ -30,15 +30,9 @@
#include "audioproperties.h"
namespace TagLib {
namespace IT {
class File;
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
{
class TAGLIB_EXPORT Properties : public AudioProperties {
friend class File;
public:
/*! Flag bits. */
enum {
@@ -58,8 +52,8 @@ namespace TagLib {
MidiConfEmbedded = 8
};
explicit AudioProperties(AudioProperties::ReadStyle propertiesStyle);
virtual ~AudioProperties();
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
int length() const;
int lengthInSeconds() const;
@@ -84,7 +78,6 @@ namespace TagLib {
unsigned char panningSeparation() const;
unsigned char pitchWheelDepth() const;
private:
void setChannels(int channels);
void setLengthInPatterns(unsigned short lengthInPatterns);
void setInstrumentCount(unsigned short instrumentCount);
@@ -101,6 +94,10 @@ namespace TagLib {
void setPanningSeparation(unsigned char panningSeparation);
void setPitchWheelDepth (unsigned char pitchWheelDepth);
private:
Properties(const Properties&);
Properties &operator=(const Properties&);
class PropertiesPrivate;
PropertiesPrivate *d;
};

View File

@@ -36,13 +36,13 @@ using namespace Mod;
class Mod::File::FilePrivate
{
public:
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle)
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: properties(propertiesStyle)
{
}
Mod::Tag tag;
Mod::AudioProperties properties;
Mod::Properties properties;
};
Mod::File::File(FileName file, bool readProperties,
@@ -73,11 +73,21 @@ Mod::Tag *Mod::File::tag() const
return &d->tag;
}
Mod::AudioProperties *Mod::File::audioProperties() const
Mod::Properties *Mod::File::audioProperties() const
{
return &d->properties;
}
PropertyMap Mod::File::properties() const
{
return d->tag.properties();
}
PropertyMap Mod::File::setProperties(const PropertyMap &properties)
{
return d->tag.setProperties(properties);
}
bool Mod::File::save()
{
if(readOnly()) {
@@ -87,13 +97,13 @@ bool Mod::File::save()
seek(0);
writeString(d->tag.title(), 20);
StringList lines = d->tag.comment().split("\n");
size_t n = std::min<size_t>(lines.size(), d->properties.instrumentCount());
for(size_t i = 0; i < n; ++ i) {
unsigned int n = std::min(lines.size(), d->properties.instrumentCount());
for(unsigned int i = 0; i < n; ++ i) {
writeString(lines[i], 22);
seek(8, Current);
}
for(size_t i = n; i < d->properties.instrumentCount(); ++ i) {
for(unsigned int i = n; i < d->properties.instrumentCount(); ++ i) {
writeString(String(), 22);
seek(8, Current);
}

View File

@@ -72,11 +72,22 @@ namespace TagLib {
Mod::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* Forwards to Mod::Tag::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* Forwards to Mod::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the Mod::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Mod::AudioProperties *audioProperties() const;
Mod::Properties *audioProperties() const;
/*!
* Save the file.

View File

@@ -38,19 +38,20 @@ Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
{
}
void Mod::FileBase::writeString(const String &s, unsigned int size, char padding)
void Mod::FileBase::writeString(const String &s, unsigned long size, char padding)
{
ByteVector data(s.data(String::Latin1));
data.resize(size, padding);
writeBlock(data);
}
bool Mod::FileBase::readString(String &s, unsigned int size)
bool Mod::FileBase::readString(String &s, unsigned long size)
{
ByteVector data(readBlock(size));
if(data.size() < size) return false;
const size_t index = data.find((char) 0);
if(index != ByteVector::npos()) {
int index = data.find((char) 0);
if(index > -1)
{
data.resize(index);
}
data.replace('\xff', ' ');
@@ -67,22 +68,22 @@ void Mod::FileBase::writeByte(unsigned char byte)
void Mod::FileBase::writeU16L(unsigned short number)
{
writeBlock(ByteVector::fromUInt16LE(number));
writeBlock(ByteVector::fromShort(number, false));
}
void Mod::FileBase::writeU32L(unsigned int number)
void Mod::FileBase::writeU32L(unsigned long number)
{
writeBlock(ByteVector::fromUInt32LE(number));
writeBlock(ByteVector::fromUInt(number, false));
}
void Mod::FileBase::writeU16B(unsigned short number)
{
writeBlock(ByteVector::fromUInt16BE(number));
writeBlock(ByteVector::fromShort(number, true));
}
void Mod::FileBase::writeU32B(unsigned int number)
void Mod::FileBase::writeU32B(unsigned long number)
{
writeBlock(ByteVector::fromUInt32BE(number));
writeBlock(ByteVector::fromUInt(number, true));
}
bool Mod::FileBase::readByte(unsigned char &byte)
@@ -97,14 +98,14 @@ bool Mod::FileBase::readU16L(unsigned short &number)
{
ByteVector data(readBlock(2));
if(data.size() < 2) return false;
number = data.toUInt16LE(0);
number = data.toUShort(false);
return true;
}
bool Mod::FileBase::readU32L(unsigned int &number) {
bool Mod::FileBase::readU32L(unsigned long &number) {
ByteVector data(readBlock(4));
if(data.size() < 4) return false;
number = data.toUInt32LE(0);
number = data.toUInt(false);
return true;
}
@@ -112,13 +113,13 @@ bool Mod::FileBase::readU16B(unsigned short &number)
{
ByteVector data(readBlock(2));
if(data.size() < 2) return false;
number = data.toUInt16BE(0);
number = data.toUShort(true);
return true;
}
bool Mod::FileBase::readU32B(unsigned int &number) {
bool Mod::FileBase::readU32B(unsigned long &number) {
ByteVector data(readBlock(4));
if(data.size() < 4) return false;
number = data.toUInt32BE(0);
number = data.toUInt(true);
return true;
}

View File

@@ -41,22 +41,22 @@ namespace TagLib {
class TAGLIB_EXPORT FileBase : public TagLib::File
{
protected:
explicit FileBase(FileName file);
explicit FileBase(IOStream *stream);
FileBase(FileName file);
FileBase(IOStream *stream);
void writeString(const String &s, unsigned int size, char padding = 0);
void writeString(const String &s, unsigned long size, char padding = 0);
void writeByte(unsigned char byte);
void writeU16L(unsigned short number);
void writeU32L(unsigned int number);
void writeU32L(unsigned long number);
void writeU16B(unsigned short number);
void writeU32B(unsigned int number);
void writeU32B(unsigned long number);
bool readString(String &s, unsigned int size);
bool readString(String &s, unsigned long size);
bool readByte(unsigned char &byte);
bool readU16L(unsigned short &number);
bool readU32L(unsigned int &number);
bool readU32L(unsigned long &number);
bool readU16B(unsigned short &number);
bool readU32B(unsigned int &number);
bool readU32B(unsigned long &number);
};
}

View File

@@ -39,9 +39,9 @@
#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 int,readU32L)
#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 int,readU32B)
#define READ_U32B(setter) READ(setter,unsigned long,readU32B)
#define READ_STRING(setter,size) \
{ \
@@ -56,9 +56,9 @@
#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 int,name,readU32L)
#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 int,name,readU32B)
#define READ_U32B_AS(name) READ_AS(unsigned long,name,readU32B)
#define READ_STRING_AS(name,size) \
String name; \

View File

@@ -1,4 +1,4 @@
/***************************************************************************
/***************************************************************************
copyright : (C) 2011 by Mathias Panzenböck
email : grosser.meister.morti@gmx.net
***************************************************************************/
@@ -29,89 +29,83 @@
using namespace TagLib;
using namespace Mod;
class Mod::AudioProperties::PropertiesPrivate
class Mod::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
channels(0),
instrumentCount(0),
lengthInPatterns(0) {}
lengthInPatterns(0)
{
}
int channels;
unsigned int instrumentCount;
unsigned char lengthInPatterns;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Mod::AudioProperties::AudioProperties(AudioProperties::ReadStyle) :
TagLib::AudioProperties(),
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle),
d(new PropertiesPrivate())
{
}
Mod::AudioProperties::~AudioProperties()
Mod::Properties::~Properties()
{
delete d;
}
int Mod::AudioProperties::length() const
int Mod::Properties::length() const
{
return 0;
}
int Mod::AudioProperties::lengthInSeconds() const
int Mod::Properties::lengthInSeconds() const
{
return 0;
}
int Mod::AudioProperties::lengthInMilliseconds() const
int Mod::Properties::lengthInMilliseconds() const
{
return 0;
}
int Mod::AudioProperties::bitrate() const
int Mod::Properties::bitrate() const
{
return 0;
}
int Mod::AudioProperties::sampleRate() const
int Mod::Properties::sampleRate() const
{
return 0;
}
int Mod::AudioProperties::channels() const
int Mod::Properties::channels() const
{
return d->channels;
}
unsigned int Mod::AudioProperties::instrumentCount() const
unsigned int Mod::Properties::instrumentCount() const
{
return d->instrumentCount;
}
unsigned char Mod::AudioProperties::lengthInPatterns() const
unsigned char Mod::Properties::lengthInPatterns() const
{
return d->lengthInPatterns;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Mod::AudioProperties::setChannels(int channels)
void Mod::Properties::setChannels(int channels)
{
d->channels = channels;
}
void Mod::AudioProperties::setInstrumentCount(unsigned int instrumentCount)
void Mod::Properties::setInstrumentCount(unsigned int instrumentCount)
{
d->instrumentCount = instrumentCount;
}
void Mod::AudioProperties::setLengthInPatterns(unsigned char lengthInPatterns)
void Mod::Properties::setLengthInPatterns(unsigned char lengthInPatterns)
{
d->lengthInPatterns = lengthInPatterns;
}

View File

@@ -33,15 +33,11 @@ namespace TagLib {
namespace Mod {
class File;
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
friend class File;
public:
explicit AudioProperties(AudioProperties::ReadStyle propertiesStyle);
virtual ~AudioProperties();
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
int length() const;
int lengthInSeconds() const;
@@ -53,12 +49,17 @@ namespace TagLib {
unsigned int instrumentCount() const;
unsigned char lengthInPatterns() const;
private:
void setChannels(int channels);
void setInstrumentCount(unsigned int sampleCount);
void setLengthInPatterns(unsigned char lengthInPatterns);
private:
friend class File;
Properties(const Properties&);
Properties &operator=(const Properties&);
class PropertiesPrivate;
PropertiesPrivate *d;
};

View File

@@ -27,7 +27,6 @@
#include "modtag.h"
#include "tstringlist.h"
#include "tpropertymap.h"
#include "tpicturemap.h"
using namespace TagLib;
using namespace Mod;
@@ -90,11 +89,6 @@ unsigned int Mod::Tag::track() const
return 0;
}
TagLib::PictureMap Mod::Tag::pictures() const
{
return PictureMap();
}
String Mod::Tag::trackerName() const
{
return d->trackerName;
@@ -130,10 +124,6 @@ void Mod::Tag::setTrack(unsigned int)
{
}
void Mod::Tag::setPictures(const PictureMap &l)
{
}
void Mod::Tag::setTrackerName(const String &trackerName)
{
d->trackerName = trackerName;

View File

@@ -88,8 +88,6 @@ namespace TagLib {
*/
virtual unsigned int track() const;
PictureMap pictures() const;
/*!
* Returns the name of the tracker used to create/edit the module file.
* Only XM files store this tag to the file as such, for other formats
@@ -153,8 +151,6 @@ namespace TagLib {
*/
virtual void setTrack(unsigned int track);
void setPictures(const PictureMap &l);
/*!
* Sets the tracker name to \a trackerName. If \a trackerName is
* String::null then this value will be cleared.

View File

@@ -23,17 +23,18 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <climits>
#include <tdebug.h>
#include <tstring.h>
#include "mp4atom.h"
using namespace TagLib;
const char *MP4::Atom::containers[11] = {
"moov", "udta", "mdia", "meta", "ilst",
"stbl", "minf", "moof", "traf", "trak",
"stsd"
"moov", "udta", "mdia", "meta", "ilst",
"stbl", "minf", "moof", "traf", "trak",
"stsd"
};
MP4::Atom::Atom(File *file)
@@ -51,7 +52,7 @@ MP4::Atom::Atom(File *file)
return;
}
length = header.toUInt32BE(0);
length = header.toUInt();
if(length == 0) {
// The last atom which extends to the end of the file.
@@ -59,7 +60,17 @@ MP4::Atom::Atom(File *file)
}
else if(length == 1) {
// The atom has a 64-bit length.
length = file->readBlock(8).toInt64BE(0);
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);
}
else {
debug("MP4: 64-bit atoms are not supported");
length = 0;
file->seek(0, File::End);
return;
}
}
if(length < 8) {
@@ -145,7 +156,7 @@ MP4::Atoms::Atoms(File *file)
atoms.setAutoDelete(true);
file->seek(0, File::End);
long long end = file->tell();
long end = file->tell();
file->seek(0);
while(file->tell() + 8 <= end) {
MP4::Atom *atom = new MP4::Atom(file);

View File

@@ -77,13 +77,13 @@ namespace TagLib {
class Atom
{
public:
explicit Atom(File *file);
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);
AtomList findall(const char *name, bool recursive = false);
long long offset;
long long length;
long offset;
long length;
TagLib::ByteVector name;
AtomList children;
private:
@@ -95,7 +95,7 @@ namespace TagLib {
class Atoms
{
public:
explicit Atoms(File *file);
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);

View File

@@ -23,33 +23,22 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "taglib.h"
#include "tdebug.h"
#include "tsmartptr.h"
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "mp4coverart.h"
using namespace TagLib;
namespace
{
struct CoverArtData
{
MP4::CoverArt::Format format;
ByteVector data;
};
}
class MP4::CoverArt::CoverArtPrivate
class MP4::CoverArt::CoverArtPrivate : public RefCounter
{
public:
CoverArtPrivate(Format f, const ByteVector &v) :
data(new CoverArtData())
{
data->format = f;
data->data = v;
}
CoverArtPrivate() :
RefCounter(),
format(MP4::CoverArt::JPEG) {}
SHARED_PTR<CoverArtData> data;
Format format;
ByteVector data;
};
////////////////////////////////////////////////////////////////////////////////
@@ -57,13 +46,16 @@ public:
////////////////////////////////////////////////////////////////////////////////
MP4::CoverArt::CoverArt(Format format, const ByteVector &data) :
d(new CoverArtPrivate(format, data))
d(new CoverArtPrivate())
{
d->format = format;
d->data = data;
}
MP4::CoverArt::CoverArt(const CoverArt &item) :
d(new CoverArtPrivate(*item.d))
d(item.d)
{
d->ref();
}
MP4::CoverArt &
@@ -83,17 +75,19 @@ MP4::CoverArt::swap(CoverArt &item)
MP4::CoverArt::~CoverArt()
{
delete d;
if(d->deref()) {
delete d;
}
}
MP4::CoverArt::Format
MP4::CoverArt::format() const
{
return d->data->format;
return d->format;
}
ByteVector
MP4::CoverArt::data() const
{
return d->data->data;
return d->data;
}

View File

@@ -76,7 +76,9 @@ namespace TagLib {
};
typedef List<CoverArt> CoverArtList;
}
}
#endif

View File

@@ -66,9 +66,9 @@ public:
delete properties;
}
MP4::Tag *tag;
MP4::Atoms *atoms;
MP4::AudioProperties *properties;
MP4::Tag *tag;
MP4::Atoms *atoms;
MP4::Properties *properties;
};
////////////////////////////////////////////////////////////////////////////////
@@ -114,7 +114,22 @@ MP4::File::tag() const
return d->tag;
}
MP4::AudioProperties *
PropertyMap MP4::File::properties() const
{
return d->tag->properties();
}
void MP4::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag->removeUnsupportedProperties(properties);
}
PropertyMap MP4::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
MP4::Properties *
MP4::File::audioProperties() const
{
return d->properties;
@@ -140,7 +155,7 @@ MP4::File::read(bool readProperties)
d->tag = new Tag(this, d->atoms);
if(readProperties) {
d->properties = new AudioProperties(this, d->atoms);
d->properties = new Properties(this, d->atoms);
}
}

View File

@@ -55,19 +55,19 @@ namespace TagLib {
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
* Constructs an MP4 file from \a file. If \a readProperties is true the
* Constructs an MP4 file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@@ -86,10 +86,26 @@ namespace TagLib {
*/
Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the MP4 audio properties for this file.
*/
AudioProperties *audioProperties() const;
Properties *audioProperties() const;
/*!
* Save the file.

View File

@@ -23,58 +23,46 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "taglib.h"
#include "tdebug.h"
#include "tsmartptr.h"
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "mp4item.h"
#include "tutils.h"
using namespace TagLib;
namespace
{
struct ItemData
{
bool valid;
MP4::AtomDataType atomDataType;
MP4::Item::ItemType type;
union {
bool m_bool;
int m_int;
MP4::Item::IntPair m_intPair;
unsigned char m_byte;
unsigned int m_uint;
long long m_longlong;
};
StringList m_stringList;
ByteVectorList m_byteVectorList;
MP4::CoverArtList m_coverArtList;
};
}
class MP4::Item::ItemPrivate
class MP4::Item::ItemPrivate : public RefCounter
{
public:
ItemPrivate() :
data(new ItemData())
{
data->valid = true;
data->atomDataType = MP4::TypeUndefined;
data->type = MP4::Item::TypeUndefined;
}
RefCounter(),
valid(true),
atomDataType(TypeUndefined) {}
SHARED_PTR<ItemData> data;
bool valid;
AtomDataType atomDataType;
union {
bool m_bool;
int m_int;
IntPair m_intPair;
unsigned char m_byte;
unsigned int m_uint;
long long m_longlong;
};
StringList m_stringList;
ByteVectorList m_byteVectorList;
MP4::CoverArtList m_coverArtList;
};
MP4::Item::Item() :
d(new ItemPrivate())
{
d->data->valid = false;
d->valid = false;
}
MP4::Item::Item(const Item &item) :
d(new ItemPrivate(*item.d))
d(item.d)
{
d->ref();
}
MP4::Item &
@@ -94,176 +82,131 @@ MP4::Item::swap(Item &item)
MP4::Item::~Item()
{
delete d;
if(d->deref())
delete d;
}
MP4::Item::Item(bool value) :
d(new ItemPrivate())
{
d->data->m_bool = value;
d->data->type = TypeBool;
d->m_bool = value;
}
MP4::Item::Item(int value) :
d(new ItemPrivate())
{
d->data->m_int = value;
d->data->type = TypeInt;
d->m_int = value;
}
MP4::Item::Item(unsigned char value) :
d(new ItemPrivate())
{
d->data->m_byte = value;
d->data->type = TypeByte;
d->m_byte = value;
}
MP4::Item::Item(unsigned int value) :
d(new ItemPrivate())
{
d->data->m_uint = value;
d->data->type = TypeUInt;
d->m_uint = value;
}
MP4::Item::Item(long long value) :
d(new ItemPrivate())
{
d->data->m_longlong = value;
d->data->type = TypeLongLong;
d->m_longlong = value;
}
MP4::Item::Item(int value1, int value2) :
d(new ItemPrivate())
{
d->data->m_intPair.first = value1;
d->data->m_intPair.second = value2;
d->data->type = TypeIntPair;
d->m_intPair.first = value1;
d->m_intPair.second = value2;
}
MP4::Item::Item(const ByteVectorList &value) :
d(new ItemPrivate())
{
d->data->m_byteVectorList = value;
d->data->type = TypeByteVectorList;
d->m_byteVectorList = value;
}
MP4::Item::Item(const StringList &value) :
d(new ItemPrivate())
{
d->data->m_stringList = value;
d->data->type = TypeStringList;
d->m_stringList = value;
}
MP4::Item::Item(const MP4::CoverArtList &value) :
d(new ItemPrivate())
{
d->data->m_coverArtList = value;
d->data->type = TypeCoverArtList;
d->m_coverArtList = value;
}
void MP4::Item::setAtomDataType(MP4::AtomDataType type)
{
d->data->atomDataType = type;
d->atomDataType = type;
}
MP4::AtomDataType MP4::Item::atomDataType() const
{
return d->data->atomDataType;
return d->atomDataType;
}
bool
MP4::Item::toBool() const
{
return d->data->m_bool;
return d->m_bool;
}
int
MP4::Item::toInt() const
{
return d->data->m_int;
return d->m_int;
}
unsigned char
MP4::Item::toByte() const
{
return d->data->m_byte;
return d->m_byte;
}
unsigned int
MP4::Item::toUInt() const
{
return d->data->m_uint;
return d->m_uint;
}
long long
MP4::Item::toLongLong() const
{
return d->data->m_longlong;
return d->m_longlong;
}
MP4::Item::IntPair
MP4::Item::toIntPair() const
{
return d->data->m_intPair;
return d->m_intPair;
}
StringList
MP4::Item::toStringList() const
{
return d->data->m_stringList;
return d->m_stringList;
}
ByteVectorList
MP4::Item::toByteVectorList() const
{
return d->data->m_byteVectorList;
return d->m_byteVectorList;
}
MP4::CoverArtList
MP4::Item::toCoverArtList() const
{
return d->data->m_coverArtList;
return d->m_coverArtList;
}
bool
MP4::Item::isValid() const
{
return d->data->valid;
}
String
MP4::Item::toString() const
{
StringList desc;
switch (d->data->type) {
case TypeBool:
return d->data->m_bool ? "true" : "false";
case TypeInt:
return Utils::formatString("%d", d->data->m_int);
case TypeIntPair:
return Utils::formatString("%d/%d", d->data->m_intPair.first, d->data->m_intPair.second);
case TypeByte:
return Utils::formatString("%d", d->data->m_byte);
case TypeUInt:
return Utils::formatString("%u", d->data->m_uint);
case TypeLongLong:
return Utils::formatString("%lld", d->data->m_longlong);
case TypeStringList:
return d->data->m_stringList.toString(" / ");
case TypeByteVectorList:
for(size_t i = 0; i < d->data->m_byteVectorList.size(); i++) {
desc.append(Utils::formatString(
"[%d bytes of data]", static_cast<int>(d->data->m_byteVectorList[i].size())));
}
return desc.toString(", ");
case TypeCoverArtList:
for(size_t i = 0; i < d->data->m_coverArtList.size(); i++) {
desc.append(Utils::formatString(
"[%d bytes of data]", static_cast<int>(d->data->m_coverArtList[i].data().size())));
}
return desc.toString(", ");
case TypeUndefined:
return "[unknown]";
}
return String();
return d->valid;
}

View File

@@ -41,19 +41,6 @@ namespace TagLib {
int first, second;
};
enum ItemType {
TypeUndefined = 0,
TypeBool,
TypeInt,
TypeIntPair,
TypeByte,
TypeUInt,
TypeLongLong,
TypeStringList,
TypeByteVectorList,
TypeCoverArtList,
};
Item();
Item(const Item &item);
@@ -92,12 +79,8 @@ namespace TagLib {
ByteVectorList toByteVectorList() const;
CoverArtList toCoverArtList() const;
ItemType type() const;
bool isValid() const;
String toString() const;
private:
class ItemPrivate;
ItemPrivate *d;

View File

@@ -31,7 +31,7 @@
using namespace TagLib;
class MP4::AudioProperties::PropertiesPrivate
class MP4::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -41,7 +41,7 @@ public:
channels(0),
bitsPerSample(0),
encrypted(false),
codec(MP4::AudioProperties::Unknown) {}
codec(MP4::Properties::Unknown) {}
int length;
int bitrate;
@@ -56,98 +56,78 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
MP4::AudioProperties::AudioProperties(File *file, MP4::Atoms *atoms, ReadStyle) :
TagLib::AudioProperties(),
MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
read(file, atoms);
}
MP4::AudioProperties::~AudioProperties()
MP4::Properties::~Properties()
{
delete d;
}
int
MP4::AudioProperties::channels() const
MP4::Properties::channels() const
{
return d->channels;
}
int
MP4::AudioProperties::sampleRate() const
MP4::Properties::sampleRate() const
{
return d->sampleRate;
}
int
MP4::AudioProperties::length() const
MP4::Properties::length() const
{
return lengthInSeconds();
}
int
MP4::AudioProperties::lengthInSeconds() const
MP4::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int
MP4::AudioProperties::lengthInMilliseconds() const
MP4::Properties::lengthInMilliseconds() const
{
return d->length;
}
int
MP4::AudioProperties::bitrate() const
MP4::Properties::bitrate() const
{
return d->bitrate;
}
int
MP4::AudioProperties::bitsPerSample() const
MP4::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
bool
MP4::AudioProperties::isEncrypted() const
MP4::Properties::isEncrypted() const
{
return d->encrypted;
}
MP4::AudioProperties::Codec
MP4::AudioProperties::codec() const
MP4::Properties::Codec
MP4::Properties::codec() const
{
return d->codec;
}
String
MP4::AudioProperties::toString() const
{
String format;
if(d->codec == AAC) {
format = "AAC";
}
else if(d->codec == ALAC) {
format = "ALAC";
}
else {
format = "Unknown";
}
StringList desc;
desc.append("MPEG-4 audio (" + format + ")");
desc.append(String::number(length()) + " seconds");
desc.append(String::number(bitrate()) + " kbps");
return desc.toString(", ");
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void
MP4::AudioProperties::read(File *file, Atoms *atoms)
MP4::Properties::read(File *file, Atoms *atoms)
{
MP4::Atom *moov = atoms->find("moov");
if(!moov) {
@@ -167,7 +147,7 @@ MP4::AudioProperties::read(File *file, Atoms *atoms)
return;
}
file->seek(hdlr->offset);
data = file->readBlock(static_cast<size_t>(hdlr->length));
data = file->readBlock(hdlr->length);
if(data.containsAt("soun", 16)) {
break;
}
@@ -185,7 +165,7 @@ MP4::AudioProperties::read(File *file, Atoms *atoms)
}
file->seek(mdhd->offset);
data = file->readBlock(static_cast<size_t>(mdhd->length));
data = file->readBlock(mdhd->length);
const unsigned int version = data[8];
long long unit;
@@ -195,16 +175,16 @@ MP4::AudioProperties::read(File *file, Atoms *atoms)
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
unit = data.toUInt32BE(28);
length = data.toInt64BE(32);
unit = data.toUInt(28U);
length = data.toLongLong(32U);
}
else {
if(data.size() < 24 + 8) {
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
unit = data.toUInt32BE(20);
length = data.toUInt32BE(24);
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,12 +195,12 @@ MP4::AudioProperties::read(File *file, Atoms *atoms)
}
file->seek(atom->offset);
data = file->readBlock(static_cast<size_t>(atom->length));
data = file->readBlock(atom->length);
if(data.containsAt("mp4a", 20)) {
d->codec = AAC;
d->channels = data.toUInt16BE(40);
d->bitsPerSample = data.toUInt16BE(42);
d->sampleRate = data.toUInt32BE(46);
d->channels = data.toShort(40U);
d->bitsPerSample = data.toShort(42U);
d->sampleRate = data.toUInt(46U);
if(data.containsAt("esds", 56) && data[64] == 0x03) {
unsigned int pos = 65;
if(data.containsAt("\x80\x80\x80", pos)) {
@@ -233,7 +213,7 @@ MP4::AudioProperties::read(File *file, Atoms *atoms)
pos += 3;
}
pos += 10;
d->bitrate = static_cast<int>((data.toUInt32BE(pos) + 500) / 1000.0 + 0.5);
d->bitrate = static_cast<int>((data.toUInt(pos) + 500) / 1000.0 + 0.5);
}
}
}
@@ -242,8 +222,8 @@ MP4::AudioProperties::read(File *file, Atoms *atoms)
d->codec = ALAC;
d->bitsPerSample = data.at(69);
d->channels = data.at(73);
d->bitrate = static_cast<int>(data.toUInt32BE(80) / 1000.0 + 0.5);
d->sampleRate = data.toUInt32BE(84);
d->bitrate = static_cast<int>(data.toUInt(80U) / 1000.0 + 0.5);
d->sampleRate = data.toUInt(84U);
}
}

View File

@@ -37,7 +37,7 @@ namespace TagLib {
class File;
//! An implementation of MP4 audio properties
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
enum Codec {
@@ -46,8 +46,8 @@ namespace TagLib {
ALAC
};
AudioProperties(File *file, Atoms *atoms, ReadStyle style = Average);
virtual ~AudioProperties();
Properties(File *file, Atoms *atoms, ReadStyle style = Average);
virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -57,7 +57,7 @@ namespace TagLib {
*
* \deprecated
*/
virtual int length() const;
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -65,14 +65,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
@@ -104,8 +106,6 @@ namespace TagLib {
*/
Codec codec() const;
String toString() const;
private:
void read(File *file, Atoms *atoms);

View File

@@ -22,10 +22,9 @@
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <cstring>
#include <tdebug.h>
#include <tstring.h>
#include <tpicturemap.h>
#include <tpropertymap.h>
#include "mp4atom.h"
#include "mp4tag.h"
@@ -75,9 +74,20 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
atom->name == "hdvd" || atom->name == "shwm") {
parseBool(atom);
}
else if(atom->name == "tmpo" || atom->name == "rate" || atom->name == "\251mvi" || atom->name == "\251mvc") {
else if(atom->name == "tmpo" || atom->name == "\251mvi" || atom->name == "\251mvc") {
parseInt(atom);
}
else if(atom->name == "rate") {
AtomDataList data = parseData2(atom);
if(!data.isEmpty()) {
AtomData val = data[0];
if (val.type == TypeUTF8) {
addItem(atom->name, StringList(String(val.data, String::UTF8)));
} else {
addItem(atom->name, (int)(val.data.toShort()));
}
}
}
else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" ||
atom->name == "sfID" || atom->name == "atID" || atom->name == "geID" ||
atom->name == "cmID") {
@@ -113,18 +123,18 @@ MP4::AtomDataList
MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm)
{
AtomDataList result;
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 8));
ByteVector data = d->file->readBlock(atom->length - 8);
int i = 0;
size_t pos = 0;
unsigned int pos = 0;
while(pos < data.size()) {
const int length = static_cast<int>(data.toUInt32BE(pos));
const int 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 int flags = static_cast<int>(data.toUInt32BE(pos + 8));
const int flags = static_cast<int>(data.toUInt(pos + 8));
if(freeForm && i < 2) {
if(i == 0 && name != "mean") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
@@ -167,7 +177,7 @@ MP4::Tag::parseInt(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
addItem(atom->name, (int)data[0].toInt16BE(0));
addItem(atom->name, (int)data[0].toShort());
}
}
@@ -176,7 +186,7 @@ MP4::Tag::parseUInt(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
addItem(atom->name, data[0].toUInt32BE(0));
addItem(atom->name, data[0].toUInt());
}
}
@@ -185,7 +195,7 @@ MP4::Tag::parseLongLong(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
addItem(atom->name, data[0].toInt64BE(0));
addItem(atom->name, data[0].toLongLong());
}
}
@@ -203,7 +213,7 @@ MP4::Tag::parseGnre(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
int idx = (int)data[0].toInt16BE(0);
int idx = (int)data[0].toShort();
if(idx > 0) {
addItem("\251gen", StringList(ID3v1::genre(idx - 1)));
}
@@ -215,8 +225,8 @@ MP4::Tag::parseIntPair(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
const int a = data[0].toInt16BE(2);
const int b = data[0].toInt16BE(4);
const int a = data[0].toShort(2U);
const int b = data[0].toShort(4U);
addItem(atom->name, MP4::Item(a, b));
}
}
@@ -289,17 +299,17 @@ void
MP4::Tag::parseCovr(const MP4::Atom *atom)
{
MP4::CoverArtList value;
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 8));
ByteVector data = d->file->readBlock(atom->length - 8);
unsigned int pos = 0;
while(pos < data.size()) {
const int length = static_cast<int>(data.toUInt32BE(pos));
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.toUInt32BE(pos + 8));
const int flags = static_cast<int>(data.toUInt(pos + 8));
if(name != "data") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
break;
@@ -322,7 +332,7 @@ ByteVector
MP4::Tag::padIlst(const ByteVector &data, int length) const
{
if(length == -1) {
length = static_cast<int>(((data.size() + 1023) & ~1023) - data.size());
length = ((data.size() + 1023) & ~1023) - data.size();
}
return renderAtom("free", ByteVector(length, '\1'));
}
@@ -330,7 +340,7 @@ MP4::Tag::padIlst(const ByteVector &data, int length) const
ByteVector
MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) const
{
return ByteVector::fromUInt32BE(data.size() + 8) + name + data;
return ByteVector::fromUInt(data.size() + 8) + name + data;
}
ByteVector
@@ -338,7 +348,7 @@ MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &da
{
ByteVector result;
for(ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) {
result.append(renderAtom("data", ByteVector::fromUInt32BE(flags) + ByteVector(4, '\0') + *it));
result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + *it));
}
return renderAtom(name, result);
}
@@ -355,7 +365,7 @@ ByteVector
MP4::Tag::renderInt(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector::fromUInt16BE(item.toInt()));
data.append(ByteVector::fromShort(item.toInt()));
return renderData(name, TypeInteger, data);
}
@@ -363,7 +373,7 @@ ByteVector
MP4::Tag::renderUInt(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector::fromUInt32BE(item.toUInt()));
data.append(ByteVector::fromUInt(item.toUInt()));
return renderData(name, TypeInteger, data);
}
@@ -371,7 +381,7 @@ ByteVector
MP4::Tag::renderLongLong(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector::fromUInt64BE(item.toLongLong()));
data.append(ByteVector::fromLongLong(item.toLongLong()));
return renderData(name, TypeInteger, data);
}
@@ -388,8 +398,8 @@ MP4::Tag::renderIntPair(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector(2, '\0') +
ByteVector::fromUInt16BE(item.toIntPair().first) +
ByteVector::fromUInt16BE(item.toIntPair().second) +
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second) +
ByteVector(2, '\0'));
return renderData(name, TypeImplicit, data);
}
@@ -399,8 +409,8 @@ MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, const MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector(2, '\0') +
ByteVector::fromUInt16BE(item.toIntPair().first) +
ByteVector::fromUInt16BE(item.toIntPair().second));
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second));
return renderData(name, TypeImplicit, data);
}
@@ -421,7 +431,7 @@ MP4::Tag::renderCovr(const ByteVector &name, const MP4::Item &item) const
ByteVector data;
MP4::CoverArtList value = item.toCoverArtList();
for(MP4::CoverArtList::ConstIterator it = value.begin(); it != value.end(); ++it) {
data.append(renderAtom("data", ByteVector::fromUInt32BE(it->format()) +
data.append(renderAtom("data", ByteVector::fromUInt(it->format()) +
ByteVector(4, '\0') + it->data()));
}
return renderAtom(name, data);
@@ -436,8 +446,8 @@ MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const
return ByteVector();
}
ByteVector data;
data.append(renderAtom("mean", ByteVector::fromUInt32BE(0) + header[1].data(String::UTF8)));
data.append(renderAtom("name", ByteVector::fromUInt32BE(0) + header[2].data(String::UTF8)));
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()) {
@@ -450,13 +460,13 @@ MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const
if(type == TypeUTF8) {
StringList value = item.toStringList();
for(StringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
data.append(renderAtom("data", ByteVector::fromUInt32BE(type) + ByteVector(4, '\0') + it->data(String::UTF8)));
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + it->data(String::UTF8)));
}
}
else {
ByteVectorList value = item.toByteVectorList();
for(ByteVectorList::ConstIterator it = value.begin(); it != value.end(); ++it) {
data.append(renderAtom("data", ByteVector::fromUInt32BE(type) + ByteVector(4, '\0') + *it));
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + *it));
}
}
return renderAtom("----", data);
@@ -481,9 +491,19 @@ MP4::Tag::save()
name == "shwm") {
data.append(renderBool(name.data(String::Latin1), it->second));
}
else if(name == "tmpo" || name == "rate" || name == "\251mvi" || name == "\251mvc") {
else if(name == "tmpo" || name == "\251mvi" || name == "\251mvc") {
data.append(renderInt(name.data(String::Latin1), it->second));
}
else if (name == "rate") {
const MP4::Item& item = it->second;
StringList value = item.toStringList();
if (value.isEmpty()) {
data.append(renderInt(name.data(String::Latin1), item));
}
else {
data.append(renderText(name.data(String::Latin1), item));
}
}
else if(name == "tvsn" || name == "tves" || name == "cnID" ||
name == "sfID" || name == "atID" || name == "geID" ||
name == "cmID") {
@@ -532,25 +552,25 @@ MP4::Tag::updateParents(const AtomList &path, long delta, int ignore)
for(AtomList::ConstIterator it = path.begin(); it != itEnd; ++it) {
d->file->seek((*it)->offset);
long size = d->file->readBlock(4).toUInt32BE(0);
long size = d->file->readBlock(4).toUInt();
// 64-bit
if (size == 1) {
d->file->seek(4, File::Current); // Skip name
long long longSize = d->file->readBlock(8).toInt64BE(0);
long long longSize = d->file->readBlock(8).toLongLong();
// Seek the offset of the 64-bit size
d->file->seek((*it)->offset + 8);
d->file->writeBlock(ByteVector::fromUInt64BE(longSize + delta));
d->file->writeBlock(ByteVector::fromLongLong(longSize + delta));
}
// 32-bit
else {
d->file->seek((*it)->offset);
d->file->writeBlock(ByteVector::fromUInt32BE(size + delta));
d->file->writeBlock(ByteVector::fromUInt(size + delta));
}
}
}
void
MP4::Tag::updateOffsets(long delta, long long offset)
MP4::Tag::updateOffsets(long delta, long offset)
{
MP4::Atom *moov = d->atoms->find("moov");
if(moov) {
@@ -561,16 +581,16 @@ MP4::Tag::updateOffsets(long delta, long long offset)
atom->offset += delta;
}
d->file->seek(atom->offset + 12);
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 12));
unsigned int count = data.toUInt32BE(0);
ByteVector data = d->file->readBlock(atom->length - 12);
unsigned int count = data.toUInt();
d->file->seek(atom->offset + 16);
size_t pos = 4;
unsigned int pos = 4;
while(count--) {
long o = data.toUInt32BE(pos);
long o = static_cast<long>(data.toUInt(pos));
if(o > offset) {
o += delta;
}
d->file->writeBlock(ByteVector::fromUInt32BE(o));
d->file->writeBlock(ByteVector::fromUInt(o));
pos += 4;
}
}
@@ -582,16 +602,16 @@ MP4::Tag::updateOffsets(long delta, long long offset)
atom->offset += delta;
}
d->file->seek(atom->offset + 12);
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 12));
unsigned int count = data.toUInt32BE(0);
ByteVector data = d->file->readBlock(atom->length - 12);
unsigned int count = data.toUInt();
d->file->seek(atom->offset + 16);
size_t pos = 4;
unsigned int pos = 4;
while(count--) {
long long o = data.toInt64BE(pos);
long long o = data.toLongLong(pos);
if(o > offset) {
o += delta;
}
d->file->writeBlock(ByteVector::fromUInt64BE(o));
d->file->writeBlock(ByteVector::fromLongLong(o));
pos += 8;
}
}
@@ -606,15 +626,15 @@ MP4::Tag::updateOffsets(long delta, long long offset)
atom->offset += delta;
}
d->file->seek(atom->offset + 9);
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 9));
const unsigned int flags = data.toUInt24BE(0);
ByteVector data = d->file->readBlock(atom->length - 9);
const unsigned int flags = data.toUInt(0, 3, true);
if(flags & 1) {
long long o = data.toInt64BE(7);
long long o = data.toLongLong(7U);
if(o > offset) {
o += delta;
}
d->file->seek(atom->offset + 16);
d->file->writeBlock(ByteVector::fromUInt64BE(o));
d->file->writeBlock(ByteVector::fromLongLong(o));
}
}
}
@@ -634,11 +654,11 @@ MP4::Tag::saveNew(ByteVector data)
data = renderAtom("udta", data);
}
long long offset = path.back()->offset + 8;
long offset = path.back()->offset + 8;
d->file->insert(data, offset, 0);
updateParents(path, static_cast<long>(data.size()));
updateOffsets(static_cast<long>(data.size()), offset);
updateParents(path, data.size());
updateOffsets(data.size(), offset);
// Insert the newly created atoms into the tree to keep it up-to-date.
@@ -652,8 +672,8 @@ MP4::Tag::saveExisting(ByteVector data, const AtomList &path)
AtomList::ConstIterator it = path.end();
MP4::Atom *ilst = *(--it);
long long offset = ilst->offset;
long long length = ilst->length;
long offset = ilst->offset;
long length = ilst->length;
MP4::Atom *meta = *(--it);
AtomList::ConstIterator index = meta->children.find(ilst);
@@ -678,17 +698,17 @@ MP4::Tag::saveExisting(ByteVector data, const AtomList &path)
}
}
long delta = static_cast<long>(data.size() - length);
long delta = data.size() - length;
if(delta > 0 || (delta < 0 && delta > -8)) {
data.append(padIlst(data));
delta = static_cast<long>(data.size() - length);
delta = data.size() - length;
}
else if(delta < 0) {
data.append(padIlst(data, 0 - delta - 8));
data.append(padIlst(data, -delta - 8));
delta = 0;
}
d->file->insert(data, offset, static_cast<size_t>(length));
d->file->insert(data, offset, length);
if(delta) {
updateParents(path, delta, 1);
@@ -752,42 +772,6 @@ MP4::Tag::track() const
return 0;
}
PictureMap
MP4::Tag::pictures() const
{
if(!d->items.contains("covr"))
return PictureMap();
CoverArtList list = d->items["covr"].toCoverArtList();
if(list.isEmpty())
return PictureMap();
PictureMap map;
for(CoverArtList::ConstIterator it = list.begin(); it != list.end(); ++it) {
CoverArt art = *it;
String mime = "image/";
switch(art.format()) {
case CoverArt::BMP:
mime.append("bmp");
break;
case CoverArt::JPEG:
mime.append("jpeg");
break;
case CoverArt::GIF:
mime.append("gif");
break;
case CoverArt::PNG:
mime.append("png");
break;
case CoverArt::Unknown:
break;
}
Picture picture(art.data(), Picture::Other, mime);
map.insert(picture);
}
return PictureMap(map);
}
void
MP4::Tag::setTitle(const String &value)
{
@@ -821,46 +805,23 @@ MP4::Tag::setGenre(const String &value)
void
MP4::Tag::setYear(unsigned int value)
{
d->items["\251day"] = StringList(String::number(value));
if (value == 0) {
d->items.erase("\251day");
}
else {
d->items["\251day"] = StringList(String::number(value));
}
}
void
MP4::Tag::setTrack(unsigned int value)
{
d->items["trkn"] = MP4::Item(value, 0);
}
void
MP4::Tag::setPictures(const PictureMap &l)
{
CoverArtList list;
for(PictureMap::ConstIterator pictureMapIt = l.begin();
pictureMapIt != l.end();
++pictureMapIt) {
PictureList pictures = pictureMapIt->second;
for(PictureList::ConstIterator pictureListIt = pictures.begin();
pictureListIt != pictures.end();
++pictureListIt) {
Picture picture = *pictureListIt;
CoverArt::Format format;
String mime = picture.mime();
if(String("image/") == mime)
format = CoverArt::Unknown;
else if(String("image/bmp") == mime)
format = CoverArt::BMP;
else if(String("image/png") == mime)
format = CoverArt::PNG;
else if(String("image/gif") == mime)
format = CoverArt::GIF;
else if(String("image/jpeg") == mime)
format = CoverArt::JPEG;
else
format = CoverArt::Unknown;
CoverArt art(format, picture.data());
list.append(art);
}
if (value == 0) {
d->items.erase("trkn");
}
else {
d->items["trkn"] = MP4::Item(value, 0);
}
d->items["covr"] = list;
}
bool MP4::Tag::isEmpty() const
@@ -898,16 +859,6 @@ bool MP4::Tag::contains(const String &key) const
return d->items.contains(key);
}
String
MP4::Tag::toString() const
{
StringList desc;
for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
desc.append(i->first + "=" + i->second.toString());
}
return desc.toString("\n");
}
namespace
{
const char *keyTranslation[][2] = {

View File

@@ -42,7 +42,7 @@ namespace TagLib {
/*!
* \deprecated
*/
typedef TagLib::Map<String, Item> ItemListMap;
TAGLIB_DEPRECATED typedef TagLib::Map<String, Item> ItemListMap;
typedef TagLib::Map<String, Item> ItemMap;
class TAGLIB_EXPORT Tag: public TagLib::Tag
@@ -60,7 +60,6 @@ namespace TagLib {
virtual String genre() const;
virtual unsigned int year() const;
virtual unsigned int track() const;
virtual PictureMap pictures() const;
virtual void setTitle(const String &value);
virtual void setArtist(const String &value);
@@ -69,14 +68,13 @@ namespace TagLib {
virtual void setGenre(const String &value);
virtual void setYear(unsigned int value);
virtual void setTrack(unsigned int value);
virtual void setPictures(const PictureMap &l);
virtual bool isEmpty() const;
/*!
* \deprecated Use the item() and setItem() API instead
*/
ItemMap &itemListMap();
TAGLIB_DEPRECATED ItemMap &itemListMap();
/*!
* Returns a string-keyed map of the MP4::Items for this tag.
@@ -104,8 +102,6 @@ namespace TagLib {
*/
bool contains(const String &key) const;
String toString() const;
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList& properties);
PropertyMap setProperties(const PropertyMap &properties);
@@ -143,7 +139,7 @@ namespace TagLib {
ByteVector renderCovr(const ByteVector &name, const Item &item) const;
void updateParents(const AtomList &path, long delta, int ignore = 0);
void updateOffsets(long delta, long long offset);
void updateOffsets(long delta, long offset);
void saveNew(ByteVector data);
void saveExisting(ByteVector data, const AtomList &path);

View File

@@ -29,7 +29,6 @@
#include <tdebug.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <tsmartptr.h>
#include "mpcfile.h"
#include "id3v1tag.h"
@@ -51,21 +50,29 @@ public:
APELocation(-1),
APESize(0),
ID3v1Location(-1),
ID3v2Header(0),
ID3v2Location(-1),
ID3v2Size(0) {}
ID3v2Size(0),
properties(0) {}
long long APELocation;
long long APESize;
~FilePrivate()
{
delete ID3v2Header;
delete properties;
}
long long ID3v1Location;
long APELocation;
long APESize;
SCOPED_PTR<ID3v2::Header> ID3v2Header;
long long ID3v2Location;
long long ID3v2Size;
long ID3v1Location;
DoubleTagUnion tag;
ID3v2::Header *ID3v2Header;
long ID3v2Location;
long ID3v2Size;
SCOPED_PTR<AudioProperties> properties;
TagUnion tag;
Properties *properties;
};
////////////////////////////////////////////////////////////////////////////////
@@ -85,7 +92,7 @@ bool MPC::File::isSupported(IOStream *stream)
// public members
////////////////////////////////////////////////////////////////////////////////
MPC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
MPC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
@@ -93,7 +100,7 @@ MPC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle)
read(readProperties);
}
MPC::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) :
MPC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
@@ -111,6 +118,16 @@ TagLib::Tag *MPC::File::tag() const
return &d->tag;
}
PropertyMap MPC::File::properties() const
{
return d->tag.properties();
}
void MPC::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap MPC::File::setProperties(const PropertyMap &properties)
{
if(ID3v1Tag())
@@ -119,9 +136,9 @@ PropertyMap MPC::File::setProperties(const PropertyMap &properties)
return APETag(true)->setProperties(properties);
}
MPC::AudioProperties *MPC::File::audioProperties() const
MPC::Properties *MPC::File::audioProperties() const
{
return d->properties.get();
return d->properties;
}
bool MPC::File::save()
@@ -134,7 +151,7 @@ bool MPC::File::save()
// Possibly strip ID3v2 tag
if(!d->ID3v2Header && d->ID3v2Location >= 0) {
removeBlock(d->ID3v2Location, static_cast<size_t>(d->ID3v2Size));
removeBlock(d->ID3v2Location, d->ID3v2Size);
if(d->APELocation >= 0)
d->APELocation -= d->ID3v2Size;
@@ -186,7 +203,7 @@ bool MPC::File::save()
}
const ByteVector data = APETag()->render();
insert(data, d->APELocation, static_cast<size_t>(d->APESize));
insert(data, d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
@@ -198,7 +215,7 @@ bool MPC::File::save()
// APE tag is empty. Remove the old one.
if(d->APELocation >= 0) {
removeBlock(d->APELocation, static_cast<size_t>(d->APESize));
removeBlock(d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->APESize;
@@ -232,8 +249,10 @@ void MPC::File::strip(int tags)
if(!ID3v1Tag())
APETag(true);
if(tags & ID3v2)
d->ID3v2Header.reset();
if(tags & ID3v2) {
delete d->ID3v2Header;
d->ID3v2Header = 0;
}
}
void MPC::File::remove(int tags)
@@ -263,7 +282,7 @@ void MPC::File::read(bool readProperties)
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location);
d->ID3v2Header.reset(new ID3v2::Header(readBlock(ID3v2::Header::size())));
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
d->ID3v2Size = d->ID3v2Header->completeTagSize();
}
@@ -291,7 +310,7 @@ void MPC::File::read(bool readProperties)
if(readProperties) {
long long streamLength;
long streamLength;
if(d->APELocation >= 0)
streamLength = d->APELocation;
@@ -308,6 +327,6 @@ void MPC::File::read(bool readProperties)
seek(0);
}
d->properties.reset(new AudioProperties(this, streamLength));
d->properties = new Properties(this, streamLength);
}
}

View File

@@ -90,7 +90,7 @@ namespace TagLib {
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an MPC file from \a stream. If \a readProperties is true the
@@ -102,7 +102,7 @@ namespace TagLib {
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@@ -115,18 +115,27 @@ namespace TagLib {
*/
virtual TagLib::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains both an APE and an ID3v1 tag, only the APE
* tag will be converted to the PropertyMap.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* Affects only the APEv2 tag which will be created if necessary.
* If an ID3v1 tag exists, it will be updated as well.
*/
virtual PropertyMap setProperties(const PropertyMap &);
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the MPC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const;
virtual Properties *audioProperties() const;
/*!
* Saves the file.
@@ -189,7 +198,7 @@ namespace TagLib {
* \deprecated
* \see strip
*/
void remove(int tags = AllTags);
TAGLIB_DEPRECATED void remove(int tags = AllTags);
/*!
* Returns whether or not the file on disk actually has an ID3v1 tag.

View File

@@ -33,12 +33,7 @@
using namespace TagLib;
namespace
{
const unsigned int HeaderSize = 56;
}
class MPC::AudioProperties::PropertiesPrivate
class MPC::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -72,8 +67,15 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
MPC::AudioProperties::AudioProperties(File *file, long long streamLength, ReadStyle) :
TagLib::AudioProperties(),
MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
readSV7(data, streamLength);
}
MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
ByteVector magic = file->readBlock(4);
@@ -83,76 +85,76 @@ MPC::AudioProperties::AudioProperties(File *file, long long streamLength, ReadSt
}
else {
// Musepack version 7 or older, fixed size header
readSV7(magic + file->readBlock(HeaderSize - 4), streamLength);
readSV7(magic + file->readBlock(MPC::HeaderSize - 4), streamLength);
}
}
MPC::AudioProperties::~AudioProperties()
MPC::Properties::~Properties()
{
delete d;
}
int MPC::AudioProperties::length() const
int MPC::Properties::length() const
{
return lengthInSeconds();
}
int MPC::AudioProperties::lengthInSeconds() const
int MPC::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int MPC::AudioProperties::lengthInMilliseconds() const
int MPC::Properties::lengthInMilliseconds() const
{
return d->length;
}
int MPC::AudioProperties::bitrate() const
int MPC::Properties::bitrate() const
{
return d->bitrate;
}
int MPC::AudioProperties::sampleRate() const
int MPC::Properties::sampleRate() const
{
return d->sampleRate;
}
int MPC::AudioProperties::channels() const
int MPC::Properties::channels() const
{
return d->channels;
}
int MPC::AudioProperties::mpcVersion() const
int MPC::Properties::mpcVersion() const
{
return d->version;
}
unsigned int MPC::AudioProperties::totalFrames() const
unsigned int MPC::Properties::totalFrames() const
{
return d->totalFrames;
}
unsigned int MPC::AudioProperties::sampleFrames() const
unsigned int MPC::Properties::sampleFrames() const
{
return d->sampleFrames;
}
int MPC::AudioProperties::trackGain() const
int MPC::Properties::trackGain() const
{
return d->trackGain;
}
int MPC::AudioProperties::trackPeak() const
int MPC::Properties::trackPeak() const
{
return d->trackPeak;
}
int MPC::AudioProperties::albumGain() const
int MPC::Properties::albumGain() const
{
return d->albumGain;
}
int MPC::AudioProperties::albumPeak() const
int MPC::Properties::albumPeak() const
{
return d->albumPeak;
}
@@ -163,7 +165,7 @@ int MPC::AudioProperties::albumPeak() const
namespace
{
unsigned long readSize(File *file, size_t &sizeLength, bool &eof)
unsigned long readSize(File *file, unsigned int &sizeLength, bool &eof)
{
sizeLength = 0;
eof = false;
@@ -185,7 +187,7 @@ namespace
return size;
}
unsigned long readSize(const ByteVector &data, size_t &pos)
unsigned long readSize(const ByteVector &data, unsigned int &pos)
{
unsigned char tmp;
unsigned long size = 0;
@@ -202,26 +204,26 @@ namespace
const unsigned short sftable [8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 };
}
void MPC::AudioProperties::readSV8(File *file, long long streamLength)
void MPC::Properties::readSV8(File *file, long streamLength)
{
bool readSH = false, readRG = false;
while(!readSH && !readRG) {
const ByteVector packetType = file->readBlock(2);
size_t packetSizeLength;
unsigned int packetSizeLength;
bool eof;
const size_t packetSize = readSize(file, packetSizeLength, eof);
const unsigned long packetSize = readSize(file, packetSizeLength, eof);
if(eof) {
debug("MPC::AudioProperties::readSV8() - Reached to EOF.");
debug("MPC::Properties::readSV8() - Reached to EOF.");
break;
}
const size_t dataSize = packetSize - 2 - packetSizeLength;
const unsigned long dataSize = packetSize - 2 - packetSizeLength;
const ByteVector data = file->readBlock(dataSize);
if(data.size() != dataSize) {
debug("MPC::AudioProperties::readSV8() - dataSize doesn't match the actual data size.");
debug("MPC::Properties::readSV8() - dataSize doesn't match the actual data size.");
break;
}
@@ -230,29 +232,28 @@ void MPC::AudioProperties::readSV8(File *file, long long streamLength)
// http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket
if(dataSize <= 5) {
debug("MPC::AudioProperties::readSV8() - \"SH\" packet is too short to parse.");
debug("MPC::Properties::readSV8() - \"SH\" packet is too short to parse.");
break;
}
readSH = true;
size_t pos = 4;
unsigned int pos = 4;
d->version = data[pos];
pos += 1;
d->sampleFrames = readSize(data, pos);
if(pos > dataSize - 3) {
debug("MPC::AudioProperties::readSV8() - \"SH\" packet is corrupt.");
debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt.");
break;
}
const unsigned long begSilence = readSize(data, pos);
if(pos > dataSize - 2) {
debug("MPC::AudioProperties::readSV8() - \"SH\" packet is corrupt.");
debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt.");
break;
}
const unsigned short flags = data.toUInt16BE(pos);
const unsigned short flags = data.toUShort(pos, true);
pos += 2;
d->sampleRate = sftable[(flags >> 13) & 0x07];
@@ -270,7 +271,7 @@ void MPC::AudioProperties::readSV8(File *file, long long streamLength)
// http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket
if(dataSize <= 9) {
debug("MPC::AudioProperties::readSV8() - \"RG\" packet is too short to parse.");
debug("MPC::Properties::readSV8() - \"RG\" packet is too short to parse.");
break;
}
@@ -278,10 +279,10 @@ void MPC::AudioProperties::readSV8(File *file, long long streamLength)
const int replayGainVersion = data[0];
if(replayGainVersion == 1) {
d->trackGain = data.toUInt16BE(1);
d->trackPeak = data.toUInt16BE(3);
d->albumGain = data.toUInt16BE(5);
d->albumPeak = data.toUInt16BE(7);
d->trackGain = data.toShort(1, true);
d->trackPeak = data.toShort(3, true);
d->albumGain = data.toShort(5, true);
d->albumPeak = data.toShort(7, true);
}
}
@@ -295,25 +296,25 @@ void MPC::AudioProperties::readSV8(File *file, long long streamLength)
}
}
void MPC::AudioProperties::readSV7(const ByteVector &data, long long streamLength)
void MPC::Properties::readSV7(const ByteVector &data, long streamLength)
{
if(data.startsWith("MP+")) {
d->version = data[3] & 15;
if(d->version < 7)
return;
d->totalFrames = data.toUInt32LE(4);
d->totalFrames = data.toUInt(4, false);
const unsigned int flags = data.toUInt32LE(8);
const unsigned int flags = data.toUInt(8, false);
d->sampleRate = sftable[(flags >> 16) & 0x03];
d->channels = 2;
const unsigned int gapless = data.toUInt32LE(5);
const unsigned int gapless = data.toUInt(5, false);
d->trackGain = data.toUInt16LE(14);
d->trackPeak = data.toUInt16LE(12);
d->albumGain = data.toUInt16LE(18);
d->albumPeak = data.toUInt16LE(16);
d->trackGain = data.toShort(14, false);
d->trackPeak = data.toShort(12, false);
d->albumGain = data.toShort(18, false);
d->albumPeak = data.toShort(16, false);
// convert gain info
if(d->trackGain != 0) {
@@ -343,7 +344,7 @@ void MPC::AudioProperties::readSV7(const ByteVector &data, long long streamLengt
d->sampleFrames = d->totalFrames * 1152 - 576;
}
else {
const unsigned int headerData = data.toUInt32LE(0);
const unsigned int headerData = data.toUInt(0, false);
d->bitrate = (headerData >> 23) & 0x01ff;
d->version = (headerData >> 11) & 0x03ff;
@@ -351,9 +352,9 @@ void MPC::AudioProperties::readSV7(const ByteVector &data, long long streamLengt
d->channels = 2;
if(d->version >= 5)
d->totalFrames = data.toUInt32LE(4);
d->totalFrames = data.toUInt(4, false);
else
d->totalFrames = data.toUInt16LE(6);
d->totalFrames = data.toUShort(6, false);
d->sampleFrames = d->totalFrames * 1152 - 576;
}

View File

@@ -35,6 +35,8 @@ namespace TagLib {
class File;
static const unsigned int HeaderSize = 8 * 7;
//! An implementation of audio property reading for MPC
/*!
@@ -42,19 +44,27 @@ namespace TagLib {
* API.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Creates an instance of MPC::AudioProperties with the data read directly
* Create an instance of MPC::Properties with the data read from the
* ByteVector \a data.
*
* This constructor is deprecated. It only works for MPC version up to 7.
*/
Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
/*!
* Create an instance of MPC::Properties with the data read directly
* from a MPC::File.
*/
AudioProperties(File *file, long long streamLength, ReadStyle style = Average);
Properties(File *file, long streamLength, ReadStyle style = Average);
/*!
* Destroys this MPC::Properties instance.
*/
virtual ~AudioProperties();
virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -64,7 +74,7 @@ namespace TagLib {
*
* \deprecated
*/
virtual int length() const;
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -72,14 +82,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
@@ -131,8 +143,11 @@ namespace TagLib {
int albumPeak() const;
private:
void readSV7(const ByteVector &data, long long streamLength);
void readSV8(File *file, long long streamLength);
Properties(const Properties &);
Properties &operator=(const Properties &);
void readSV7(const ByteVector &data, long streamLength);
void readSV8(File *file, long streamLength);
class PropertiesPrivate;
PropertiesPrivate *d;

View File

@@ -25,7 +25,6 @@
#include <tdebug.h>
#include <tfile.h>
#include <tpicturemap.h>
#include "id3v1tag.h"
#include "id3v1genres.h"
@@ -35,28 +34,8 @@ using namespace ID3v1;
namespace
{
class DefaultStringHandler : public TagLib::StringHandler
{
public:
DefaultStringHandler() :
TagLib::StringHandler() {}
virtual String parse(const ByteVector &data) const
{
return String(data, String::Latin1).stripWhiteSpace();
}
virtual ByteVector render(const String &s) const
{
if(s.isLatin1())
return s.data(String::Latin1);
else
return ByteVector();
}
};
const DefaultStringHandler defaultStringHandler;
const TagLib::StringHandler *stringHandler = &defaultStringHandler;
const ID3v1::StringHandler defaultStringHandler;
const ID3v1::StringHandler *stringHandler = &defaultStringHandler;
}
class ID3v1::Tag::TagPrivate
@@ -69,7 +48,7 @@ public:
genre(255) {}
File *file;
long long tagOffset;
long tagOffset;
String title;
String artist;
@@ -80,6 +59,27 @@ public:
unsigned char genre;
};
////////////////////////////////////////////////////////////////////////////////
// StringHandler implementation
////////////////////////////////////////////////////////////////////////////////
StringHandler::StringHandler()
{
}
String ID3v1::StringHandler::parse(const ByteVector &data) const
{
return String(data, String::Latin1).stripWhiteSpace();
}
ByteVector ID3v1::StringHandler::render(const String &s) const
{
if(s.isLatin1())
return s.data(String::Latin1);
else
return ByteVector();
}
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
@@ -90,7 +90,7 @@ ID3v1::Tag::Tag() :
{
}
ID3v1::Tag::Tag(File *file, long long tagOffset) :
ID3v1::Tag::Tag(File *file, long tagOffset) :
TagLib::Tag(),
d(new TagPrivate())
{
@@ -162,11 +162,6 @@ unsigned int ID3v1::Tag::track() const
return d->track;
}
TagLib::PictureMap ID3v1::Tag::pictures() const
{
return PictureMap();
}
void ID3v1::Tag::setTitle(const String &s)
{
d->title = s;
@@ -202,10 +197,6 @@ void ID3v1::Tag::setTrack(unsigned int i)
d->track = i < 256 ? i : 0;
}
void ID3v1::Tag::setPictures(const PictureMap &l)
{
}
unsigned int ID3v1::Tag::genreNumber() const
{
return d->genre;
@@ -216,7 +207,7 @@ void ID3v1::Tag::setGenreNumber(unsigned int i)
d->genre = i < 256 ? i : 255;
}
void ID3v1::Tag::setStringHandler(const TagLib::StringHandler *handler)
void ID3v1::Tag::setStringHandler(const StringHandler *handler)
{
if(handler)
stringHandler = handler;

View File

@@ -28,7 +28,6 @@
#include "tag.h"
#include "tbytevector.h"
#include "tstringhandler.h"
#include "taglib_export.h"
namespace TagLib {
@@ -39,6 +38,50 @@ namespace TagLib {
namespace ID3v1 {
//! A abstraction for the string to data encoding in ID3v1 tags.
/*!
* ID3v1 should in theory always contain ISO-8859-1 (Latin1) data. In
* practice it does not. TagLib by default only supports ISO-8859-1 data
* in ID3v1 tags.
*
* However by subclassing this class and reimplementing parse() and render()
* and setting your reimplementation as the default with
* ID3v1::Tag::setStringHandler() you can define how you would like these
* transformations to be done.
*
* \warning It is advisable <b>not</b> to write non-ISO-8859-1 data to ID3v1
* tags. Please consider disabling the writing of ID3v1 tags in the case
* that the data is not ISO-8859-1.
*
* \see ID3v1::Tag::setStringHandler()
*/
class TAGLIB_EXPORT StringHandler
{
TAGLIB_IGNORE_MISSING_DESTRUCTOR
public:
// BIC: Add virtual destructor.
StringHandler();
/*!
* Decode a string from \a data. The default implementation assumes that
* \a data is an ISO-8859-1 (Latin1) character array.
*/
virtual String parse(const ByteVector &data) const;
/*!
* Encode a ByteVector with the data from \a s. The default implementation
* assumes that \a s is an ISO-8859-1 (Latin1) string. If the string is
* does not conform to ISO-8859-1, no value is written.
*
* \warning It is recommended that you <b>not</b> override this method, but
* instead do not write an ID3v1 tag in the case that the data is not
* ISO-8859-1.
*/
virtual ByteVector render(const String &s) const;
};
//! The main class in the ID3v1 implementation
/*!
@@ -71,7 +114,7 @@ namespace TagLib {
* Create an ID3v1 tag and parse the data in \a file starting at
* \a tagOffset.
*/
Tag(File *file, long long tagOffset);
Tag(File *file, long tagOffset);
/*!
* Destroys this Tag instance.
@@ -99,7 +142,6 @@ namespace TagLib {
virtual String genre() const;
virtual unsigned int year() const;
virtual unsigned int track() const;
virtual PictureMap pictures() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
@@ -108,7 +150,6 @@ namespace TagLib {
virtual void setGenre(const String &s);
virtual void setYear(unsigned int i);
virtual void setTrack(unsigned int i);
virtual void setPictures(const PictureMap &l);
/*!
* Returns the genre in number.
@@ -133,15 +174,16 @@ namespace TagLib {
*
* \note The caller is responsible for deleting the previous handler
* as needed after it is released.
*
* \see StringHandler
*/
static void setStringHandler(const TagLib::StringHandler *handler);
static void setStringHandler(const StringHandler *handler);
protected:
/*!
* Reads from the file specified in the constructor.
*/
void read();
/*!
* Pareses the body of the tag in \a data.
*/

View File

@@ -135,17 +135,17 @@ void AttachedPictureFrame::parseFields(const ByteVector &data)
d->textEncoding = String::Type(data[0]);
size_t pos = 1;
int pos = 1;
d->mimeType = readStringField(data, String::Latin1, pos);
d->mimeType = readStringField(data, String::Latin1, &pos);
/* Now we need at least two more bytes available */
if(pos + 1 >= data.size()) {
if(static_cast<unsigned int>(pos) + 1 >= data.size()) {
debug("Truncated picture frame.");
return;
}
d->type = (TagLib::ID3v2::AttachedPictureFrame::Type)data[pos++];
d->description = readStringField(data, d->textEncoding, pos);
d->description = readStringField(data, d->textEncoding, &pos);
d->data = data.mid(pos);
}
@@ -191,7 +191,7 @@ void AttachedPictureFrameV22::parseFields(const ByteVector &data)
d->textEncoding = String::Type(data[0]);
size_t pos = 1;
int pos = 1;
String fixedString = String(data.mid(pos, 3), String::Latin1);
pos += 3;
@@ -206,7 +206,7 @@ void AttachedPictureFrameV22::parseFields(const ByteVector &data)
}
d->type = (TagLib::ID3v2::AttachedPictureFrame::Type)data[pos++];
d->description = readStringField(data, d->textEncoding, pos);
d->description = readStringField(data, d->textEncoding, &pos);
d->data = data.mid(pos);
}

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